应该在Repository还是Service层中编写复杂的查询?

我计划将数据访问层迁移到使用存储库模式和工作单元。

我知道存储库将帮助我轻松地将持久性存储(数据库,集合等)和EF等技术更改为MongoDB。 所以我注意到了一些存储库实现的关键点,例如:

  1. 返回IEnumerable而不是IQueryable
  2. 存储库应仅对CRUD操作负责
  3. 存储库方法的返回类型应该是模型(实体)
  4. 仅实现聚合根的存储库

如果我在项目中的实现存储库中应用这些关键点,我完全忘记了如何处理与多个实体相关的复杂查询。

目前我已经拥有的是在BLL库上有很多服务类将直接联系到EF的DbContextDbSet以及一些这样的validation:

 public IEnumerable GetProjectWithDetails() { // Validation // Logging // Can be any logic need to before query data. Dbcontext.Projects.Where(p => // multiple of conditions go here follow business rules // conditions will need to check another entities (task, phase, employee...) such as: // 1. project have task status 'in-progress' .. etc // 2. project have employeeid 1,2,3.. // 3. project have stask start at some specific date. // 4.... ) .Select(p => new ProjectDTO { Label = p.Label, Phase = new PhaseDTO{ Label = p.Phase.Label, Tasks = p.Phase.Tasks.Select(t => new TaskDTO{ // some related properties }) } }).ToList(); } 

我目前正在使用数据传输对象(DTO)作为控制器上的模型和视图模型之间的中间类,并使用Mapper来映射属性。

如果我在上面的存储库上保留关键注释,我需要多次往返数据库获取数据,它将返回整个模型而不是有用的列。 但是,如果我将这些方法迁移到存储库,我将破坏存储库模式,因为它将包含业务逻辑和返回类型而不是模型。

所以问题是在这种情况下我应该怎么做? 请给我一些建议,让我走上正轨。

非常感谢。

使用Repository Pattern的好处之一是隐藏复杂查询,您应该将repository视为内存中对象的集合(martin fowler):

存储库在域和数据映射层之间进行调解,其作用类似于内存中的域对象集合。 客户端对象以声明方式构造查询规范,并将其提交到存储库以获得满意。 可以从简单的对象集合中添加和删除对象,也可以从存储库中删除对象,并且由存储库封装的映射代码将在后台执行适当的操作。 从概念上讲,存储库封装了持久存储在数据存储中的对象集以及对它们执行的操作,从而提供了更加面向对象的持久层视图。 存储库还支持在域和数据映射层之间实现清晰分离和单向依赖的目标https://martinfowler.com/eaaCatalog/repository.html

这取决于意见和用例,但我个人不同意您提到的一些要点。

返回IEnumerable而不是IQueryable

同意。 返回IQueryable破坏存储IQueryable基本目的。 网上有很多文章解释了如何产生比解决方案更多的问题。 虽然,我学会了永远不要说永远。 请参考this , this或this 。 或者只是搜索谷歌 。

存储库应仅对CRUD操作负责

同意。 使用简单的CRUD,它也可以执行复杂的读写操作。 我的经验告诉我们,在特殊情况下,如果要在RDBMS端实现它,就必须将业务逻辑的一部分放在存储库中。 这不对或错。 如果你知道自己在做什么,那就不应该有问题了。

存储库方法的返回类型应该是模型(实体)

如果您没有使用DDD,那么是的。 否则,这是实施决定。 使用像EF或NHibernate这样的完整ORM,最好直接返回Domain Model而不是每个表的Entity实例。

始终建议Repository应返回Domain Model。 这样,从RDBMS返回的数据与域模型的映射(反之亦然)成为存储库的责任。 这样就避免了将持久性问题泄露到存储库之外的必要性,从而使得应用程序的其余部分持久性无知。

但是,并非每个应用程序都实现DDD。 许多小型应用程序设计实体,这些实体通过数据库设计以1对1映射。 在这种情况下,存储库可以返回实体(相当于您的表和字段)本身,映射成为调用代码的责任。 或者,存储库可以映射必要的模型并返回模型本身。 由于上述问题,强烈建议不要这样做。 有了这个,你必须放弃ORM提供的一些function。

所有这些取决于你的问题是什么,你的设计目标是什么,应用程序的大小和其他设计模式的实现等等。这就是它成为设计决策的原因。

仅实现聚合根的存储库

同意是否与DDD一起使用。 如果没有,则可以使用多个选项,例如每个表存储库。 再次,取决于用例。

关于复杂查询

存储库不必仅实现简单的CRUD方法。 它也可能返回复杂的对象图。 它可能会进行复杂的查询。 也就是说,使用GetGetById等简单方法,它也可能使用像GetTopBrokenVehicles(vehicleType, top)这样的复杂方法。 如果为复杂查询编写单独的方法,那绝对没问题。

挑战在于,您如何接受必要的参数。 您可以接受内联参数或构建单独的简单输入参数类。

以下是Repository和UoW的示例代码。