存储库和查询对象模式.如何实现复杂的查询

Raf*_*ael 8 c# sql design-patterns entity-framework

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

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

存储库不应该有业务逻辑:所有业务逻辑必须继续查询对象

目前我有一个包装每个逻辑对象的类(几乎总是单个实体对象),它实现了多个"Get"方法,实现了大多数复杂的查询(连接,groupBy等等),这不是一个好的模式因为类似查询的样板代码往往会增长很多,并且它的公共方法依赖于将使用此类的上下文,因此,使这些类不能用于依赖于同一数据库的多个项目,这是我的主要目标对于这个项目重构.

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

或者,如果此业务逻辑不适合存储库,也不适用于此逻辑适合的查询对象

谢谢

Phi*_*ght 5

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

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

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

  • 您知道几十年来在查询数据库方面真正有效的方法吗?SQL。叫我厌倦,但这些存储库和工作单元模式似乎过度架构且缺乏好处 (3认同)
  • 我同意@Manachi,这是过度设计的,并且不能随着复杂的查询而扩展。我尝试过使用 LINQ 和其他抽象编写复杂的联接,但这是一场噩梦。在 SQL 中,在 5 个表之间编写复杂的联接需要几秒钟或几分钟。使用将所有内容分开然后重新组合到一个工作单元中的存储库可能需要 2 小时,然后维护和理解将是一场噩梦。不知何故,提出这些解决方案的人似乎通常使用小型数据库,并且不知道如何使用数据仓库。 (3认同)

Phi*_*yck 3

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

我认为良好的实施示例可以在这里找到:

https://genericunitofworkandrepositories.codeplex.com/

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

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

重点是,例如,当您想要放弃 EF 时,您很可能必须更改存储库的界面。

为了展示这个库的强大功能,一些代码片段可以证明这一点:

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

或使用投影:

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