a.k.a How can we create multiple identity columns in Code First?
Because of clustering performance, a common recommendation is to use an autoincremented integer column instead of a GUID created with newid()
.
In order to declare a column as autoincrement, you have to specify it with the Annotation [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
.
But, you can only have one identity in a table.
So starting with a base model like:
public abstract class ModelBase {
// the primary key
public virtual Guid Id { get; set; }
// a unique autoincrementing key
public virtual int ClusterId { get; set; }
}
how do we set it up so that:
- Guid is automatically generated by the database, not code
ClusterId
is autoincremented- Entity Framework Code First doesn't throw all sorts of errors like:
- Modifications to tables where a primary key column has property 'StoreGeneratedPattern' set to 'Computed' are not supported. Use 'Identity' pattern instead.
FYI, if you do want to automatically generate it in code, you could skip the annotation on the Id field and do something like:
public abstract class AbstractContext : DbContext {
/// <summary>
/// Custom processing when saving entities in changetracker
/// </summary>
/// <returns></returns>
public override int SaveChanges()
{
// recommended to explicitly set New Guid for appropriate entities -- http://msdn.microsoft.com/en-us/library/dd283139.aspx
foreach (var entry in ChangeTracker.Entries<ModelBase>().Where(e => e.State == EntityState.Added) ) {
// only generate if property isn't identity...
Type t = entry.Entity.GetType();
var info = t.GetProperty("Id").GetCustomAttributes(
typeof(DatabaseGeneratedAttribute), true).Cast<DatabaseGeneratedAttribute>().Single();
if (info.DatabaseGeneratedOption != DatabaseGeneratedOption.Identity) {
entry.Entity.Id = Guid.NewGuid(); // now we make it
}
}
return base.SaveChanges();
}
}