【问题标题】:Should I return IEnumerable<T> or IQueryable<T> from my DAL?我应该从我的 DAL 返回 IEnumerable<T> 还是 IQueryable<T>?
【发布时间】:2011-03-03 15:04:04
【问题描述】:

我知道这可能是意见,但我正在寻找最佳做法。

据我了解,IQueryable&lt;T&gt; 实现了IEnumerable&lt;T&gt;,所以在我的 DAL 中,我目前有如下方法签名:

IEnumerable<Product> GetProducts();
IEnumerable<Product> GetProductsByCategory(int cateogoryId);
Product GetProduct(int productId);

我应该在这里使用IQueryable&lt;T&gt; 吗?

这两种方法的优缺点是什么?

请注意,我计划使用存储库模式,所以我将有一个这样的类:

public class ProductRepository {

    DBDataContext db = new DBDataContext(<!-- connection string -->);

    public IEnumerable<Product> GetProductsNew(int daysOld) {
        return db.GetProducts()
          .Where(p => p.AddedDateTime > DateTime.Now.AddDays(-daysOld ));
    }
}

我应该将我的IEnumerable&lt;T&gt; 更改为IQueryable&lt;T&gt; 吗?其中一个有什么优点/缺点?

【问题讨论】:

    标签: linq-to-sql datacontext data-access-layer iqueryable


    【解决方案1】:

    还要考虑一件事:您的分页/排序支持在哪里?如果您在存储库中提供分页支持,则返回 IEnumerable&lt;T&gt; 很好。如果您在存储库之外进行分页(例如在控制器或服务层中),那么您真的想使用IQueryable&lt;T&gt;,因为您不想在分页之前将整个数据集加载到内存中。

    【讨论】:

      【解决方案2】:

      您可以使用 IQueryable 并接受有人可以创建一个SELECT N+1 可能发生的场景。这是一个缺点,而且您最终可能会在存储库上方的层中获得特定于存储库实现的代码。这样做的好处是您允许在您的存储库之外表达诸如分页和排序之类的委派常见操作,从而减轻了此类担忧。如果您需要将数据与其他数据库表连接起来,它也会更加灵活,因为查询将保持为表达式,因此可以在将其解析为查询并命中数据库之前添加。

      另一种方法是锁定您的存储库,以便它通过调用ToList() 返回具体化列表。对于分页和排序的示例,您需要将 skip、take 和排序表达式作为参数传递给存储库的方法,并使用这些参数仅返回结果窗口。这意味着存储库负责分页和排序,以及所有数据的投影。

      这有点像判断调用,您是否赋予您的应用程序 linq 的功能,并且在存储库中具有较低的复杂性,或者您是否控制您的数据访问。对我来说,这取决于与每个实体关联的查询数量、实体组合以及我想在哪里管理这种复杂性。

      【讨论】:

      • 嗯...有些事情要考虑。
      【解决方案3】:

      这取决于你想要什么行为。

      • 返回 IList 告诉调用者他们已收到他们请求的所有数据
      • 返回一个 IEnumerable 告诉调用者他们需要迭代结果并且它可能会被延迟加载。
      • 返回 IQueryable 会告诉调用者结果由可以处理某些查询类别的 Linq 提供程序提供支持,从而使调用者承担形成高性能查询的负担。李>

      虽然后者为调用者提供了很大的灵活性(假设您的存储库完全支持它),但它是最难测试的,并且可以说是最不确定的。

      【讨论】:

      • 正确。我总是将 IList 和 IEnumerable 放在同一个存储桶中,但我更喜欢这个想法。
      【解决方案4】:

      HUUUUGGGE 差异。这个我看多了。

      您在 IQueryable 访问数据库之前构建它。 IQueryable 仅在调用一个急切的函数(例如 .ToList() )或您实际上尝试提取值时才访问数据库。 IQueryable = 懒惰。

      IEnumerable 将立即针对数据库执行您的 lambda。 IEnumerable = 渴望。

      至于与存储库模式一起使用,我相信它是渴望的。我通常会看到 ILists 被通过,但其他人需要为你解决这个问题。编辑 - 您通常会看到 IEnumerable 而不是 IQueryable,因为您不希望层通过您的存储库 A)确定何时发生数据库命中或 B)将任何逻辑添加到存储库外部的联接

      有一个非常棒的 LINQ 视频,我非常喜欢它——它不仅仅是 IEnumerable v IQueryable,但它确实有一些奇妙的洞察力。

      http://channel9.msdn.com/posts/matthijs/LINQ-Tips-Tricks-and-Optimizations-by-Scott-Allen/

      【讨论】:

      • 为了清楚起见,ProductRepository 位于我的应用程序中......而 DAL 是一个单独的项目,我有一个参考。所以我从我的应用程序调用存储库,存储库调用 DAL 项目中的 DBDataContext 类(其中包含所有通用方法),然后对数据库执行任何 LinqToSQL。
      • IEnumerable 不一定是急切的。 Enumerable.Select 返回一个惰性 IEnumerable。
      • 很棒的视频。谢谢!
      猜你喜欢
      • 1970-01-01
      • 2021-12-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-10-27
      相关资源
      最近更新 更多