代码之家  ›  专栏  ›  技术社区  ›  Rafael

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

  •  10
  • Rafael  · 技术社区  · 10 年前

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

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

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

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

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

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

    2 回复  |  直到 4 年前
        1
  •  5
  •   Phil Wright    10 年前

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

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

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

        2
  •  3
  •   Philip Stuyck    10 年前

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

    我认为良好实施的示例如下:

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

    或使用投影:

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