klucz złożony jako klucz obcy

91

Używam Entity Framework 4.1 w aplikacji MVC 3. Mam jednostkę, w której mam klucz podstawowy składa się z dwóch kolumn (klucz złożony). I to jest używane w innej jednostce jako klucz obcy. Jak stworzyć relację? W normalnych scneriosach używamy:

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

ale co jeśli kategoria ma klucz dwóch kolumn?

DotnetSparrow
źródło

Odpowiedzi:

169

Możesz użyć dowolnego interfejsu API Fluent:

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

    }
}

Lub adnotacje danych:

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
źródło
Czy muszę zachować właściwości wirtualne (publiczną wirtualną kategorię kategorii {get; set;}), a także annovacje danych?
DotnetSparrow
4
virtualwłaściwości nawigacji jest konieczne do leniwego ładowania. virtualna właściwościach skalarnych pomaga w śledzeniu zmian dołączonych obiektów.
Ladislav Mrnka
4
Co byś zrobił, gdyby nazwy kolumn tabeli kluczy obcych były inne niż w tabeli nadrzędnej? Na przykład w produkcie, w jaki sposób oznaczyłbyś atrybut ForeignKey, gdyby nazwy kolumn wyglądały następująco: PCategoryId2, PCategoryId3?
W odniesieniu do tej linii: .HasRequired(p => p.Category)ale Productnie ma właściwości Entity, Catagory ale dwa identyfikatory, które tworzą złożony klucz kategorii. Czy możesz wyjaśnić, ponieważ wierzę, że nawet się nie skompiluje ... Dzięki!
gdoron wspiera Monikę
@gdoron: Productma Categoryw mojej odpowiedzi.
Ladislav Mrnka
25

Uważam, że najłatwiejszym sposobem jest użycie adnotacji danych we właściwości Navigation w następujący sposób: [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
źródło
To działało świetnie. Ja też wolę używać tego na Navigationwłaściwościach. Jak jednak mogę ustawić cascadeDelete: falsetylko tę właściwość, a nie całą witrynę? Dzięki
RoLYroLLs
W niektórych przypadkach klucz obcy jest również częścią klucza złożonego bieżącej tabeli. Ten sposób zadziałał. W inny sposób (@Ladislov) nie. Otrzymałem błąd: „Zduplikowany atrybut kolumny”
D. Kermott,
RoLYroLLs: cascadeDelete jest ustawiane w pliku migracyjnym (po użyciu polecenia add -igration package manager). Przykład: AddForeignKey („dbo.Product”, „GuidedActivityID”, „dbo.GuidedActivity”, „ID”, cascadeDelete: false);
Christophe