Código de Entity Framework Primero, usar Guid como identidad con otra columna de identidad

aka ¿Cómo podemos crear múltiples columnas de identidad en Code First?

Debido al rendimiento del clúster, una recomendación común es usar una columna entera autoincrementada en lugar de un GUID creado con newid() .

Para declarar una columna como autoincrement, debe especificarla con la Anotación [DatabaseGenerated(DatabaseGeneratedOption.Identity)] .

Pero, solo puedes tener una identidad en una tabla.

Entonces comenzando con un modelo base como:

 public abstract class ModelBase { // the primary key public virtual Guid Id { get; set; } // a unique autoincrementing key public virtual int ClusterId { get; set; } } 

¿Cómo lo configuramos para que:

  1. Guid es generado automáticamente por la base de datos, no el código
  2. ClusterId está autoincrementado
  3. El código de Entity Framework First no arroja todo tipo de errores como:
    • Las modificaciones a tablas en las que una columna de clave principal tiene la propiedad ‘StoreGeneratedPattern’ establecida en ‘Computada’ no son compatibles. Use el patrón ‘Identidad’ en su lugar.

Para su información , si desea generarlo automáticamente en el código, puede omitir la anotación en el campo Id y hacer algo como:

 public abstract class AbstractContext : DbContext { ///  /// Custom processing when saving entities in changetracker ///  ///  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().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().Single(); if (info.DatabaseGeneratedOption != DatabaseGeneratedOption.Identity) { entry.Entity.Id = Guid.NewGuid(); // now we make it } } return base.SaveChanges(); } } 

Esto terminó funcionando para mí, Entity Framework 5.

  1. Desactivar migraciones automáticas
  2. Migra para crear la tabla inicial, sin lujos
  3. Declarar el ClusterId como identidad (anotación)

     [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public override int ClusterId { get; set; } 
  4. Emigrar

  5. Declare el Id propiedad pk como identidad después de que se haya actualizado el otro

     [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public override Guid Id { get; set; } 
    • bonificación : EF parece suponer que Id es la clave principal, por lo que no necesita [Key, Required]
  6. Cree el código de migración como add-migration TrickEfIntoAutogeneratingMultipleColumns

  7. En el método Up() , en la instrucción AlterColumn , AlterColumn a la base de datos que defaultSqlValue el GUID declarando defaultSqlValue
    • AlterColumn(theTable, "Id", c => c.Guid(nullable: false, identity: true, defaultValueSql: "newid()"));
  8. Emigrar

Esto parece “engañar” a EF, en el sentido de que asume que ambas columnas son identidades y reactjs en consecuencia. Durante la migración, intenta hacer que otra columna sea una identidad, pero al parecer no le importa cuando eso falla silenciosamente: usted termina con una marcada como Identidad y la otra con un valor predeterminado.

Durante el funcionamiento normal del código, cuando EF pasa por los pasos SaveChanges / ChangeTracking, porque ve la propiedad Id como una Identidad, hace todo su “asignar clave temporal” , de modo que no está tratando de usar el valor 0000000 predeterminado … y en su lugar permite que la base de datos la genere utilizando la función de valor por defecto que especificó.

(Hubiera pensado anotar este campo como Computed hubiera logrado lo mismo, pero … los errores que mencioné en la pregunta … boo …)

Y, como el campo ClusterId también es una Identidad en el código, y realmente es una Identidad en la base de datos, también se autoincrementa.