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?

DotnetSparrow
la source

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; }
}
Ladislav Mrnka
la source
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; }
}
Christophe
la source
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