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

7
假设我有一个小部件实体,并且我想跟踪相邻的其他小部件。如果第一个小部件与第二个小部件相邻,则反之亦然 - 第二个小部件也与第一个相邻。
理想情况下,我希望在实体上只有一个集合,并且可以流畅地配置实体以适应这种关系。
public class Widget
{
    // ...

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

然而,当我尝试这样做时...
modelBuilder.Entity<Widget>
            .HasMany(w => w.Adjacent)
            .WithMany(w => w.Adjacent);

Entity Framework 非常不喜欢这样。

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

有没有一种配置实体的方法可以实现这个目标,还是我只能创建父/子集合导航属性或单独的关系容器?

1个回答

9
你需要在小部件内引入另一个集合,类似于以下内容。
public virtual ICollection<Widget> AdjacentFrom { get; set; }
public virtual ICollection<Widget> AdjacentTo { get; set; }

默认情况下,没有流畅的API配置,此代码只会在数据库中创建一个包含两个列Widget_IdWidget_Id1WidgetWidgets容器表。


但是,您需要始终只使用其中一个集合来建立相邻关系。如果您使用AdjacentTo集合来建立相邻关系。

widget1.AdjacentTo.Add(widget2);

保存后,widget1.AdjacentTo 将会拥有 widget2,而 widget2.AdjacentFrom 将会拥有 widget1

Widget_Id   Widget_Id1
    2           1

但是,如果您再次使用“AdjacentFrom”集合进行输入,则可以建立相邻关系。
widget1.AdjacentFrom.Add(widget2);

保存后,widget1.AdjacentFromwidget1.AdjacentTo将会拥有widget2widget2同样也是如此。
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);
        }
    }
}

想到可能是这样的情况。 - Adam Maras

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接