存储库模式 - 如何正确处理 JOIN 和复杂查询?
Posted
技术标签:
【中文标题】存储库模式 - 如何正确处理 JOIN 和复杂查询?【英文标题】:Repository pattern - how to correctly handle JOINs and complex queries? 【发布时间】:2014-02-11 00:18:58 【问题描述】:我对存储库模式有疑问 - 如何在多个存储库之间执行 JOIN 操作。在这个项目中,我们使用 MVC、EF、DDD。我知道这种问题在这里出现过好几次,我会在后面提到这些问题。
在通用存储库模型 (IRepository) 和特定存储库模型之间,我选择了特定选项,因为我认为 ORM(在我们的例子中为 EF)本身就是通用存储库模式,因此添加另一个通用存储库并没有意义我们宁愿根据域需求定制存储库。
问题是我有几个(约 10 个)表,每个表有很多行(数百万),我需要执行 JOIN,因此使用 IList 或 IEnumerable 是不可行的选择。
我的理解(和我的观点)是 IQueryable 不应该离开存储库(“在 DAL 中发生的事情应该留在 DAL 中。”)。公开 IQueryable 并在服务中的 LINQ 中使用它会更简单,但它强烈违反关注点分离并破坏存储库的作用——在这种情况下,服务将做与存储库相同的事情。举几个例子,这些文章支持这种观点(或者更确切地说是信念):
To return IQueryable<T> or not return IQueryable<T>
Should I return IEnumerable<T> or IQueryable<T> from my DAL?
http://www.shawnmclean.com/blog/2011/06/iqueryable-vs-ienumerable-in-the-repository-pattern/
http://blog.ploeh.dk/2012/03/26/IQueryableTisTightCoupling/
也有类似的问题和解决方案,例如How to join Multiple tables using Repository Pattern & Entity Framework? 建议使用 .Include(),但这不是用于重载表和跨多个表连接的选项 - 对于每个 JOIN,我们使用子选择来限制实际连接的内容。
这个问题(答案和 cmets) - How can I query cross tables with Repository Pattern? - 基本上建议基于任务的区分:为使用 JOINS 的查询创建一个存储库,并为每个实体的操作创建“常规”存储库。
我知道我们有以下选择:
-
在服务中公开 IQueryable 并执行 JOIN 复杂查询;我真心觉得这是反模式,我不喜欢这样。
不要对这 10 个表使用 Repository 并在服务中执行查询;有些文章建议使用 EF 就足够了(例如Is it okay to bypass the repository pattern for complex queries?),我不同意。
使用基于任务的区分,不要限制存储库 1:1 repo:entity(我赞成这个选项)
完全不同的东西?
那么 - 你有什么建议?一次又一次,谢谢你。
【问题讨论】:
处理存储库的重点是忽略数据库。数据库引擎、表、sql、连接等是永远不会通过持久性边界的存储库的实现细节。所以应用程序永远不会知道 EF 或加入,也永远不会直接访问数据库。应用程序只知道存储库,它告诉它做什么 去做/得到而不是如何 去做 嗨,罗伯特,我正在为同样的问题苦苦挣扎。到目前为止,我发现的结果与您对选项的结论相同。你最终会采用哪种方法,为什么? 嗨,对于复杂的查询,我们只使用一些特定的存储库进行查询,在我们的例子中,这些存储库中的查询通常由返回多个记录集的存储过程执行(当连接不够时)并且存储库将其转换为特定的(通常是复杂的)域对象(它本身不是实体,但我相信它仍然不是滥用 DDD)。 对于非复杂查询呢?例如 GetOrderLineByOrderId 或 GetOrderLineByCustomerId 对于标准查询,我们使用围绕聚合根和 EF6 的普通存储库,在提到的情况下,这将类似于处理订单域的 OrderRepository。 【参考方案1】:-
意味着将持久性泄漏到应用程序中 - 反模式、意大利面条代码。
它与 (1) 有何不同?还是同样的问题。
再靠近一点...
使用查询对象模式。将您的复杂查询封装在与存储库一起驻留的基于任务的对象中。它可以返回针对视图而不是域对象优化的 DTO。
严重依赖 QO 会导致您使用称为 CQRS 的架构 - 命令-查询职责分离。
还有一件事。 entity:repo 没有 1:1 匹配。只有聚合应该有一个存储库,而不是每个实体。
【讨论】:
以上是关于存储库模式 - 如何正确处理 JOIN 和复杂查询?的主要内容,如果未能解决你的问题,请参考以下文章