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-
好吧,您并没有与任何特定的提供者严格耦合,但作为对它的重新表述:您不能轻易测试代码,因为每个提供者都有不同的支持功能(意味着: 对一个人有用的东西可能对另一个人不起作用——甚至像
.Single()
)
我不这么认为,如果您有任何关于不断变化的提供商的问题 - 见上文
它只是提供了一个装饰包装器,它在任何 lambdas 上使用.Compile()
,并使用 LINQ-to-Objects 代替。注意 LINQ-to-Objects 比任何其他提供程序都更多 支持,所以这不会是一个问题 - 只是这意味着使用这种方法的任何“模拟”都不会真正测试你的实际代码完全而且基本上毫无意义(IMO)
是的,很棘手 - 见下文
是的,很棘手 - 见下文
就个人而言,我更喜欢这里定义明确的 API,它们采用已知参数并返回 已加载 List<T>
或 IList<T>
(或类似)结果;这为您提供了一个可测试/可模拟的 API,并且不会让您受到延迟执行的摆布(关闭连接地狱等)。这也意味着提供者之间的任何差异都在内部处理到数据层的实现。它也更适合调用场景,例如 Web 服务等。
简而言之;如果在IEnumerable<T>
和IQueryable<T>
之间进行选择,我两个都不选——而是选择使用IList<T>
或List<T>
。如果我需要额外的过滤,那么:
-
我将通过参数将其添加到现有 API,并在我的数据层中进行过滤
我会接受返回的超大数据,然后我需要在调用者处过滤掉这些数据
【讨论】:
您能否解释一下为什么您更喜欢 IListIList<T>
上出售,那么请考虑返回IEnumerable<T>
,但实际情况是一个列表。它仍然解决了大部分问题(包括延迟执行)。这就是我们从“dapper”做的事情,碰巧(除非你设置buffered = false
,当我们返回原始IEnumerable<T>
时)
@Smudge202 list 的一个巨大优势 - 调用者知道他们可以廉价地查询长度,并根据需要进行多次迭代,并廉价地获取第三项,等以上是关于IEnumerable 与 IQueryable 用于业务逻辑或 DAL 返回类型的主要内容,如果未能解决你的问题,请参考以下文章
返回 IEnumerable<T> 与 IQueryable<T>
2.3 IQueryable与 IEnumerable的区别
IQueryable 和 IEnumerable 有啥区别 [重复]
温故知新C#中 IEnumerable 与IQueryable