clé composite comme clé étrangère


91

J'utilise Entity Framework 4.1 dans l'application MVC 3. J'ai une entité où j'ai la clé primaire se compose de deux colonnes (clé composite). Et ceci est utilisé dans une autre entité comme clé étrangère. Comment créer la relation? Dans les scnerios normaux, nous utilisons:

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

mais que faire si la catégorie a deux colonnes clés?

Réponses:


169

Vous pouvez utiliser l'une des API fluentes:

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 annotations de données:

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

Dois-je conserver les propriétés virtuelles (catégorie de catégorie virtuelle publique {get; set;}) ainsi que les annovations de données?
DotnetSparrow

4
virtualsur les propriétés de navigation est nécessaire pour le chargement paresseux. virtualsur les propriétés scalaires facilite le suivi des modifications des objets attachés.
Ladislav Mrnka

4
Que feriez-vous si les noms de colonne de la table de clés étrangères étaient différents de ceux du parent? Par exemple, dans le produit, comment étiqueteriez-vous l'attribut ForeignKey si les noms de colonne ressemblaient à: PCategoryId2, PCategoryId3?

Concernant cette ligne: .HasRequired(p => p.Category)mais Productn'a pas de propriété de l' entité Catagory mais deux identifiants qui constituent la clé composite d'une catégorie. Pouvez-vous s'il vous plaît expliquer, parce que je crois qu'il ne sera même pas compilé ... Merci!
gdoron soutient Monica

@gdoron: Producta Categorydans ma réponse.
Ladislav Mrnka

25

Je pense que le moyen le plus simple est d'utiliser l'annotation de données sur la propriété Navigation comme ceci: [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; }
}

Cela a très bien fonctionné. Moi aussi, je préfère l'utiliser sur les Navigationpropriétés. Cependant, comment puis-je définir cascadeDelete: falsepour cette propriété uniquement, pas à l'échelle du site? Merci
RoLYroLLs

Dans certains cas, la clé étrangère fait également partie de la clé composite de la table actuelle. Cette façon a fonctionné. L'autre moyen (@Ladislov) ne l'a pas fait. J'ai eu l'erreur: "Attribut de colonne en double"
D. Kermott

RoLYroLLs: cascadeDelete est défini dans le fichier de migration (après utilisation de la commande add-migration package manager). Un exemple: AddForeignKey ("dbo.Product", "GuidedActivityID", "dbo.GuidedActivity", "ID", cascadeDelete: false);
Christophe
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.