Reputation: 14716
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":
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 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?
Upvotes: 0
Views: 278
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