【问题标题】:Entity Framework entity referencing another twice实体框架实体引用另一个两次
【发布时间】:2026-01-20 04:15:01
【问题描述】:

我有这个产品类用于点餐餐厅应用程序:

public class Product
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public string Name { get; set; }
    public decimal Price { get; set; }

    public virtual ICollection<ServingChoice> ServingChoices { get; set; }
}

一种产品可以选择服务,例如“每日汤”为用户提供了在几种产品中进行选择的选择。 Product 和 Choices 都是 Product 类型:

public class ServingChoice
{
    [Key]
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    public int ProductId { get; set; }
    public int ChoiceId { get; set; }

    public Product Product { get; set; }
    [InverseProperty("ServingChoices")]
    public Product Choice { get; set; }
}

我做错了什么,因为产品从不加载它的选择。该表似乎已正确创建。

已编辑

它看起来有点工作,但反过来。如果我手动将一些记录添加到数据库中,如下所示:

ProductID | ChoiceID
1           10
2           10
3           10

它适用于 Id=10 的产品。

【问题讨论】:

    标签: entity-framework one-to-many


    【解决方案1】:

    我认为您的问题是因为您没有按照应有的方式映射 FK。试试这个:

    public class ServingChoice
    {
      //...
      [ForeignKey("Product")]
      public int ProductId { get; set; }
      [ForeignKey("Choice")]
      public int ChoiceId { get; set; }
    
      public Product Product { get; set; }
    
      [InverseProperty("ServingChoices")]
      public Product Choice { get; set; }
     }
    

    顺便说一句,您正在配置两个一对多关系,但在涉及Product 导航属性(在ServingChoice 类中)的关系中,您没有定义另一端。如果你想这样做,你应该在 Product 实体中声明另一个导航属性:

    public class Product
    {
      [Key]
      //[DatabaseGenerated(DatabaseGeneratedOption.Identity)] this is not necessary, it's the default behavior
      public int Id { get; set; }
      public string Name { get; set; }
      public decimal Price { get; set; }
    
      public virtual ICollection<ServingChoice> ProductChoices { get; set; }
      public virtual ICollection<ServingChoice> ServingChoices { get; set; }
    }
    

    然后,在ServiceChoice 类中,您应该在Product 导航属性上添加InverseProperty 注释,以明确指定关系的另一端是什么:

    public class ServingChoice
    {
      [Key]
      // [DatabaseGenerated(DatabaseGeneratedOption.Identity)] the same I explain before
      public int Id { get; set; }
      [ForeignKey("Product")]
      public int ProductId { get; set; }
      [ForeignKey("Choice")]
      public int ChoiceId { get; set; }
    
      [InverseProperty("ProductChoices")]
      public Product Product { get; set; }
    
      [InverseProperty("ServingChoices")]
      public Product Choice { get; set; }
     }
    

    此外,您可以按照相同的想法使用 Fluent Api

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
         modelBuilder.Entity<ServingChoice>().HasRequired(m => m.Product).WithMany(m => m.ProductChoices).HasForeignKey(m=>m.ProductId);
         modelBuilder.Entity<ServingChoice>().HasRequired(m => m.Choice).WithMany(m => m.ServingChoices).HasForeignKey(m=>m.ChoiceId);
    }
    

    【讨论】:

    • 好吧,我不知道Product 导航属性在您的逻辑中的用途,但是如果您愿意,请不要声明ProductChoice 属性,这不是必需的。尝试仅映射 FK。现在再次看到您的模型,我认为这可以解决您的问题。尝试添加一些元素,然后告诉我之后发生了什么。
    • 设置好Inverse属性后,就可以了。谢谢。但是现在出现了一个新问题:在保存一个包含 Product 的 Order 后,该产品的所有 ServingChoice 项在数据库中都翻了一番。知道为什么吗?
    • OrderProduct 之间的关系是什么,您如何保存 Order
    • Order has many OrderItemsProduct 属于 OrderItem。我编辑了问题以包含这些类。
    • 我认为你的问题是因为你正在使用分离的实体。如果你有一个你知道数据库中已经存在但当前没有被上下文跟踪的实体,那么你可以告诉上下文使用 DbSet 上的 Attach 方法跟踪实体。该实体将在上下文中处于未更改状态。