带有 ThenIncludes 的通用存储库模式

Posted

技术标签:

【中文标题】带有 ThenIncludes 的通用存储库模式【英文标题】:Generic Repository pattern with ThenIncludes 【发布时间】:2021-11-15 08:52:22 【问题描述】:

我的解决方案有几个层,其中之一是我实现存储库模式的DataAccess 层。

我的主要关注点是EntityFramework 仅在DataAccess 层中被引用

我需要在查询中包含关系,因此我调整了查询​​方法以接收 Includes 作为输入。

/// <inheritdoc/>
public IQueryable<T> AsQueryable(params Expression<Func<T, object>>[] includes)

    var query = _dbSet.AsQueryable();

    if (includes != null)
    
        query = includes.Aggregate(query,
                  (current, include) => current.Include(include));
    

    return query;

使用示例:

// Books :: ICollection<Book>
var query = _repository.AsQueryable(e => e.Books);

使用上面的示例,例如,我如何在查询中包含Book -&gt; Author 关系? Books 属性是一个集合,因此我无法引用 Author 属性。

示例:.AsQueryable(e =&gt; e.Books, e =&gt; Books.Author).AsQueryable(e =&gt; e.Books.ChildInclude(b =&gt; b.Author))

【问题讨论】:

也许你应该先解释一下为什么现在不能。 @GertArnold 仅仅因为Books 是一个集合,我无法引用Author 属性 请将其添加到您的问题中。 看起来通过暴露(泄漏)可查询的实体框架,您正试图在现有框架之上构建自己的“ORM”:) 一个选项 - 将 DbContext 视为通用存储库,因为您已经尝试将通用存储库用作 DbContext ;)。第二种选择 - 不公开可查询或查询表达式,而是接受一些自定义查询类型(命令或请求)并在存储库内部根据该类型生成查询并返回“物化”集合作为结果(请参阅规范模式) 【参考方案1】:

我的主要关注点是 EntityFramework 仅在 DataAccess 层中被引用。

我可以推荐的唯一安全方法是在必须与 EF 知识隔离的代码和可以了解 EF 的代码之间定义一个边界。这意味着只有物化的 DTO 或非实体模型才能跨越此边界。这通常适用于您希望多个消费者以隔离且相同的方式访问数据的情况。 (即网站 + API)即便如此,这也会在灵活性和性能方面进行权衡。该边界通常不是存储库,而是可以感知 EF、管理 DbContext 范围的服务(通过工作单元,或在调用 SaveChanges() 时进行管理等)访问利用 @987654322 的存储库@,然后将结果投射到消费者的具体化 List&lt;TDTO&gt;TDO 实例中。

在跟踪它们的 DBContext 范围之外传递实体会导致系统内的各种复杂性和问题。这意味着设计一个返回 DTO 或 IEnumerable&lt;TDTO&gt; 而不是 IQueryable&lt;TEntity&gt; 甚至 IEnumerable&lt;TEntity&gt; 的分离层。任何接受实体的代码都应始终具有完整的实体图或可完成的实体图。 (可延迟加载)当接受实体图的函数可能会或可能不会获得引用或填充所有属性并猜测某些获取或构造的实体是否“足够完整”以传递给现有方法时,Bug 条件已经成熟。

如果这种抽象只是“非常需要”来满足个人偏好,或者对可能需要您用其他机制替换 EF 的未来需求的不确定性担忧,我的建议将是“不要”。通过实现模式以从 EF 抽象代码,您将绝对的性能限制和通常显着的复杂性强加到您的系统中,而不会立即受益。利用IQueryable 允许您的代码针对数据层构建高效和高性能的查询。将对象具体化以传回消费层将意味着大量非常相似的代码,或者非常复杂的代码来处理过滤、预加载、投影、排序和分页,并且通常返回的数据比更直接的方法返回的数据多得多.

可以使用魔术字符串或表达式来促进急切加载。表达式也可以促进过滤和排序。但是,开发复杂的基于表达式的抽象的一个重要警告是,虽然这可能会将调用代码与 EF 引用隔离开来,但它仍然不会将该代码与 EF 特定的规则隔离开来。例如,这些表达式需要了解并遵守 EF 规则,例如不调用函数或任何最终无法转换为 SQL 的内容。

像这样的抽象通常会导致性能问题,从而增强了以后由于性能问题而用另一种 ORM 或数据访问方法替换 EF 的愿望。它变成了一个自我实现的预言。

【讨论】:

以上是关于带有 ThenIncludes 的通用存储库模式的主要内容,如果未能解决你的问题,请参考以下文章

javascript 带有Typescript和Node.js的通用存储库

Mongodb,Typegoose - 通用存储库模式

EF 包括其他实体(通用存储库模式)

csharp 存储库模式的通用实现

Asp.net Core - 通用存储库模式 - 使用 TrimStart 搜索

实体框架使用 Codefirst、通用存储库、工作单元模式保存多对多关系