IEnumerable 与 IQueryable 用于业务逻辑或 DAL 返回类型

Posted

技术标签:

【中文标题】IEnumerable 与 IQueryable 用于业务逻辑或 DAL 返回类型【英文标题】:IEnumerable vs IQueryable for Business Logic or DAL return Types 【发布时间】:2011-10-04 09:33:10 【问题描述】:

我知道以前有人问过这些问题,我将首先列出其中的几个(目前我读过的):

IEnumerable vs IQueryable List, IList, IEnumerable, IQueryable, ICollection, which is most flexible return type? Returning IEnumerable<T> vs. IQueryable<T> IEnumerable<T> as return type https://***.com/questions/2712253/ienumerable-and-iqueryable Views with business logic vs code WPF IEnumerable<T> vs IQueryable<T> as DataSource IEnumerable<T> VS IList<T> VS IQueryable<T> What interface should my service return? IQueryable, IList, IEnumerable? Should I return IEnumerable<T> or IQueryable<T> from my DAL?

如您所见,仅关于 SO 的主题就有一些很好的资源,但有一个问题/问题的一部分我仍然不确定是否已通读这些内容。

我主要关心的是 IEnumerable 与 IQueryable 的问题,更具体地说,是 DAL 与它的消费者之间的耦合

我发现对这两个界面提出了不同的意见,这些意见都很棒。但是,我担心 DAL 返回 IQueryable 的含义。据我了解,IQueryable 建议/暗示引擎盖下有一个 Linq 提供程序。这是第一个问题——如果 DAL 突然需要来自非 Linq 提供的源的数据怎么办?以下是可行的,但它更像是一种黑客攻击吗?

public static IQueryable<Product> GetAll()

    // this function used to use a L2S context or similar to return data 
    // from a database, however, now it uses a non linq provider

    // simulate the non linq provider...
    List<Product> results = new List<Product>  new Product() ;
    return results.AsQueryable();

所以我可以使用 AsQueryable() 扩展,尽管我不承认确切知道它的作用?我总是把 IQueryables 想象成底层的表达式树,我们可以根据需要附加它们,直到我们准备好执行我们的查询并获取结果。

我可以通过将函数的返回类型更改为 IEnumerable 来纠正这个问题。然后我可以从函数中返回 IQueryable,因为它继承了 IEnumerable,并且我可以保持延迟加载。我失去的是追加到查询表达式的能力:

var results = SomeClass.GetAll().Where(x => x.ProductTypeId == 5);

当返回 IQueryable 时,据我所知,这只会附加表达式。返回 IEnumerable 时,尽管保持延迟加载,但必须对表达式进行求值,以便将结果带入内存并进行枚举以过滤掉不正确的 ProductTypeId。

其他人如何解决这个问题?

在 DAL 中提供更多功能 - GetAllByProductType、GetAllByStartDate 等

提供一个接受谓词的重载?即

public static IEnumerable<Product> GetAll(Predicate<Product> predicate)


    List<Product> results = new List<Product>  new Product() ;
    return results.Where(x => predicate(x));

最后一部分(抱歉,我知道,这个问题真的很长!)。

在我检查的所有问题中,我发现 IEnumerable 是最受推荐的,但是延迟加载对于数据上下文可用的要求呢?据我了解,如果您的函数返回 IEnumerable,但您返回 IQueryable,则 IQueryable 依赖于基础数据上下文。因为这个阶段的结果实际上是一个表达式并且没有任何东西被带入内存,所以你不能保证 DAL 的/函数的使用者将要执行查询,也不保证什么时候。那么我是否必须以某种方式保留结果源自可用的上下文实例?这就是工作单元模式发挥作用的方式/原因吗?

为了清楚起见,问题摘要(是否搜索过“?”...):

    如果使用 IQueryable 作为返回类型,您的 UI/业务逻辑与 Linq 提供程序的耦合是否过于紧密? 如果您突然需要从非 Linq 提供的源返回数据,使用 AsQueryable() 扩展是个好主意吗? 任何人都有一个很好的链接来描述例如将标准列表转换为 AsQueryable 的工作原理,它实际上做了什么? 您如何处理由业务逻辑提供给 DAL 的额外过滤要求? 似乎 IEnumerable 和 IQueryable 的延迟加载取决于维护底层提供程序,我应该使用工作单元模式还是其他方式来处理这个问题?

提前非常感谢!

【问题讨论】:

