如何在代码第一实体框架设计中避免循环依赖

时间:2017-06-04 22:07:25

标签: c# .net entity-framework design-patterns

我正在尝试创建模块化(基于组件的)应用程序。

我的方案非常简单,我有一个SalesManagement组件,其中包含Invoice,我有PartyManagement组件,其中包含Customer

到目前为止一切顺利, 然后我为Customer定义了一个类

public class Customer {
   public int ID {get;set;}
   public string Name {get;set;}
}

和Invoice as

public class Invoice {
   public int ID {get;set;}
   public int CustomerID {get;set;}
   public Customer Customer {get;set;}
}

为了在发票中Customer引用,我在PartyManagement组件中引用了SalesManagement

然后我尝试在customer下添加一个Invoice集合作为

public class Customer {
   public int ID {get;set;}
   public string Name {get;set;}
   public virtual ICollection<Invoice> Invoices {get;set;}
}

要实现它我必须在InvoiceManagement中引用PartyManagement,这会在编译时在程序集之间产生循环依赖

知道如何解决这个设计问题吗?

p.s如果我将所有内容保存在1个程序集中没有问题,我想将它们保存在单独的程序集中

2 个答案:

答案 0 :(得分:1)

“p.s如果我将所有内容保存在1个程序集中就没有问题,我想将它们保存在单独的程序集中”

别。除非你绝对不得不这样做,否则这会导致你反复出现问题,比如你已经看到过的问题。

但是,如果有正当理由将您的实体保存在单独的程序集中,那么您可以: A)在其他特定于业务的程序集中共享的公共程序集中定义共享实体(这可能会冒泡到您可能只有1个程序集的位置。) B)使用有界上下文,使得与一组业务实体相关的“客户”和/或“发票”与其他业务实体所使用的引用相比可以保持独立和唯一。

如果父实体不能彼此存在于同一个程序集中,那么您应该强烈重新考虑尝试“共享”他们的任何子项。鉴于大会A严重处理客户,而大会B大量处理发票,但会导致循环引用(A)将客户与发票相关联,那么您应该看到的是客户和发票的概念(如果需要)在程序集A中,以及在程序集B中定义的客户和发票。如果使用程序集A实体的逻辑“使用”来自发票的任何数据,它将是最小的,甚至根本不使用。 “发票”的实体可以保持非常轻量级。程序集B可能会更多地处理发票,并且只需要有关客户的令牌信息。

通过拆分域,您可以保持实体的实现符合目的,从而提高性能。因此,例如,当您处理来自程序集A的某些内容时,您需要客户详细信息而不关心发票,A中的客户实现甚至没有映射发票,或者简单的发票实体作为摘要视图。 (我在有限的上下文中将这些实体称为“轻量级”)。请记住,只有一个上下文应该作为任何给定实体的责任方。 (只有一个有界上下文映射“重量级”实体)在上面的例子中,重量级客户将驻留在程序集A中,在程序集B中使用重量级发票。轻量级可以通过在项目中使用文件链接在有界上下文之间共享,但这是通常比它的价值更麻烦。

答案 1 :(得分:0)

如果您特别要求双向链接,那么您可以使用您的ORM通过延迟加载任何链接的实体来处理此问题 - 您已经使用Customer.Invoices集合执行此操作,因为它具有{{1} } keyword。

如果之前没有遇到过这种情况,则意味着链接的实体或集合在被引用之前不会被加载,这会阻止循环引用导致数据访问出现问题,但仍允许您使用模式。

我假设您正在使用Entity Framework,在这种情况下,您可以在此处详细了解:https://msdn.microsoft.com/en-us/library/jj574232(v=vs.113).aspx

编辑 - 我会做什么,以避免循环引用*

|的解决方案

| - ProjectOne (引用Dtos和存储库)

| - ProjectTwo (引用Dtos和存储库)

| - 存储库(引用实体和Dtos,但仅返回Dtos

| - 实体(不引用任何其他项目 - EF上下文在这里)

| - Dtos (不引用任何其他项目)

这意味着ProjectOne&amp; ProjectTwo与我们的DataAccess实现分离,我们可以轻松地模拟我们的Dto模型关系,因为它们都在同一个Dtos项目中,也不受我们的数据库“实体”模型的限制 - 这意味着我们可以组合来自不同来源的数据或使其更多可以通过客户端代码轻松使用。

*此处还有覆盖我们的Repository层的接口(可能还有一个Service层以及更大的解决方案)。我把它留下来简化布局。