Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
205 views
in Technique[技术] by (71.8m points)

c# - Many-to-many collection of same entity, with two-way relationship

Suppose I have a widget entity, and I want to track other widgets that are adjacent to each. If the first widget is adjacent to the second widget, then the inverse is also true—the second is adjacent to the first.

Ideally, I would have a single collection on the entity, and could fluently configure the entity for this sort of a relationship.

public class Widget
{
    // ...

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

However, when I try that...

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

...Entity Framework doesn't like it at all.

The navigation property 'Adjacent' declared on type 'Widget' cannot be the inverse of itself.

Is there a way to configure the entity that achieves this goal, or am I going to be stuck creating parent/child collection navigation properties or separate relationship containers?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

You need to introduce another collection inside the widget, something like.

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

By default with no fluent-api configuration, this code only will create a container table of WidgetWidgets in the database that contains two columns Widget_Id and Widget_Id1.


But you need to be consistent to only use one of the collection to make an adjacent relationship. If you use AdjacentTo collection to make an adjacent relationship.

widget1.AdjacentTo.Add(widget2);

After being saved widget1.AdjacentTo will have widget2 and widget2.AdjacentFrom will have widget1.

Widget_Id   Widget_Id1
    2           1

But if you input again with AdjacentFrom collection to make an adjacent relationship.

widget1.AdjacentFrom.Add(widget2);

After being saved widget1.AdjacentFrom and widget1.AdjacentTo will have widget2. Same thing happens with widget2.

Widget_Id   Widget_Id1
    2           1
    1           2

Composite unique key can't prevent second record to be inserted, because the second record is not considered as duplicate row. But there is a workaround by adding a check constraint, you can add this constraint in the migration.

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

To select all adjacent you can add another collection, something like.

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

After adding check constraint, then you can use this extension to add or remove adjacent.

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);
        }
    }
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...