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

vie*_*uoc 7 c# entity-framework repository-pattern

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

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

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

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

目前,我已经有了是对BLL库有很多服务类将直接联系到DbContextDbSetEF和一些像这样的验证:

public IEnumerable<ProjectDTO> 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();
} 
Run Code Online (Sandbox Code Playgroud)

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

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

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

非常感谢.

Ami*_*shi 6

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

返回IEnumerable而不是IQueryable

同意。返回会IQueryable破坏存储库存在的基本目的。网上有很多文章解释了这如何带来比解决方案更多的问题。虽然,我学会了永不言败。请参考这个这个这个。或者直接搜索google

存储库应仅负责CRUD操作

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

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

如果您不使用DDD,则可以。否则,是实施决定。对于像EF或NHibernate这样的完整ORM,最好直接返回域模型,而不是按表实体实例返回。

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

但是,并非每个应用程序都实现DDD。许多小型应用程序设计实体将它们与数据库设计一对一映射。在这种情况下,存储库可能会返回实体(等效于您的表和字段)本身,并且映射成为调用代码的责任。或者,存储库可以映射必要的模型并返回模型本身。强烈建议不要这样做,因为存在上述问题。这样,您就必须放弃完整的ORM提供的某些功能。

这一切都取决于您的问题,您的设计目标,应用程序的大小以及所实现的其他设计模式等。这就是为什么它成为设计决策。

仅实现聚合根的存储库

同意是否与DDD一起使用。如果没有,则像每个表存储库一样有多个选择。同样,取决于用例。

关于复杂查询

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

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

这是RepositoryUoW的示例代码。