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

时间:2018-05-13 04:16:50

标签: c# entity-framework repository-pattern

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

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

  1. 返回IEnumerable而不是IQueryable
  2. 存储库应仅负责CRUD操作
  3. 存储库方法的返回类型应为model(entity)
  4. 仅为聚合根
  5. 实施存储库

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

    目前我已经拥有的是有很多服务类的BLL库将直接联系到EF的DbContextDbSet以及一些类似的验证:

    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();
    } 
    

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

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

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

    非常感谢。

2 个答案:

答案 0 :(得分:4)

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

  

返回IEnumerable而不是IQueryable

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

  

存储库应仅负责CRUD操作

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

  

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

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

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

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

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

  

仅实现聚合根

的存储库

同意DDD是否合适。如果没有,则可以使用多个选项,例如每个表存储库。同样,取决于用例。

关于复杂查询

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

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

以下是RepositoryUoW的示例代码。

答案 1 :(得分:3)

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

  

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