如何在EF 4.1 Code First中建立与关系实体的多对多关系模型

时间:2011-09-21 15:09:03

标签: many-to-many entity-framework-4.1 ef-code-first

热门示例:在问题跟踪器JIRA中,问题可以与其他问题相关联。链接本身附有一些数据,特别是一种类型。

示例:

问题A - > 取决于 - > 问题B
问题B < - 取决于< - 问题A

我们正在使用EF 4.1 CodeFirst为我们的C#ASP.NET MVC应用程序中的实体引入相同类型的关系,我想知道如何最好地建模这种关系?


详情

这种情况有一些特殊之处:

  • 链接附加了一些数据,因此我们不能简单地模拟问题与问题之间的多对多关系。我们宁愿引入新的实体链接,它代表两个问题之间的关系。
  • 根据定义,链接链接同一实体的两个实例,它是“两对多”关系(链接有两个问题,一个问题可以有很多链接)。
  • 该链接是定向的,这意味着,如果问题A 依赖于问题B,那么问题B 将依赖于问题A.

我们肯定会有一个链接实体,如下所示:

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

问题类可能如下所示:

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

目前只有一种链接类型:依赖。因此,链接类型如下所示:

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

现在提出一个大问题:

如果我希望EF自动管理Issue.Links,我必须告诉它使用Link表上的外键。要么我使用IssueA,要么使用IssueB。我不能同时使用,可以吗?

我要么定义:

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

或我定义:

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

可能的方法 - 我很好奇您对其中一些是否会导致问题,无法实施或是否可以将这些方法视为“最佳做法”的反馈:

  • 将两个收藏集添加到问题ICollection<Link> OutgoingLinksICollection<Link> IncomingLinks。这样,集合可以由EF维护,但从业务逻辑的角度来看,它们没有多大意义。
  • 仅添加一个集合并配置EF 4.1以向其添加传入的传出链接(如果可能的话)。
  • 只添加一个集合并自行实现:

    ICollection<Link> AllLinks { return _context.Links.Where(l => l.IssueA == this || l.IssueB == this).ToList(); }

    这种方法的问题在于,域实体执行的数据访问任务在关注点分离方面是不好的。

  • 还有其他吗?

1 个答案:

答案 0 :(得分:2)

选项(1)是我认为的方式,以及一个结合两个集合的只读助手:

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

选项(2)是不可能的,因为您无法将一个导航属性映射到两个不同的端/导航属性。