Mapeando chaves compostas usando o código EF primeiro

115

Tabela do servidor SQL:

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

Como devo mapear isso no código EF 6 primeiro?

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

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

Eu vi alguns exemplos em que você deve definir a ordem de cada coluna, isso é necessário?

Existe documentação oficial sobre isso em algum lugar?

fluxo leal
fonte
É SomeIdum stringou um int?
Corey Adler
@ IronMan84 É uma string, vou consertar isso.
loyalflow

Respostas:

186

Você definitivamente precisa colocar a ordem das colunas, caso contrário, como o SQL Server deve saber qual vai primeiro? Aqui está o que você precisa fazer em seu código:

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

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

Você também pode olhar para esta pergunta SO . Se você quiser a documentação oficial, eu recomendo olhar o site oficial da EF . Espero que isto ajude.

EDIT: Acabei de encontrar um post de blog de Julie Lerman com links para todos os tipos de coisas do EF 6. Você pode encontrar o que precisa aqui .

Corey Adler
fonte
Como você faz isso por meio de uma EntityConfiguration? Na verdade, não tenho uma entidade para a tabela de junção ... Só tenho as duas entidades e uma EntityConfiguration em uma delas com um .Map () para configurar o mapeamento.
Mir
31
otherwise how is SQL Server supposed to know which one goes first?- por que não da mesma forma que conhece a ordem de todas as outras colunas?
Davor
1
EF não conhece a ordem das outras colunas, você pode inserir colunas em qualquer ordem, desde que os nomes sejam especificados. Se o EF requer o pedido do PK composto, ele deve estar relacionado à indexação.
Sylvain Gantois
@Davor Eu imagino que os criadores do EF poderiam ter usado reflexão para inferir a ordem da chave / coluna, mas talvez haja considerações de desempenho para não fazer isso. Vou considerar a especificidade do tempo de design em vez do desempenho mais lento em qualquer dia, especialmente no meu DAL.
Jacob Stamm
47

Para mapear a chave primária composta usando o framework Entity, podemos usar duas abordagens.

1) Substituindo o método OnModelCreating ()

Por exemplo: eu tenho a classe de modelo chamada VehicleFeature conforme mostrado abaixo.

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

O código em meu DBContext seria 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 anotações de dados.

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 os links abaixo para obter mais informações.

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

2) Como adicionar uma chave única composta usando EF 6 Fluent Api?

Krishna
fonte
5
Para conhecimento do EF Core, a opção nº 2 não é possível , "Chaves compostas só podem ser configuradas usando a API Fluent - as convenções nunca irão configurar uma chave composta e você não pode usar Anotações de dados para configurá-la."
Tobias J
7

Por meio da configuração, você pode fazer isso:

Model1
{
    int fk_one,
    int fk_two
}

Model2
{
    int pk_one,
    int pk_two,
}

então na configuração 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
fonte
Onde na configuração de contexto?
Charlie
Se você estiver configurando contexto por meio de código usando API Fluent ... seção 7 . public class MyContext: DbContext {override protegido void OnModelCreating (DbModelBuilder modelBuilder) {modelBuilder.Entity <Model1> () .HasRequired (e => e.Model2) .WithMany (e => e.Model1s) .HasForeignKey (e.Model1s). {e.fk_one, e.fk_two}) .WillCascadeOnDelete (false); }}
philn5d
Descobri que tinha que usar ModelBuilder em vez de DbModelBuilder no dotnet core.
kiml42
6

Pensei em acrescentar a esta pergunta, pois é o principal resultado de pesquisa do Google.

Como foi observado nos comentários, no EF Core não há suporte para o uso de anotações (atributo da chave) e isso deve ser feito com fluente.

Como eu estava trabalhando em uma grande migração do EF6 para o EF Core, isso foi desagradável e tentei hackear usando o Reflection para procurar o atributo Key e depois aplicá-lo 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);
}

Não testei totalmente isso em todas as situações, mas funciona em meus testes básicos. Espero que isso ajude alguém

cmorgan091
fonte