我认为不应该围绕 IQueryable 的功能建立延迟加载机制。 @Urban 你能帮我解释一下吗?我想,如果我理解你,我同意。我们当前的项目正在返回 IQueryable,尽管它在业务层甚至表示层中提供了额外的功能和便利性,但感觉并不正确 我认为虽然让 IQueryable 将负载推迟到最后可能的时间是很好的,但这实际上是在您构建查询时进行的。正如您已经说过的,分发它需要您保持上下文的活力,您可能无法保证。我宁愿使用一个定义和执行我的查询并让它返回一个 IEnumerable 的类。不过我可能错了:) @Urban,我完全同意,但请记住,只需将函数更改为 IEnumerable,我仍然可以保持查询的延迟加载。我仍然面临必须维护我认为的上下文的问题? 您的 IEnumerable 将是一个完全加载的列表。您可能会在方法中使用列表并将其作为 IEnumerable 或 IList 传递出去 【参考方案1】:
    好吧,您并没有与任何特定的提供者严格耦合,但作为对它的重新表述:您不能轻易测试代码,因为每个提供者都有不同的支持功能(意味着: 对一个人有用的东西可能对另一个人不起作用——甚至像.Single()) 我不这么认为,如果您有任何关于不断变化的提供商的问题 - 见上文 它只是提供了一个装饰包装器,它在任何 lambdas 上使用.Compile(),并使用 LINQ-to-Objects 代替。注意 LINQ-to-Objects 比任何其他提供程序都更多 支持,所以这不会是一个问题 - 只是这意味着使用这种方法的任何“模拟”都不会真正测试你的实际代码完全而且基本上毫无意义(IMO) 是的,很棘手 - 见下文 是的,很棘手 - 见下文

就个人而言,我更喜欢这里定义明确的 API,它们采用已知参数并返回 已加载 List&lt;T&gt;IList&lt;T&gt;(或类似)结果;这为您提供了一个可测试/可模拟的 API,并且不会让您受到延迟执行的摆布(关闭连接地狱等)。这也意味着提供者之间的任何差异都在内部处理到数据层的实现。它也更适合调用场景,例如 Web 服务等。

简而言之;如果在IEnumerable&lt;T&gt;IQueryable&lt;T&gt; 之间进行选择,我两个都不选——而是选择使用IList&lt;T&gt;List&lt;T&gt;。如果我需要额外的过滤,那么:

    我将通过参数将其添加到现有 API,并在我的数据层中进行过滤 我会接受返回的超大数据,然后我需要在调用者处过滤掉这些数据

【讨论】:

您能否解释一下为什么您更喜欢 IList 或 List 而不是 IEnumerable?我一直认为返回可变列表不是一个好习惯,因此首选返回 IEnumerable。希望对此有不同的看法 @UrbanEsc 为什么返回一个列表?该列表对调用者是唯一的。如果他们想为自己的目的在其中放入其他东西,那就是他们的问题。它不会影响其他任何东西。 我明白你的意思,并不强烈反对。正如我所说,我一直认为最好的做法是选择最低的通用接口。所以最有可能调用者期望一个他们可以迭代并且不需要修改这个列表的东西的列表,对吧?我是否正确理解您,您使用列表而不是 IEnumerable 的原因是因为您或多或少更喜欢它,它不是技术问题。 @UrbanEsc 如果您没有在IList&lt;T&gt; 上出售,那么请考虑返回IEnumerable&lt;T&gt;,但实际情况一个列表。它仍然解决了大部分问题(包括延迟执行)。这就是我们从“dapper”做的事情,碰巧(除非你设置buffered = false,当我们返回原始IEnumerable&lt;T&gt;时) @Smudge202 list 的一个巨大优势 - 调用者知道他们可以廉价地查询长度,并根据需要进行多次迭代,并廉价地获取第三项,等

以上是关于IEnumerable 与 IQueryable 用于业务逻辑或 DAL 返回类型的主要内容,如果未能解决你的问题,请参考以下文章

返回 IEnumerable<T> 与 IQueryable<T>

2.3 IQueryable与 IEnumerable的区别

IQueryable 和 IEnumerable 有啥区别 [重复]

温故知新C#中 IEnumerable 与IQueryable

.NET Core - IEnumerable 与 IQueryable 产生不同的空间/地理结果

.Take() 的行为如何根据我在左侧使用的接口参考发生变化。 IQueryable 与 IEnumerable