【问题标题】:Entity Framework symetric many to many relation实体框架对称多对多关系
【发布时间】:2015-09-21 23:35:19
【问题描述】:

我有数据库实体Item,它可以链接到其他项目(相同的实体)。期望的状态是在Item 类上拥有一个集合:

public class Item
{
    public virtual ICollection<Item> LinkedItems { get; set; }
}

这种关系是对称的,所以如果itemA 链接到itemB,它自动意味着itemB 链接到itemA

我有以下两种方法:

  1. 拥有一个LinkedItems 属性,每次都添加两个项目:

    itemA.LinkedItems.Add(itemB);
    itemB.LinkedItems.Add(itemA);
    
  2. 有两个属性 LinkedItems1LinkedItems2 并每次连接它们:

    var linkedItems = itemA.LinkedItems.Concat(itemB.LinkedItems);
    

以上方法都不理想。可以做得更好(更清洁)吗?

我首先使用 EF 代码。

【问题讨论】:

    标签: c# entity-framework ef-code-first


    【解决方案1】:

    在您的情况下,Item 应该有 2 个导航属性。一个用于链接的项目,另一个用于链接的项目。

    public class Item
    {
        public virtual ICollection<ItemAssociation> AssociatedItems { get; set; }
        public virtual ICollection<ItemAssociation> ItemsAssociatedThisItem { get; set; }
    }
    
    public class ItemAssociation
    {
        public int ItemId { get; set; }
    
        public int ItemAssociatedId { get; set; }
    
        public virtual Item Item { get; set; }
    
        public virtual Item ItemAssociated { get; set; }
    }
    

    映射:

    modelBuilder.Entity<ItemAssociation>()
            .HasKey(i => new {i.ItemId , i.ItemAssociatedId });
    
        modelBuilder.Entity<Item>()
            .HasMany(i => i.AssociatedItems)
            .WithRequired(i => i.ItemAssociated)
            .HasForeignKey(i => i.ItemAssociatedId)
            .WillCascadeOnDelete(false);
    
        modelBuilder.Entity<Item>()
            .HasMany(i => i.ItemsAssociatedThisItem )
            .WithRequired(i => i.Item)
            .HasForeignKey(i => i.ItemId)
            .WillCascadeOnDelete(false);
    

    当您想要检索与特定项目相关的所有项目时,您可以获取两个集合,将它们连接起来并删除重复的值。

    编辑

    无关联实体的映射:

    类:

    public class Item
    {
        public int ItemId { get; set; }
    
        public virtual ICollection<Item> AssociatedItems { get; set; }
        public virtual ICollection<Item> ItemsAssociatedThisItem { get; set; }
    }
    

    映射:

    modelBuilder.Entity<Item>()
        .HasMany(i => i.AssociatedItems)
        .WithMany(i=> i.ItemsAssociatedThisItem)
        .Map(i =>
            {
                i.MapRightKey("ItemId");
                i.MapLeftKey("AssociatedItemId");
                i.ToTable("ItemsAssociation");
            });
    

    生成的迁移:

    CreateTable(
        "dbo.Items",
        c => new
            {
                ItemId = c.Int(nullable: false, identity: true),
            })
        .PrimaryKey(t => t.ItemId);
    
    CreateTable(
        "dbo.ItemsAssociation",
        c => new
            {
                AssociatedItemId = c.Int(nullable: false),
                ItemId = c.Int(nullable: false),
            })
        .PrimaryKey(t => new { t.AssociatedItemId, t.ItemId })
        .ForeignKey("dbo.Items", t => t.AssociatedItemId)
        .ForeignKey("dbo.Items", t => t.ItemId)
        .Index(t => t.AssociatedItemId)
        .Index(t => t.ItemId);
    

    我觉得现在更干净了。只需根据您的风格更改名称

    【讨论】:

    • 添加关联实体根本不能解决问题,我认为它会使问题变得更加复杂。
    • 一个Item 可以链接到多个Item,对吗?项目 A 链接到 B 和 C。项目 B 链接到 D 和 A(因为之前的链接)。所以,我相信你能做到这一点的唯一方法是使用关联实体。此外,要建立“自反关系”,实体需要 2 个集合……每种情况一个。看看这个帖子,它可能会有所帮助stackoverflow.com/questions/31461173/…
    • 您描述的是我熟悉的经典多对多关系。我提出了一个用例,从逻辑上讲,您只需要一个集合,问题是它是否可以用 EF 和一个集合来完成。不,您不必为多对多关系建立关联实体。 EF 可以为您执行此操作 - 当然 DB 中会有一个关联表,但它会隐藏在模型中。
    • 您可以隐藏“关联实体”,但您仍然必须有 2 个集合。我不相信还有另一种方法......但是,我正在寻找这个解决方案......也许可以使用拦截器。总的来说,看看我编辑的答案,看看现在是否更清楚了。
    猜你喜欢
    • 2017-01-28
    • 2011-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-13
    相关资源
    最近更新 更多