C# 使用实体框架的存储库模式检索复杂对象图的模式

C# 使用实体框架的存储库模式检索复杂对象图的模式,c#,asp.net,asp.net-mvc,entity-framework,repository-pattern,C#,Asp.net,Asp.net Mvc,Entity Framework,Repository Pattern,我们有一个ASP.NETMVC站点,它使用实体框架抽象以及存储库和UnitOfWork模式。我想知道的是,其他人是如何用这些模式实现复杂对象图的导航的。让我举一个我们其中一个控制器的例子: var model = new EligibilityViewModel { Country = person.Pathway.Country.Name, Pathway = person.Pathway.Name, Answers = person.Answe

我们有一个ASP.NETMVC站点,它使用实体框架抽象以及存储库和UnitOfWork模式。我想知道的是,其他人是如何用这些模式实现复杂对象图的导航的。让我举一个我们其中一个控制器的例子:

var model = new EligibilityViewModel
   {
       Country = person.Pathway.Country.Name,
       Pathway = person.Pathway.Name,
       Answers = person.Answers.ToList(),
       ScoreResult = new ScoreResult(person.Score.Value),
       DpaText = person.Pathway.Country.Legal.DPA.Description,
       DpaQuestions = person.Pathway.Country.Legal.DPA.Questions,
       Terms = person.Pathway.Country.Legal.Terms,
       HowHearAboutUsOptions = person.Pathway.Referrers
   };
这是一个注册过程,几乎所有事情都与POCO类人员有关。在本例中,我们通过注册过程缓存此人。我现在已经开始实现注册过程的后一部分,这需要访问对象图中更深的数据。具体来说,DPA数据挂起了国内的法律


上面的代码只是将模型信息映射为ViewModel的更简单格式。我的问题是,你是否考虑过对图的良好实践的相当深的导航,或者你将抽象出的对象进一步从图中提取到知识库中吗?

< P>这取决于你在任何时候使用的信息有多大。 例如,如果您只想获取一个人的国家名称(person.path.country.name),那么从数据库中获取所有其他对象的意义是什么

当我只需要一小部分数据时,我倾向于提取我将要使用的数据。换句话说,我将投射到一个匿名类型(或者一个特别制作的具体类型,如果我必须有一个)

每次要访问某些属性时,都要拉出整个对象以及与该对象相关的所有内容,这不是一个好主意。如果你每次回发都这样做一次,甚至多次呢?通过这样做,您可能会在短期内使生活变得更轻松,但从长远来看,您的应用程序的可扩展性会降低


正如我在一开始所说的,这方面没有一刀切的规则,但我要说的是,你很少需要这么多信息。

在我看来,这里的重要问题是-你是否禁用了懒散加载?

如果您没有做任何事情,那么默认情况下它处于启用状态


因此,当您执行
Person.Pathway.Country
时,您将调用另一个对数据库服务器的调用(除非您正在进行急切加载,稍后我将介绍这一点)。鉴于您使用的是存储库模式,这是一个很大的禁忌。控制器不应导致直接调用数据库服务器

一旦C控制器从M模型接收到信息,它应该准备好进行投影(如有必要),并传递到V视图,而不是返回M模型

这就是为什么在我们的实现中(我们也使用存储库、ef4和工作单元),我们禁用延迟加载,并允许通过我们的服务层传递导航属性(一系列“Include”语句,通过枚举和扩展方法使其更甜)

然后,我们将根据控制器的要求加载这些属性。但重要的是,控制器必须明确请求它们。

这基本上告诉用户界面:“嘿,你只得到了关于这个实体的核心信息。如果你还想要什么,就去索取吧。”

我们还有一个服务层在控制器和存储库之间进行中介(存储库返回
IQueryable
)。这使得存储库不再需要处理复杂的关联。急切加载在服务层完成(以及分页之类的事情)

服务层的好处很简单——更松散的耦合。存储库只处理添加、删除、查找(返回IQueryable),工作单元处理DC的“更新”和提交更改,服务层处理实体到具体集合中的物化

这是一种很好的1-1堆栈式方法:

personService.FindSingle(1, "Addresses") // Controller calls service
 |
 --- Person FindSingle(int id, string[] includes) // Service Interface
      |
       --- return personRepository.Find().WithIncludes(includes).WithId(id); // Service calls Repository, adds on "filter" extension methods
           |
            --- IQueryable<T> Find() // Repository
                |
                 -- return db.Persons; // return's IQueryable of Persons (deferred exec)
personService.FindSingle(1,“地址”)//控制器调用服务
|
---Person FindSingle(int id,字符串[]包含)//服务接口
|
---return personRepository.Find().WithIncludes(includes).WithId(id);//服务调用存储库,添加“过滤器”扩展方法
|
---IQueryable Find()//存储库
|
--返回db.Persons;//个人申报表(延期执行)
我们还没有达到MVC层(我们正在做TDD),但是服务层可以是另一个可以将核心实体合并到ViewModels中的地方。再说一次,这将取决于控制器来决定它想要多少信息

同样,这都是关于松耦合的。您的控制器应该尽可能简单,不必担心复杂的关联

就存储库的数量而言,这是一个备受争议的话题。有些人喜欢每个实体有一个(如果你问我的话,那就太夸张了),有些人喜欢根据功能进行分组(在功能方面有意义,更容易使用),但是我们每个聚合根都有一个

在你的模型上,我只能猜测“Person”应该是我能看到的唯一聚合根

因此,当路径总是与特定的“人”关联时,使用另一个存储库来处理“路径”没有多大意义。Person存储库应该处理这个问题

再说一次-如果你对你的EDMX进行筛选,我们可以给你更多提示

根据问题的范围,这个答案可能会延伸得太远,但我想我会给出一个深入的答案,因为我们现在正在处理这个确切的场景


HTH.

谢谢,你的回答肯定澄清了我的想法。到目前为止的答案帮助我意识到,当我看我的代码时,我关心的是EF在幕后做了什么来实现这些对象图。我知道我可以启动SQL事件探查器