chiave composta come chiave esterna


91

Sto usando Entity Framework 4.1 nell'applicazione MVC 3. Ho un'entità in cui ho la chiave primaria composta da due colonne (chiave composta). E questo viene utilizzato in un'altra entità come chiave esterna. Come creare la relazione? Negli scenari normali usiamo:

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

ma cosa succede se la categoria ha due colonne chiave?

Risposte:


169

Puoi utilizzare una delle due API fluenti:

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

    }
}

O annotazioni di dati:

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

Devo mantenere le proprietà virtuali (categoria di categoria virtuale pubblica {get; set;}) così come le innovazioni dei dati?
DotnetSparrow

4
virtualsulle proprietà di navigazione è necessario per il caricamento lento. virtualsulle proprietà scalari aiuta con il rilevamento delle modifiche degli oggetti associati.
Ladislav Mrnka

4
Cosa faresti se i nomi delle colonne della tabella della chiave esterna fossero diversi da quelli contenuti nel genitore? Ad esempio, in product, come etichetteresti l'attributo ForeignKey se i nomi delle colonne fossero simili a: PCategoryId2, PCategoryId3?

Riguardo a questa riga: .HasRequired(p => p.Category)but Productnon ha una proprietà dell'Entità Catagory ma due id che fanno la chiave composta di una categoria. Puoi spiegarmi per favore, perché credo che non si compilerà nemmeno ... Grazie!
gdoron supporta Monica il

@gdoron: Productha Categorynella mia risposta.
Ladislav Mrnka

25

Credo che il modo più semplice sia utilizzare l'annotazione dei dati sulla proprietà di navigazione in questo modo: [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; }
}

Funzionava benissimo. Anch'io preferisco usarlo sulle Navigationproprietà. Tuttavia, come posso impostare solo cascadeDelete: falseper questa proprietà, non a livello di sito? Grazie
RoLYroLLs

In alcuni casi la chiave esterna fa anche parte della chiave composta della tabella corrente. In questo modo ha funzionato. L'altro modo (@Ladislov) no. Ho ricevuto l'errore: "Attributo colonna duplicato"
D. Kermott

RoLYroLLs: cascadeDelete è impostato nel file di migrazione (dopo aver utilizzato il comando add-migration package manager). Un esempio: AddForeignKey ("dbo.Product", "GuidedActivityID", "dbo.GuidedActivity", "ID", cascadeDelete: false);
Christophe
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.