chave composta como chave estrangeira

93

Estou usando o Entity framework 4.1 no aplicativo MVC 3. Eu tenho uma entidade onde tenho a chave primária composta por duas colunas (chave composta). E isso está sendo usado em outra entidade como chave estrangeira. Como criar o relacionamento? Em scnerios normais, usamos:

public class Category
{
    public string CategoryId { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public string CategoryId { get; set; }

    public virtual Category Category { get; set; }
} 

mas e se a categoria tiver duas chaves de colunas?

DotnetSparrow
fonte

Respostas:

172

Você pode usar qualquer API fluente:

public class Category
{
    public int CategoryId1 { get; set; }
    public int CategoryId2 { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    public int ProductId { get; set; }
    public string Name { get; set; }
    public int CategoryId1 { get; set; }
    public int CategoryId2 { get; set; }

    public virtual Category Category { get; set; }
}

public class Context : DbContext
{
    public DbSet<Category> Categories { get; set; }
    public DbSet<Product> Products { get; set; }

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

        modelBuilder.Entity<Category>()
            .HasKey(c => new {c.CategoryId1, c.CategoryId2});

        modelBuilder.Entity<Product>()
            .HasRequired(p => p.Category)
            .WithMany(c => c.Products)
            .HasForeignKey(p => new {p.CategoryId1, p.CategoryId2});

    }
}

Ou anotações de dados:

public class Category
{
    [Key, Column(Order = 0)]
    public int CategoryId2 { get; set; }
    [Key, Column(Order = 1)]
    public int CategoryId3 { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    [Key]
    public int ProductId { get; set; }
    public string Name { get; set; }
    [ForeignKey("Category"), Column(Order = 0)]
    public int CategoryId2 { get; set; }
    [ForeignKey("Category"), Column(Order = 1)]
    public int CategoryId3 { get; set; }

    public virtual Category Category { get; set; }
}
Ladislav Mrnka
fonte
Preciso manter as propriedades virtuais (categoria pública virtual da categoria {get; set;}), bem como as anunciações de dados?
DotnetSparrow
4
virtualnas propriedades de navegação é necessário para carregamento lento. virtualnas propriedades escalares ajuda no rastreamento de alterações de objetos anexados.
Ladislav Mrnka
4
O que você faria se os nomes das colunas da tabela de chave estrangeira fossem diferentes do que está no pai? Por exemplo, no produto, como você rotularia o atributo ForeignKey se os nomes das colunas fossem semelhantes a: PCategoryId2, PCategoryId3?
Em relação a esta linha: .HasRequired(p => p.Category)mas Productnão tem uma propriedade da Entidade, Catagory mas dois ids que constituem a chave composta de uma categoria. Pode explicar, porque acredito que nem vai compilar ... Obrigado!
gdoron está apoiando Monica em
@gdoron: Producttem Categoryem minha resposta.
Ladislav Mrnka
26

Acredito que a maneira mais fácil é usar a anotação de dados na propriedade Navigation assim: [ForeignKey("CategoryId1, CategoryId2")]

public class Category
{
    [Key, Column(Order = 0)]
    public int CategoryId1 { get; set; }
    [Key, Column(Order = 1)]
    public int CategoryId2 { get; set; }
    public string Name { get; set; }

    public virtual ICollection<Product> Products { get; set; }
}

public class Product
{
    [Key]
    public int ProductId { get; set; }
    public string Name { get; set; }
    public int CategoryId1 { get; set; }
    public int CategoryId2 { get; set; }

    [ForeignKey("CategoryId1, CategoryId2")]
    public virtual Category Category { get; set; }
}
Christophe
fonte
Isso funcionou muito bem. Eu também prefiro usar isso em Navigationpropriedades. No entanto, como posso definir apenas cascadeDelete: falsepara esta propriedade, não para todo o site? Obrigado
RoLYroLLs
Em alguns casos, a chave estrangeira também faz parte da chave composta da tabela atual. Assim funcionou. A outra forma (@Ladislov) não. Recebi o erro: "Atributo de coluna duplicado"
D. Kermott
RoLYroLLs: cascadeDelete é definido no arquivo de migração (depois de usar o comando add-migration package manager). Um exemplo: AddForeignKey ("dbo.Product", "GuidedActivityID", "dbo.GuidedActivity", "ID", cascadeDelete: false);
Christophe