【问题标题】:Repository and query objects pattern. How to implement complex queries存储库和查询对象模式。如何实现复杂的查询
【发布时间】:2015-03-17 00:02:28
【问题描述】:

我已经阅读了很多关于存储库模式的帖子,但有一些实际问题似乎没有解决或解释。这就是我对这两种模式的理解:

存储库和查询模式是互补的: 查询对象代表业务逻辑(WHERE 子句),存储库模式有一个 Get(IPredicate) 方法,该方法接受一个查询对象并返回一个 SELECT WHERE 结果

存储库不应有业务逻辑:所有业务逻辑都必须在查询对象上进行

目前我有一个包装每个逻辑对象(几乎总是一个实体对象)的类,它实现了多个“Get”方法,这些方法实现了最复杂的查询(joins、groupBy 等),这不是一个很好的模式,因为类往往会因为类似查询的样板代码而增长很多,并且它的公共方法依赖于该类将被使用的上下文,因此,使这些类无法用于依赖于同一数据库的多个项目,即我对这个项目进行重构的主要目标。

如何使用这两种模式实现比单个 SELECT WHERE 更复杂的查询,而不会将业务逻辑泄漏到存储库中?

或者,如果此业务逻辑不适合存储库或查询对象 这个逻辑在哪里合适?

【问题讨论】:

  • 谢谢,这不是我的母语,我会更正问题

标签: c# sql entity-framework design-patterns


【解决方案1】:

Repository 模式适用于标准 CRUD 应用程序。您需要针对数据库中的单个表实现经典的创建、读取、更新和删除操作集。在这种情况下,您为每个表创建一个存储库并允许读取操作具有额外的值,以便可以应用过滤。

在下一个级别,您将拥有 工作单元 模式。这些用于跨越多个存储库并执行业务操作。例如,您将从多个存储库中读取值,执行计算,然后将更改写回多个存储库。所有这些都将发生在事务中,因此您在数据库中始终拥有一致的状态。

问题是当您有跨越多个表的复杂查询时。在这种情况下,您可以将查询放入查询 from 子句中的第一个表的存储库中。然后,您需要为该存储库方法提供参数,以便可以根据需要对其进行参数化。

【讨论】:

  • 那么工作单元模式用于跨多个需要执行业务逻辑的类的方法中?
  • 您知道几十年来在查询数据库方面什么做得很好吗? SQL。说我厌倦了,但是这些存储库和工作单元模式似乎过度架构并且缺乏好处
  • 存储库将包含实际的 SQL。工作单元包含围绕来自存储库的多个 SQL 命令的事务逻辑。我同意,如果您永远不需要担心迁移到另一个数据库后端,那么您通常不需要这些模式。
  • 直到答案的最后一段,您都做对了。现代 ORM(EF 和 NHibernate)跨多个表没有问题。 UoW 的挑战不是在不同的表中写入多个条目,ORM 已经做到了。目标是在其中一个步骤失败时提供“自动”(由您实现)回滚。
  • 我同意@Manachi,这是过度设计的,不能通过复杂的查询进行扩展。我曾尝试使用 LINQ 和其他抽象来编写复杂的连接,这是一场噩梦。在 SQL 中,编写 5 个表之间的复杂连接需要几秒钟或几分钟的时间。使用将所有内容拆分然后在一个工作单元中重新组合在一起的存储库可能需要 2 个小时,然后维护和理解将是一场噩梦。不知何故,提出这些解决方案的人似乎通常使用小型数据库,并且不知道如何使用数据仓库。
【解决方案2】:

互联网上有很多存储库模式和工作单元的实现。其中一些非常简单,开发人员基本上是自己手动为每个表实现自己的,有些是通用的但不高级,还有一些非常酷,通用并且仍然为您提供了做一个像样的位置,投影等的能力。

可以在此处找到我认为良好实现的示例:

https://genericunitofworkandrepositories.codeplex.com/

它是针对MVC的,由界面显示。我专注于 WPF 应用程序,所以我需要对其进行一些调整。但是这个工作单元实现的思路还是不错的。

这个实现有缺点。由于它依赖于一些高级 LINQ 和 EF 功能,因此可能会认为您的底层访问层正在感染存储库层和使用存储库的层。

重点是,例如,当您想离开 EF 时,您可能必须更改存储库的接口。

为了展示这个库的力量,一些代码 sn-ps 来证明这一点:

_fotoRepository = unitOfWork.RepositoryAsync<Foto>();
var fotos = await _fotoRepository
            .Query(r => r.BestelBonId == bestelBonId || werkstukids.Contains(r.WerkstukMetBewerkingenId.Value))
            .SelectAsync()
            .ConfigureAwait(false);

或使用投影:

IRepository<Relatie> relatieRepository = unitOfWork.RepositoryAsync<Relatie>();
        var relatiesOverviewsEnumerable = relatieRepository
            .Query()
            .NoTracking()
            .OrderBy(q => q.OrderBy(d => d.RelatieId))
            .Select(b => new RelatieOverview
            {
                RelatieId = b.RelatieId,
                Naam = b.Naam,
                BTW = b.BTW,
                HoofdAdres = b.Adressen.FirstOrDefault(a => a.AdresTypeId == HoofdadresType)
            });
        _relatieOverviews = new ObservableCollection<RelatieOverview>(relatiesOverviewsEnumerable);

【讨论】:

  • unitOfWork 对象如何与此处显示的 IUnitOfWork 接口相关:link?据我了解,工作单元用于更改数据,因此 MakeDirty、MakeNew 和 MakeDeleted 方法,而 Repository 模式用于从数据库中获取 IQueryables 数据
猜你喜欢
  • 2014-02-11
  • 2011-09-04
  • 1970-01-01
  • 1970-01-01
  • 2011-11-09
  • 2012-08-10
  • 2012-08-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多