Entity Framework 存储库模式为什么不能 return Iqueryable?

Entity Framework Repository Pattern why not return Iqueryable?

关于如何使用通用 classes 实现存储库模式和工作单元模式,有几篇不错的博客。

Implementing a Data Access Layer with Entity Framework 6.1

Implementing the Repository and Unit of Work Patterns

想法是,定义一个通用接口 IRepository 和一个隐藏数据实际访问方式的 class 存储库。可以使用 Entity Framework DbContext 访问它,或者存储库可能是用于单元测试的内存集合。

public interface public interface IRepository<T> where T : class
{
    T GetById(int Id);
    void DeleteById(int Id);

    void Add(T entity);
    void Update(T entity);

    etc.
}

我经常看到添加了几个类似于 Queryable and/or Enumerable 函数的 Query 函数。

例如在Implementing a data access layer中我看到:

/// Returns an IEnumerable based on the query, order clause and the properties included
/// <param name="query">Link query for filtering.</param>
/// <param name="orderBy">Link query for sorting.</param>
/// <param name="includeProperties">Navigation properties seperated by comma for eager loading.</param>
/// <returns>IEnumerable containing the resulting entity set.</returns>
IEnumerable<T> GetByQuery(Expression<Func<T, bool>> query = null, Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null, string includeProperties = "");

/// <summary>
/// Returns the first matching entity based on the query.
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
T GetFirst(Expression<Func<T, bool>> predicate);

如果接口有函数 IQueryable GetQuery(),那么我就不必创建像 GetFirst() 和 GetByQuery() 这样的函数。

Question: Why is this not recommended? Can people change the data in an undesirable way?

不推荐这样做,因为它会使存储库模式无效。 此模式的目的是通过抽象将您的 DAL 实现与其他项目分开。

本质上 returning IQueryable 会 return TSQL 语句而不是结果,这意味着任何引用您的 DAL 的项目都需要额外引用 EF 才能执行询问。这 'data leak' 会使您的项目更加紧密,从而与关注点分离原则相矛盾。

您可以在此处阅读有关存储库模式及其优点的更多信息: http://www.codeproject.com/Articles/526874/Repositorypluspattern-cplusdoneplusright

我们使用存储库模式的原因之一是封装胖查询。这些查询使得阅读、理解和测试 ASP.NET MVC 控制器中的操作变得困难。此外,随着应用程序的增长,您在多个地方重复胖查询的机会也会增加。使用存储库模式,我们将这些查询封装在存储库 类 中。结果是更苗条、更清洁、更易于维护和更易于测试的操作。考虑这个例子:

var orders = context.Orders
    .Include(o => o.Details)
        .ThenInclude(d => d.Product)
    .Where(o => o.CustomerId == 1234);

这里我们直接使用没有存储库模式的 DbContext。当您的存储库方法 return IQueryable 时,其他人将获取该 IQueryable 并在其之上编写查询。结果如下:

var orders = repository.GetOrders()
    .Include(o => o.Details)
        .ThenInclude(d => d.Product)
    .Where(o => o.CustomerId == 1234);

你能看出这两个代码片段之间的区别吗?唯一的区别是在第一行。在第一个示例中,我们使用 context.Orders,在第二个示例中我们使用 repository.GetOrders()。那么,这个存储库解决了什么问题?没有!

您的存储库应该 return 域对象。因此,GetOrders() 方法应该 return 一个 IEnumerable。有了这个,第二个例子可以重写为:

var orders = repository.GetOrders(1234);

看出区别了吗? 取自 Hamedani 先生 blog