Código de Entity Framework Primero Usando Guid como Identidad con otra Columna de Identidad


a. k. a ¿Cómo podemos crear varias columnas de identidad en el Código Primero?

Debido al rendimiento de la agrupación en clústeres, una recomendación común es usar una columna de enteros autoincrementados en lugar de un GUID creado con newid().

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

Pero, solo puede tener una identidad en una tabla.

Así que 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. El Guid es generado automáticamente por la base de datos, no por el código
  2. {[4] } se autoaumenta
  3. El código de Entity Framework Primero no arroja todo tipo de errores como:
    • No se admiten modificaciones en tablas donde una columna de clave primaria tiene la propiedad 'StoreGeneratedPattern' establecida en 'Computed'. Utilice el patrón 'Identidad' en su lugar.

FYI, si desea generar automáticamente en código, puede omitir la anotación en el campo Id y hacer algo como:

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();
  }

}
Author: drzaus, 2012-08-18

1 answers

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

  1. Desactivar las migraciones automáticas
  2. Migrar para crear la tabla inicial, sin lujos
  3. Declare el ClusterId como Identidad (anotación)

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

  5. Declare la propiedad pk Id como Identidad después de que la otra se haya actualizado

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

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

Esto parece "engañar" a EF, en el sentido de que asume que ambas columnas son identidades y reacciona en consecuencia. Durante la migración, intenta hacer de otra columna una identidad, pero aparentemente no le importa cuando eso silenciosamente falla end termina con uno marcado como Identidad y el otro con un valor predeterminado.

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

(Yo hubiera pensado anotar este campo como Computed habría logrado lo mismo, pero...los errores que mencioné en la pregunta...boo...)

Y, debido a que 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.

 36
Author: drzaus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-01-31 16:50:14