多对多的同一实体集合,具有双向关系

时间:2014-08-12 19:08:22

标签: c# .net entity-framework entity-framework-6

假设我有一个小部件实体,并且我想跟踪与每个小部件相邻的其他小部件。如果第一个小部件与第二个小部件相邻,则反之亦然 - 第二个小部件与第一个小部件相邻。

理想情况下,我会在实体上拥有一个集合,并且可以流畅地为这种关系配置实体。

public class Widget
{
    // ...

    public virtual ICollection<Widget> Adjacent { get; set; }
}

然而,当我尝试那个......

modelBuilder.Entity<Widget>
            .HasMany(w => w.Adjacent)
            .WithMany(w => w.Adjacent);

......实体框架根本不喜欢它。

  

在'Widget'类型上声明的导航属性'Adjacent'不能与其自身相反。

有没有办法配置实现此目标的实体,或者我是否会陷入创建父/子集合导航属性或单独关系容器的困境?

1 个答案:

答案 0 :(得分:8)

您需要在窗口小部件中引入另一个集合,例如。

public virtual ICollection<Widget> AdjacentFrom { get; set; }
public virtual ICollection<Widget> AdjacentTo { get; set; }

默认情况下,没有fluent-api配置,此代码仅在数据库中创建WidgetWidgets的容器表,其中包含两列Widget_IdWidget_Id1


但是你需要保持一致,只使用其中一个集合来建立相邻的关系。如果您使用AdjacentTo集合来建立相邻关系。

widget1.AdjacentTo.Add(widget2);

保存后widget1.AdjacentTowidget2widget2.AdjacentFromwidget1

Widget_Id   Widget_Id1
    2           1

但是,如果您再次使用AdjacentFrom集合输入以建立相邻关系。

widget1.AdjacentFrom.Add(widget2);

保存后widget1.AdjacentFromwidget1.AdjacentTowidget2widget2也发生了同样的事情。

Widget_Id   Widget_Id1
    2           1
    1           2

复合唯一键无法阻止插入第二条记录,因为第二条记录不被视为重复行。但是通过添加检查约束有一种解决方法,您可以在迁移中添加此约束。

Sql("alter table WidgetWidgets add constraint CK_Duplicate_Widget check (Widget_Id > Widget_Id1)");

要选择所有相邻的,您可以添加其他集合,例如。

[NotMapped]
public ICollection<Widget> Adjacent
{
   get { return (AdjacentFrom ?? new Widget[0]).Union((AdjacentTo ?? new Widget[0])).Distinct().ToArray(); }
}

添加检查约束后,您可以使用此扩展来添加或删除相邻。

public static class WidgetDbExtension
{
    public static void AddAdjacent(this Widget widget1, Widget widget2)
    {
        if (widget1.Id < widget2.Id)
        {
            widget1.AdjacentTo.Add(widget2);
        }
        else
        {
            widget2.AdjacentTo.Add(widget1);
        }
    }
    public static void RemoveAdjacent(this Widget widget1, Widget widget2)
    {
        if (widget1.Id < widget2.Id)
        {
            widget1.AdjacentTo.Remove(widget2);
        }
        else
        {
            widget2.AdjacentTo.Remove(widget1);
        }
    }
}