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

发布于 2024-11-19 23:12:35 字数 3679 浏览 1 评论 0原文

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

一部分我仍然不确定是否已阅读完所有这些内容。

我主要关心 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/函数的使用者将执行查询,也不能保证何时执行。那么我是否必须以某种方式保留结果所源自的上下文实例?这就是工作单元模式发挥作用的方式/原因吗?

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

  1. 如果使用 IQueryable 作为返回类型,您的 UI/业务逻辑与 Linq 提供程序的耦合是否太紧密?
  2. 如果您突然需要从非 Linq 提供的源返回数据,那么使用 AsQueryable() 扩展是个好主意吗?
  3. 任何人都有一个很好的链接,描述如何将标准列表转换为 AsQueryable 工作,它实际上做了什么?
  4. 您如何处理业务逻辑向 DAL 提供的额外过滤要求?
  5. 看来 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):

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 "?"...):

  1. If using IQueryable as a return type, are you too tightly coupling your UI/Business Logic to Linq Providers?
  2. Is using the AsQueryable() extension a good idea if you suddenly need to return data from a non-Linq Provided source?
  3. Anyone have a good link describing how for example converting a standard list to AsQueryable works, what it actually does?
  4. How do you handle additional filtering requirements supplied by business logic to your DAL?
  5. 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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

北渚 2024-11-26 23:12:35
  1. 好吧,您并没有严格耦合到任何特定的提供程序,但换句话来说:您无法轻松地测试代码,因为每个提供程序都有不同的支持功能(意思是:什么有效因为一个可能不适用于另一个 - 即使是像 .Single() 这样的东西)
  2. 我不这么认为,如果您对不断变化的提供商有任何疑问 -见上面
  3. 它只是提供了一个装饰包装器,使用任何 lambda 上的 .Compile() ,并使用 LINQ-to-Objects 代替。注意 LINQ-to-Objects 比任何其他提供程序都拥有更多支持,因此这不会成为问题 - 只不过这意味着使用此方法的任何“模拟”都不会真正测试您的实际代码根本而且基本上毫无意义(IMO)
  4. 是的,棘手 - 见下文
  5. 是的,棘手 - 见下文就

我个人而言,我更喜欢这里定义良好的 API,它接受已知参数并返回 已加载< /em>结果的 ListIList(或类似);这为您提供了一个可测试/可模拟的 API,并且不会让您受到延迟执行(封闭连接地狱等)的支配。这也意味着提供者之间的任何差异都将在数据层的实现内部进行处理。它还更适合 Web 服务等调用场景

。如果在 IEnumerableIQueryable 之间进行选择,我两者都不选择 - 选择使用 IList列表。如果我需要额外的过滤,那么:

  1. 我将通过参数将其添加到现有的 API,并在我的数据层中进行过滤
  2. 我将接受超大数据返回,然后我需要在调用者处过滤掉这些数据
  1. well, you aren't strictly coupled to any specific provider, but as a re-phrasing of that: you can't easily test the code, since each provider has different supported features (meaning: what works for one might not work for another - even something like .Single())
  2. I don't think so, if there is any question in your mind about ever changing provider - see above
  3. it just provides a decorated wrapper that uses .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)
  4. yeah, tricky - see below
  5. yeah, tricky - see below

Personally, I'd prefer well defined APIs here that take known parameters and return a loaded List<T> or IList<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> and IQueryable<T>, I choose neither - opting instead to use IList<T> or List<T>. If I need additional filtering, then either:

  1. I'll add that to the existing API via parameters, and do the filtering inside my data layer
  2. I'll accept that oversized data is coming back, which I then need to filter out at the caller
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文