chiccodoro
chiccodoro

Reputation: 14716

How to model many-to-many relationships with a relationship entity in EF 4.1 Code First

Popular example: In the issue tracker JIRA, issues can be linked to other issues. The link itself has some data attached, in particular a type.

Example:

Issue A -> depends on -> Issue B
Issue B <- is depended on by <- Issue A

We are introducing the same kind of relationship for an entity in our C# ASP.NET MVC application using EF 4.1 CodeFirst, and I'm wondering how to best model this relationship?


Details:

There are some particularities about this situation:

We will certainly have a Link entity that looks like this:

public class Link
{
    public int ID { get; set; }
    public Issue IssueA { get; set; }
    public Issue IssueB { get; set; }
    public LinkType Type { get; set; }
}

The Issue class might look like this:

public class Issue
{
    public int ID { get; set; }
    public virtual ICollection<Link> Links { get; set; }
}

Currently there would be only one link type: dependency. So, the link type would look like this:

public class LinkType
{
    public int ID { get; set; }
    public string ForwardName { get; set; } // depends on
    public string BackwardName { get; set; } // is depended on by
}

Now for the big question:

If I want EF to automatically manage Issue.Links, I have to tell it what Foreign key on the Link table to use. Either I use IssueA, or I use IssueB. I can't use both, can I?

Either I define:

modelBuilder.Entity<Issue>().HasMany(i => i.Links).WithRequired(l => l.IssueA);

or I define:

modelBuilder.Entity<Issue>().HasMany(i => i.Links).WithRequired(l => l.IssueB);

Possible approaches - I am curious about your feedback on whether some of them will lead to troubles, cannot be implemented, or whether any of these approaches can be regarded as "best practice":

Upvotes: 0

Views: 278

Answers (1)

Slauma
Slauma

Reputation: 177143

Option (1) is the way to go in my opinion, together with a readonly helper perhaps which combines the two collections:

public class Issue
{
    public int ID { get; set; }
    public virtual ICollection<Link> OutgoingLinks { get; set; }
    public virtual ICollection<Link> InComingLinks { get; set; }

    public IEnumerable<Link> Links // not mapped because readonly
    {
        get { return OutgoingLinks.Concat(InComingLinks); }
    }
}

Option (2) isn't possible because you cannot map one navigation property to two different ends/navigation properties.

Upvotes: 2

Related Questions