【问题标题】:How to model many-to-many relationships with a relationship entity in EF 4.1 Code First如何在 EF 4.1 Code First 中使用关系实体建模多对多关系
【发布时间】:2011-09-21 15:09:02
【问题描述】:

流行示例:在问题跟踪器 JIRA 中,问题可以链接到其他问题。链接本身附有一些数据,尤其是类型。

例子:

问题 A -> 取决于 -> 问题 B
问题 B 依赖于 问题A

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


详情

这种情况有一些特殊性:

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

我们肯定会有一个如下所示的Link实体:

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&lt;Link&gt; OutgoingLinksICollection&lt;Link&gt; IncomingLinks。这样集合可以由 EF 维护,但从业务逻辑的角度来看,它们没有多大意义。
  • 如果可能的话,只添加一个集合并配置 EF 4.1 以添加传入 传出链接。
  • 只添加一个集合,自己实现:

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

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

  • 还有其他的吗?

【问题讨论】:

  • 只有第一个解决方案是正确的,并且只有该解决方案才能与 EF 一起使用。从业务角度来看也是正确的,因为您提到链接是定向的,因此如果问题依赖于另一个问题或另一个问题依赖于这个问题,这是一个很大的区别。
  • @Ladislav:谢谢。我知道我可以指望你回答 :-) - 关于业务角度:使用链接对象的属性仍然可以检索方向信息,但我总是希望获得 all 的列表问题的链接,而不仅仅是传入或传出集(我认为...)

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


【解决方案1】:

在我看来,选项 (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) 不可行,因为您无法将一个导航属性映射到两个不同的端点/导航属性。

【讨论】:

  • 嗨,斯劳马。感谢您的回复。 readonly 属性是一个很好的提示,可用于支持业务视角的有用扩展。
  • 这就是我最终做的:-)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-12
相关资源
最近更新 更多