Mapeo de claves compuestas usando el código EF primero

115

Tabla de servidor SQL:

SomeId PK varchar(50) not null 
OtherId PK int not null

¿Cómo debo asignar esto en el código EF 6 primero?

public class MyTable
{
    [Key]
    public string SomeId { get; set; }

    [Key]
    public int OtherId { get; set; }
}

He visto algunos ejemplos en los que debe establecer el orden para cada columna, ¿es necesario?

¿Hay documentación oficial sobre esto en alguna parte?

Loyalflow
fuente
¿Es SomeIduna stringo una int?
Corey Adler
@ IronMan84 Es una cadena, lo arreglaré.
loyalflow

Respuestas:

187

Definitivamente debe poner el orden de las columnas, de lo contrario, ¿cómo se supone que SQL Server sepa cuál va primero? Esto es lo que debe hacer en su código:

public class MyTable
{
  [Key, Column(Order = 0)]
  public string SomeId { get; set; }

  [Key, Column(Order = 1)]
  public int OtherId { get; set; }
}

También puede consultar esta pregunta SO . Si desea documentación oficial, le recomiendo que consulte el sitio web oficial de EF . Espero que esto ayude.

EDITAR: Acabo de encontrar una publicación de blog de Julie Lerman con enlaces a todo tipo de bondades de EF 6. Puedes encontrar lo que necesites aquí .

Corey Adler
fuente
¿Cómo se hace esto a través de una EntityConfiguration? En realidad, no tengo una entidad para la tabla de combinación ... Solo tengo las dos entidades y una EntityConfiguration en una de ellas con un .Map () para configurar el mapeo.
Mir
31
otherwise how is SQL Server supposed to know which one goes first?- ¿Por qué no conoce el orden de las demás columnas de la misma forma?
Davor
1
EF no conoce el orden de otras columnas, puede insertar columnas en cualquier orden siempre que se especifiquen los nombres. Si EF requiere el pedido del PK compuesto, debe estar relacionado con la indexación.
Sylvain Gantois
@Davor Me imagino que los creadores de EF podrían haber usado la reflexión para inferir el orden de clave / columna, pero tal vez haya consideraciones de rendimiento para no hacer esto. Cualquier día tomaré la especificidad del tiempo de diseño sobre el rendimiento más lento, especialmente en mi DAL.
Jacob Stamm
47

Para el mapeo de la clave primaria compuesta usando Entity framework, podemos usar dos enfoques.

1) Anulando el método OnModelCreating ()

Por ejemplo: tengo la clase de modelo denominada VehicleFeature como se muestra a continuación.

public class VehicleFeature
{
    public int VehicleId { get; set; }
    public int FeatureId{get;set;}
    public Vehicle Vehicle{get;set;}
    public Feature Feature{get;set;}
}

El código en mi DBContext sería como,

public class VegaDbContext : DbContext
{
    public DbSet<Make> Makes{get;set;}

    public DbSet<Feature> Features{get;set;}
    public VegaDbContext(DbContextOptions<VegaDbContext> options):base(options)        
    {           

    }
    // we override the OnModelCreating method here.
    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<VehicleFeature>().HasKey(vf=> new {vf.VehicleId, vf.FeatureId});
    }
}

2) Por anotaciones de datos.

public class VehicleFeature
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]  
    [Key]
    public int VehicleId { get; set; }
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]   
    [Key]
    public int FeatureId{get;set;}
    public Vehicle Vehicle{get;set;}
    public Feature Feature{get;set;}
}

Consulte los enlaces a continuación para obtener más información.

1) https://msdn.microsoft.com/en-us/library/jj591617(v=vs.113).aspx

2) ¿Cómo agregar una clave única compuesta usando EF 6 Fluent Api?

Krishna
fuente
5
Para su información, para EF Core, la opción n. ° 2 no es posible , "Las claves compuestas solo se pueden configurar usando la API Fluent; las convenciones nunca configurarán una clave compuesta y no puede usar anotaciones de datos para configurar una".
Tobias J
7

A través de la configuración, puede hacer esto:

Model1
{
    int fk_one,
    int fk_two
}

Model2
{
    int pk_one,
    int pk_two,
}

luego en la configuración de contexto

public class MyContext : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Model1>()
            .HasRequired(e => e.Model2)
            .WithMany(e => e.Model1s)
            .HasForeignKey(e => new { e.fk_one, e.fk_two })
            .WillCascadeOnDelete(false);
    }
}
philn5d
fuente
¿Dónde en la configuración de contexto?
Charlie
Si está configurando el contexto a través de código usando Fluent API ... sección 7 . public class MyContext: DbContext {anulación protegida void OnModelCreating (DbModelBuilder modelBuilder) {modelBuilder.Entity <Model1> () .HasRequired (e => e.Model2) .WithMany (e => e.Model1s) .HasForeignKey (e => nuevo {e.fk_one, e.fk_two}) .WillCascadeOnDelete (falso); }}
philn5d
Descubrí que tenía que usar ModelBuilder en lugar de DbModelBuilder en dotnet core.
kiml42
6

Pensé que agregaría algo a esta pregunta, ya que es el mejor resultado de búsqueda de Google.

Como se ha señalado en los comentarios, en EF Core no hay soporte para el uso de anotaciones (atributo clave) y debe hacerse con fluidez.

Como estaba trabajando en una gran migración de EF6 a EF Core, esto fue desagradable y traté de piratearlo usando Reflection para buscar el atributo Key y luego aplicarlo durante OnModelCreating

// get all composite keys (entity decorated by more than 1 [Key] attribute
foreach (var entity in modelBuilder.Model.GetEntityTypes()
    .Where(t => 
        t.ClrType.GetProperties()
            .Count(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(KeyAttribute))) > 1))
{
    // get the keys in the appropriate order
    var orderedKeys = entity.ClrType
        .GetProperties()
        .Where(p => p.CustomAttributes.Any(a => a.AttributeType == typeof(KeyAttribute)))
        .OrderBy(p => 
            p.CustomAttributes.Single(x => x.AttributeType == typeof(ColumnAttribute))?
                .NamedArguments?.Single(y => y.MemberName == nameof(ColumnAttribute.Order))
                .TypedValue.Value ?? 0)
        .Select(x => x.Name)
        .ToArray();

    // apply the keys to the model builder
    modelBuilder.Entity(entity.ClrType).HasKey(orderedKeys);
}

No lo he probado completamente en todas las situaciones, pero funciona en mis pruebas básicas. Espero que esto ayude a alguien

cmorgan091
fuente