2011-05-23 11 views
8

¿Hay alguna forma de implementar la estrategia de identidad Guid COMB para objetos en el nuevo Entity Framework 4.1 utilizando el diseño CodeFirst? Pensé que configurar el StoreGeneratedPattern funcionaría, pero todavía me da GUID normales.Estrategia GUID COMB en EF

+0

Supongo que ya ha comprobado el valor predeterminado del lado del servidor de base de datos? –

+0

Sí, tengo. No lo encontré para resolver mi problema. – Ciel

Respuesta

8

Supongo que está utilizando el servidor SQL como su base de datos. Este es un buen ejemplo de inconsistencia entre las diferentes herramientas de MS. El equipo del servidor SQL no recomienda usar newid() como valor predeterminado para las columnas UNIQUEIDENTIFIER y el equipo ADO.NET lo usa si especifica la propiedad Guid como autogenerada en la base de datos. ¡Deberían usar newsequentialid() en su lugar!

Si desea Guias secuenciales generados por la base de datos, debe modificar la tabla generada y es realmente compleja porque debe encontrar la restricción predeterminada autogenerada, soltarla y crear una nueva restricción. Todo esto se puede hacer en el inicializador de base de datos personalizado. Aquí tienes mi código de ejemplo:

class Program 
{ 

    static void Main(string[] args) 
    { 
     Database.SetInitializer(new CustomInitializer()); 
     using (var context = new Context()) 
     { 
      context.TestEntities.Add(new TestEntity() { Name = "A" }); 
      context.TestEntities.Add(new TestEntity() { Name = "B" }); 
      context.SaveChanges(); 
     } 
    } 
} 

public class CustomInitializer : DropCreateDatabaseAlways<Context> 
{ 
    protected override void Seed(Context context) 
    { 
     base.Seed(context); 

     context.Database.ExecuteSqlCommand(@" 
      DECLARE @Name VARCHAR(100) 

      SELECT @Name = O.Name FROM sys.objects AS O 
      INNER JOIN sys.tables AS T ON O.parent_object_id = T.object_id 
      WHERE O.type_desc LIKE 'DEFAULT_CONSTRAINT' 
       AND O.Name LIKE 'DF__TestEntities__Id__%' 
       AND T.Name = 'TestEntities' 

      DECLARE @Sql NVARCHAR(2000) = 'ALTER TABLE TestEntities DROP Constraint ' + @Name 

      EXEC sp_executesql @Sql 

      ALTER TABLE TestEntities 
      ADD CONSTRAINT IdDef DEFAULT NEWSEQUENTIALID() FOR Id"); 
    } 
} 

public class TestEntity 
{ 
    public Guid Id { get; set; } 
    public string Name { get; set; } 
} 

public class Context : DbContext 
{ 
    public DbSet<TestEntity> TestEntities { get; set; } 

    protected override void OnModelCreating(DbModelBuilder modelBuilder) 
    { 
     base.OnModelCreating(modelBuilder); 

     modelBuilder.Entity<TestEntity>() 
      .Property(e => e.Id) 
      .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity); 
    } 
} 
+0

Esto parece innecesario. Realmente había esperado que el 4.1 EF fuera más robusto que esto. Muchas gracias por aclararme esto, funcionó perfectamente. – Ciel

+1

Recomiendo encarecidamente no hacer cosas como esta en el método de la semilla. En cambio, pertenece a una migración separada. Se llamará semilla cada vez que se active una migración. No diciendo que esta no es una solución correcta, solo que se desencadena en el lugar equivocado. –

+2

@CasperLeonNielsen: escribí este ejemplo durante mucho tiempo antes de que EF obtuviera la API de migración. Puede agregar una nueva respuesta con la migración para actualizarla. –

14

¿Por qué preocuparse por defecto para las columnas GUID en la base de datos en absoluto? ¿Por qué no generar el Guid en el cliente como cualquier otro valor? Eso requiere que tengas un método en su código de cliente que va a generar los GUID de peine:

public static Guid NewGuid() 
{ 
    var guidBinary = new byte[16]; 
    Array.Copy(Guid.NewGuid().ToByteArray(), 0, guidBinary, 0, 8); 
    Array.Copy(BitConverter.GetBytes(DateTime.Now.Ticks), 0, guidBinary, 8, 8); 
    return new Guid(guidBinary); 
} 

Una de las ventajas del GUID es específicamente que se puede generarlos en el cliente sin necesidad de volver a la base de datos.

+2

+1 Si definir COMB como Guids es tan fácil, puede combinar este código con esto: http://stackoverflow.com/questions/5275306/does-entity-framework-4-code-first-have-support-for-identity -generators-like-nhib/5277642 # 5277642 y obtienes el inicializador guid.comb. –

+0

Tuve un momento difícil eligiendo entre las dos respuestas, ya que ambas inevitablemente condujeron al mismo resultado. Ambos son útiles. Muchas gracias por esta información, funciona muy bien. – Ciel

+0

El uso de Guids generados secuencialmente hará que la indexación sea menos compleja en el servidor SQL. –

2

La respuesta más simple

public class User 
{ 
    public User(Guid? id = null, DateTime? created = null) 
    { 
     if (id != null) 
      Id = id; 

     if (created != null) 
      Created = created; 
    } 

    public User() 
    { 
    } 

    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public DateTime? Created { get; internal set; } 

    [Key] 
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)] 
    public Guid? Id { get; internal set; } 
} 

Esto supone que tiene su tabla de base fija con el valor por defecto de newsequentialid() que en mi caso es administrado por migraciones FluentMigrator.

+1

Para que esto ayude a otros, necesita un ejemplo de "Migraciones de FluentMigrator" –

+1

@CasperLeonNielsen que no es realmente cierto, requiere que el esquema de la tabla tenga un conjunto predeterminado. La forma en que establece el valor predeterminado de 'newsequentialid()' está realmente fuera del alcance de esta pregunta y se convierte en una preocupación genérica de esquema sql. –

+0

http://stackoverflow.com/questions/12257465/update-row-data-with-a-new-value-per-row-using-fluentmigrator –