关于C#:用于业务逻辑或DAL返回类型的IEnumerable与IQueryable

IEnumerable vs IQueryable for Business Logic or DAL return Types

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

  • IEnumerable与IQueryable
  • list、ilist、ienumerable、iqueryable、icocollection,哪种返回类型最灵活?
  • 返回IEnumerable与iqueryable
  • IEnumerable作为返回类型
  • https://stackoverflow.com/questions/2712253/ienumerable-and-iqueryable
  • 具有业务逻辑和代码的视图
  • wpf ienumerablevs iqueryableas datasource
  • IEnumerablevs IListvs iqueryable
  • 我的服务应该返回什么接口?iqueryable,ilist,ienumerable?
  • 我应该从DAL返回IEnumerable还是iqueryable

正如你所看到的,在这个问题上,有很多很好的资源,但是这个问题的一个问题/部分,我仍然不确定是否已经通读了这些。

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

对于这两个界面,我发现有不同的意见,这是非常好的。然而,我关注的是DAL返回iqueryable的含义。据我所知,iqueryable建议/暗示在引擎盖下有一个Linq提供者。这是第一个问题——如果DAL突然需要来自非LINQ提供的源的数据怎么办?下面的内容是可行的,但它更像是一个黑客吗?

1
2
3
4
5
6
7
8
9
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,并且我可以保留延迟的加载。我失去的是追加到查询表达式的能力:

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

当返回iqueryable时,正如我理解的那样,这将简单地附加表达式。返回IEnumerable时,尽管保持延迟加载,但必须对表达式进行计算,以便将结果带到内存中并通过枚举筛选出不正确的ProductTypeID。

其他人是怎么绕过这个的?

  • 在dal-getallbyproducttype、getallbystartdate…中提供更多函数。等
  • 提供接受谓词的重载?即

    1
    2
    3
    4
    5
    6
    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的延迟加载都受制于维护底层提供程序,我应该使用工作模式单元还是其他东西来处理这个问题?
  • 提前多谢!


  • 好吧,您并没有严格地与任何特定的提供者耦合,但是作为对这一点的补充:您不能轻易地测试代码,因为每个提供者都有不同的支持特性(意思是:为一个提供者工作的东西可能不为另一个提供者工作——甚至是像.Single()这样的东西)。
  • 我不这么认为,如果您对不断变化的供应商有任何疑问,请参见上文。
  • 它只是提供了一个装饰的包装器,在任何lambda上使用.Compile(),并使用linq来替代对象。注意,linq-to-objects比任何其他提供者都有更多的支持,所以这不会是一个问题,除非它意味着使用这种方法的任何"模拟"都不会真正测试您的实际代码,而且基本上是无意义的(imo)
  • 是的,很棘手-见下文
  • 是的,很棘手-见下文
  • 就我个人而言,我更喜欢这里定义良好的API,它采用已知的参数并返回加载的ListIList或类似的结果;这为您提供了一个可测试/可模拟的API,并且不会让您受制于延迟执行(关闭连接地狱等)。它还意味着提供者之间的任何差异都在内部处理到数据层的实现。它也更适合于调用场景,如Web服务等。

    简而言之,考虑到IEnumerableIQueryable之间的选择,我既不选择使用IList也不选择List。如果我需要额外的过滤,那么:

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