业务逻辑或 DAL 返回类型的 IEnumerable 与 IQueryable
我知道以前有人问过这些问题,我将首先列出其中的一些问题(到目前为止我读过的问题):
- IEnumerable 与 IQueryable
- List、IList、IEnumerable、IQueryable、ICollection,哪个最灵活返回类型?
- 返回 IEnumerable
与 IQueryable - IEnumerable
相比作为返回类型 - https://stackoverflow.com/questions/2712253/ienumerable-and-iqueryable
- 具有业务逻辑的视图与代码
- WPF IEnumerable
;与 IQueryable 比较作为数据源 - IEnumerable
; VS IList VS IQueryable - 我的服务应该使用什么接口返回? IQueryable、IList、IEnumerable?
- 我应该返回 IEnumerable< ;T>或IQueryable
正如您所看到的,仅就该主题而言,就有一些很棒的资源,但是有一个问题/问题的
一部分我仍然不确定是否已阅读完所有这些内容。
我主要关心 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
; GetAll(谓词<产品>谓词) { 列表<产品>结果=新列表<产品>; { 新产品() }; 返回结果。Where(x => 谓词(x)); }
最后一部分(抱歉,我知道,问题很长!)。
我发现 IEnumerable 是我检查的所有问题中最受推荐的,但是延迟加载对可用数据上下文的要求又如何呢?据我了解,如果您的函数返回 IEnumerable,但返回 IQueryable,则 IQueryable 依赖于底层数据上下文。因为此阶段的结果实际上是一个表达式,并且没有任何内容被带入内存,所以您无法保证 DAL/函数的使用者将执行查询,也不能保证何时执行。那么我是否必须以某种方式保留结果所源自的上下文实例?这就是工作单元模式发挥作用的方式/原因吗?
为了清晰起见,问题摘要(是否搜索了“?”...):
- 如果使用 IQueryable 作为返回类型,您的 UI/业务逻辑与 Linq 提供程序的耦合是否太紧密?
- 如果您突然需要从非 Linq 提供的源返回数据,那么使用 AsQueryable() 扩展是个好主意吗?
- 任何人都有一个很好的链接,描述如何将标准列表转换为 AsQueryable 工作,它实际上做了什么?
- 您如何处理业务逻辑向 DAL 提供的额外过滤要求?
- 看来 IEnumerable 和 IQueryable 的延迟加载都需要维护底层提供程序,我应该使用工作单元模式还是其他方式来处理这个问题?
预先非常感谢!
I know these questions have been asked before, I'll start by listing a few of them (the ones I've read so far):
- 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://stackoverflow.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?
As you can see, there's some great resources on SO alone on the subject, but there is one question/section of the question I'm still not sure about having read these all through.
I'm primarily concerned with the IEnumerable vs IQueryable question, and more specifically the coupling between the DAL and it's consumers.
I've found varying opinions suggested regarding the two interfaces, which have been great. However, I'm concerned with the implications of a DAL returning IQueryable. As I understand it IQueryable suggest/implies that there is a Linq Provider under the hood. That's concern number one - what if the DAL suddenly requires data from a non-Linq provided source? The following would work, but is it more of a hack?
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();
}
So I can use the AsQueryable() extension though I don't admit to knowing exactly what this does? I always imagine IQueryables as being the underlying expression trees which we can append as necessary until we're ready to perform our query and fetch the results.
I could rectify this by changing the return type of the function to IEnumerable. I can then return IQueryable from the function because it inherits IEnumerable, and I get to keep the deferred loading. What I lose is the ability to append to the query expression:
var results = SomeClass.GetAll().Where(x => x.ProductTypeId == 5);
When returning IQueryable, as I understand it, this would simply append the expression. When returning IEnumerable, despite maintaining the deferred loading, the expression has to be evaluated so the results will be brought to memory and enumerated through to filter out incorrect ProductTypeIds.
How do other people get round this?
- Provide more functions in the DAL - GetAllByProductType, GetAllByStartDate,... etc
Provide an overload that accepts predicates? i.e.
public static IEnumerable<Product> GetAll(Predicate<Product> predicate) { List<Product> results = new List<Product> { new Product() }; return results.Where(x => predicate(x)); }
One last part (Sorry, I know, really long question!).
I found IEnumerable to be the most recommended across all the questions I checked, but what about the deferred loadings' requirement for a datacontext to be available? As I understand it, if your function returns IEnumerable, but you return IQueryable, the IQueryable is reliant on an underlying datacontext. Because the result at this stage is actually an expression and nothing has been brought to memory, you cannot guarantee that the DAL's/function's consumer is going to perform the query, nor when. So do I have to keep the instance of the context that the results were derived from available somehow? Is this how/why the Unit of Work pattern comes into play?
Summary of the questions for clarity ( did a search for "?"...):
- If using IQueryable as a return type, are you too tightly coupling your UI/Business Logic to Linq Providers?
- Is using the AsQueryable() extension a good idea if you suddenly need to return data from a non-Linq Provided source?
- Anyone have a good link describing how for example converting a standard list to AsQueryable works, what it actually does?
- How do you handle additional filtering requirements supplied by business logic to your DAL?
- It seems the deferred loading of both IEnumerable and IQueryable are subject to maintaining the underlying provider, should I be using a Unit of Work pattern or something else to handle this?
Thanks a lot in advance!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
.Single()
这样的东西).Compile()
,并使用 LINQ-to-Objects 代替。注意 LINQ-to-Objects 比任何其他提供程序都拥有更多支持,因此这不会成为问题 - 只不过这意味着使用此方法的任何“模拟”都不会真正测试您的实际代码根本而且基本上毫无意义(IMO)我个人而言,我更喜欢这里定义良好的 API,它接受已知参数并返回 已加载< /em>结果的
List
或IList
(或类似);这为您提供了一个可测试/可模拟的 API,并且不会让您受到延迟执行(封闭连接地狱等)的支配。这也意味着提供者之间的任何差异都将在数据层的实现内部进行处理。它还更适合 Web 服务等调用场景。如果在
IEnumerable
和IQueryable
之间进行选择,我两者都不选择 - 选择使用IList
或列表
。如果我需要额外的过滤,那么:.Single()
).Compile()
on any lambdas, and uses LINQ-to-Objects instead. Note LINQ-to-Objects has more support than any other provider, so this won't be an issue - except that it means that any "mocks" using this approach don't really test your actual code at all and are largely pointless (IMO)Personally, I'd prefer well defined APIs here that take known parameters and return a loaded
List<T>
orIList<T>
(or similar) of results; this gives you a testable/mockable API, and doesn't leave you at the mercy of deferred execution (closed connection hell, etc). It also means that any differences between providers is handled internally to the implementation of your data layer. It also makes a much closer fit for calling scenarios such as web-services, etc.In short; given a choice between
IEnumerable<T>
andIQueryable<T>
, I choose neither - opting instead to useIList<T>
orList<T>
. If I need additional filtering, then either: