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:
- A link has some data attached, so we can’t simply model a many-to-many relationship between issues and issues. We rather have to introduce a new entity Link, which represents a relationship between two issues.
- A link, by definition, links two instances of the same entity, it is a “two-to-many” relationship (a link has two issues, an issue can have many links).
- The link is directed, which means, if Issue A depends on Issue B, then Issue B is depended on by Issue A.
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”:
- Add two Collections to the Issue,
ICollection<Link> OutgoingLinks,ICollection<Link> IncomingLinks. This way the collections can be maintained by EF, but from a business logic point of view they don’t make much sense. - Only add one collection and configure EF 4.1 to add incoming and outgoing links to it, if that is possible.
-
Only add one collection and implement it on my own:
ICollection<Link> AllLinks { return _context.Links.Where(l => l.IssueA == this || l.IssueB == this).ToList(); }The problem with this approach is that the domain entity executes data access tasks which is bad in terms of seperation of concerns.
-
Any other?
Option (1) is the way to go in my opinion, together with a readonly helper perhaps which combines the two collections:
Option (2) isn’t possible because you cannot map one navigation property to two different ends/navigation properties.