【问题标题】:Multiple joins in linq using repository pattern generics使用存储库模式泛型在 linq 中进行多个连接
【发布时间】:2018-09-10 09:21:42
【问题描述】:

我已经实现了一个存储库模式,其中包含对数据库执行 CRUD 操作的通用方法列表。如下图所示。

public Repository(DbContext context)
    {
        _entities = context;
        _dbset = context.Set<T>();
    }

    public IEnmerable<T> GetAll()
    {

        return _dbset.AsEnumerable<T>();
    }

接下来,我有一个实现上述存储库类的 service 层。

卡车服务

在这个类中,为了使用存储库通用方法,我有以下代码:

private IRepository<Workers> _repositoryWorkers;
private IRepository<Jobs> _repositoryJobs;
private IRepository<Depts> _repositoryDepts;


public WorkerService(IRepository<Workers> repositoryWorkers, IRepository<Jobs> repositoryJobs, IRepository<Depts> repositoryDepts) 
{
  _repositoryWorkers = repositoryWorkers;
  _repositoryJobs = repositoryJobs;
  _repositoryDepts = repositoryDepts
}

// a query that makes multiple joins to retrieve data.
from h in _repositoryWorkers.GetAll()
join p in _repositoryJobs.GetAll() on h.Id equals p.Id
join q in _repositoryDepts.GetAll() on h.Id equals q.Id

所以,在上面的TruckService类中,我实现了IRepository接口来继承泛型方法。在本课程中,我正在执行一个查询,该查询执行多个连接以从数据库中获取数据。我正确获取了所有数据,但我在某处读到这种方法是:

混合和匹配基于存储库的查询和 LINQ 查询。相当 与真正的联接相比,您正在获取每个表的所有行 然后在内存中加入它们

那么,在这种情况下,有人可以告诉我一个更好/更有效的方法来进行多重连接吗?

*请注意,我是 EF 新手,这是我第一个使用存储库模式的项目。

【问题讨论】:

  • 存储库上的特殊方法。例如GetJobsByWorkerId
  • _repositoryJobs.GetAll() on h.Id equals p.Id 您正在有效地下载整个表,只是为了进行内存查询。专业提示:当您刚刚学习实体框架时,不要遵循存储库模式。存储库模式可能适用于某些用例,但不适用于 EF。作为一个额外的评论:每当你在使用实体框架时看到IEnumerable&lt;T&gt;,你就做错了
  • 我的建议:删除任何与存储库相关的内容并直接使用 EF,简单明了
  • 短期内 - 不要使用存储库反模式。使用 DbContext 和 EF 模型的全部功能,尤其是导航属性、预加载、投影查询等。
  • 创建一个我的 DbContext 实例并使用该对象通过连接查询我的表 几乎。但现在不使用连接,而是使用导航属性。存储库是一种反模式,因为 1) 通常它只是一个围绕 DbSet 的薄(= 无用)包装器,2) 它阻碍 LINQ 查询组合,以及 3) 它引入了模棱两可的职责(“哪个存储库负责我的嵌套实体?”)。

标签: c# entity-framework repository-pattern


【解决方案1】:

返回数据库中所有实体的 IEnumerable 集是一个完美的方法,可以让你自食其果。避免这样做,因为它会将所有数据加载到内存中。

你可以使用IQueryable,不像IEnumerable,它在数据库中执行查询,你可以给它添加过滤,在你具体化之前它不会真正执行查询和加载结果(比如调用ToList(), FirstOrDefault()和其他几个)。

外键、导航属性都是您可以用来在实体之间轻松导航的工具,并为您自己节省了大量的显式连接。您可以对您希望经常使用的导航属性使用诸如预先加载之类的东西。 EF 的主要目标之一是帮助您编写更少的代码。它为您节省了大量时间,也减少了您可能出错的地方(更少的代码,更少的错误,但在使用时仍然要小心)。

您是否想使用存储库模式,这是您的选择。有些人认为这是对 EF 上下文和集合已经提供的抽象的不必要的抽象。有些人将其视为将业务逻辑与数据库访问分开的必要条件。但是,在使用它之前阅读很多关于它的内容很重要,尤其是如果您是 EF 新手。这是他们文档中的Microsoft give as an example

【讨论】:

  • 有趣的是,他们从来没有展示过如何使用存储库模式编写 joins,哦等等……你不能不暴露 IQueryable。 MSDN 示例应该与编写它们的人一起埋在地下,他们几乎总是使用不良做法。
【解决方案2】:

我建议您从通用存储库中返回IQueryable&lt;T&gt;,以便稍后在特定继承存储库中修改查询。

但是,为了在通用存储库中拥有 Include,您可以将要包含的属性传递给 GetAll 方法 params Func&lt;T, object&gt;[],然后将其聚合到查询中。 请参阅this gist. 上的示例

但是,您将无法使用ThenInclude。考虑返回IQueryable,以便稍后修改查询。

【讨论】:

  • 返回 IQueryable 完全违背了拥有存储库的目的。拥有存储库的目的是抽象数据库层。如果IQueryable 开始泄漏到其他层,则您无法交换数据库实现,因为IQueryable 特定于实体框架。
猜你喜欢
  • 2017-09-19
  • 2015-10-06
  • 2023-03-21
  • 1970-01-01
  • 2013-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-09-27
相关资源
最近更新 更多