You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Entity Framework Core多对多关系主键重复问题求助

EF Core多对多关系主键重复问题解决

问题描述

在用Entity Framework Core构建Item和Ingredient的多对多关系时,使用中间关联类ItemIngredient,首次添加ItemId=1、IngredientId=1的记录正常,但添加ItemId=1、IngredientId=2的记录时触发主键重复错误。相关实体ID均存在且可正常读取,问题出在关系配置上。

报错截图:
报错截图

原代码

DataContext配置代码

modelBuilder.Entity<ItemIngredient>()
            .HasKey(bc => new { bc.ItemId, bc.IngredientId });
modelBuilder.Entity<ItemIngredient>()
            .HasOne(bc => bc.Item)
            .WithMany(b => b.ItemIngredients)
            .HasForeignKey(bc => bc.Id);
modelBuilder.Entity<ItemIngredient>()
            .HasOne(bc => bc.Ingredient)
            .WithMany(c => c.ItemIngredients)
            .HasForeignKey(bc => bc.Id);

Item实体类

using DTO;
using System.ComponentModel.DataAnnotations;

namespace CateringApp
{
    public class Item
    {
        public enum ItemType
        {
            Food,
            Drinks
        }

        public enum OrderStatus
        {
            Preparing,
            Cooking,
            Ready
        }
        [Key]
        public int ID { get; set; }
        public string Name { get; set; }
        public double Price { get; set; }
        public ItemType itemType { get; set; }
        public string Description { get; set; }
        public ICollection<ItemOrder> ItemOrder { get; set; }
        public ICollection<ItemIngredient> ItemIngredients { get; set; }
    }
}

Ingredient实体类

using DTO;
using System.ComponentModel.DataAnnotations;

namespace CateringApp
{
    public class Ingredient
    {
        [Key]
        public int ID { get; set; }
        public string Name { get; set; }
        public int Amount { get; set; }
        public double Price { get; set; }
        public ICollection<ItemIngredient> ItemIngredients { get; set; }
    }
}

ItemIngredient中间类

using CateringApp;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DTO
{
    public class ItemIngredient
    {
        [Key]
        public int Id { get; set; }
        public int ItemId { get; set; }
        public int IngredientId { get; set; }
        public Item Item { get; set; }
        public Ingredient Ingredient {get; set;}
    }
}

问题分析

  1. 主键配置冲突:ItemIngredient类中通过[Key]标记了单独的Id作为主键,但在DataContext里又配置了ItemId+IngredientId的复合主键,导致EF无法确定正确的主键逻辑。
  2. 外键配置错误:关联关系中错误地将bc.Id作为外键指向Item和Ingredient,这完全不符合多对多关联的逻辑——外键应该分别对应ItemId和IngredientId。这种错误配置会导致EF在插入记录时,主键生成逻辑混乱,最终触发重复错误。

修正方案

方案一:使用复合主键(推荐,多对多中间表标准做法)

1. 修改ItemIngredient类

移除单独的Id主键,让复合主键作为唯一标识:

using CateringApp;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DTO
{
    public class ItemIngredient
    {
        public int ItemId { get; set; }
        public int IngredientId { get; set; }
        public Item Item { get; set; }
        public Ingredient Ingredient { get; set; }
    }
}

2. 修正DataContext配置

指定正确的外键字段:

modelBuilder.Entity<ItemIngredient>()
            .HasKey(bc => new { bc.ItemId, bc.IngredientId });
modelBuilder.Entity<ItemIngredient>()
            .HasOne(bc => bc.Item)
            .WithMany(b => b.ItemIngredients)
            .HasForeignKey(bc => bc.ItemId); // 关联Item的外键为ItemId
modelBuilder.Entity<ItemIngredient>()
            .HasOne(bc => bc.Ingredient)
            .WithMany(c => c.ItemIngredients)
            .HasForeignKey(bc => bc.IngredientId); // 关联Ingredient的外键为IngredientId

方案二:保留单独Id主键(适用于中间表需要额外属性的场景)

如果需要给ItemIngredient添加其他属性,可保留单独的Id主键,但要添加唯一约束避免重复关联:

1. ItemIngredient类(保留Id)

using CateringApp;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace DTO
{
    public class ItemIngredient
    {
        [Key]
        public int Id { get; set; }
        public int ItemId { get; set; }
        public int IngredientId { get; set; }
        public Item Item { get; set; }
        public Ingredient Ingredient { get; set; }
        // 可添加其他属性,比如用量:public int Quantity { get; set; }
    }
}

2. 修正DataContext配置

modelBuilder.Entity<ItemIngredient>()
            .HasOne(bc => bc.Item)
            .WithMany(b => b.ItemIngredients)
            .HasForeignKey(bc => bc.ItemId);
modelBuilder.Entity<ItemIngredient>()
            .HasOne(bc => bc.Ingredient)
            .WithMany(c => c.ItemIngredients)
            .HasForeignKey(bc => bc.IngredientId);
// 添加唯一约束,确保同一Item和Ingredient不会重复关联
modelBuilder.Entity<ItemIngredient>()
            .HasIndex(bc => new { bc.ItemId, bc.IngredientId })
            .IsUnique();

说明

两种方案都能解决主键重复问题,方案一更符合多对多关联的设计规范,方案二更灵活,适合中间表需要存储额外数据的场景。

内容的提问来源于stack exchange,提问作者Venoz

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 03:30:56