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的延迟加载都受制于维护底层提供程序,我应该使用工作模式单元还是其他东西来处理这个问题?
提前多谢!
- 我认为人们不应该把延迟加载机制建立在iQuery功能的基础上。
- 你能不能帮我在市区买一点?我想,如果我理解你,我同意。我们当前的项目返回iQueryable,尽管它在业务层甚至表示层中提供了额外的功能和便利性,但感觉不太好。
- 我认为让iQuery将负载延迟到最后可能的时刻是很好的,但这实际上是在构造查询时进行的。正如你已经说过的,分发它需要你保持上下文的活跃,这是你可能无法保证的。我宁愿使用一个定义和执行查询并让它返回IEnumerable的类。但我可能错了:)
- @我完全同意Urban的观点,但要记住,只需将函数return更改为IEnumerable,我仍然会维护查询的延迟负载。我仍然面临着必须保持我认为的背景的问题?
- 您的IEnumerable将是一个完全加载的列表。您可能会在方法中使用一个列表,并将其作为IEnumerable或IList传递出去。
- 虽然@urban就是这样,但是如果将函数返回类型更改为ienumerable的话,结果不会变成完全加载的列表。只有当您将AsEnumerable()附加到查询的后面时,它才会加载列表。我们起初也是这么想的,后来意识到,因为iqueryable继承了IEnumerable,所以不需要隐式强制转换,iqueryable可以按原样返回。然后维护延迟加载,并要求上下文保持活动状态,直到使用者尝试枚举结果。有道理吗?
- 似乎我还不够清楚,对此我感到抱歉:我建议在内部使用一个列表,并将其作为(然后完全加载的)IEnumerable传递出去。但"在现实中",这将是一个列表。如果您对此不满意,正如Marc建议的那样,您可能还会返回一个ilist。这样一来,如果他们得到的东西需要最初执行或不执行,没有人会感到困惑。
- 啊,我现在明白了@urban,我看到@marc在他的评论中提出了类似的建议。我个人认为我更喜欢IList,因为如果使用IEnumerable,那么在某人忘记添加tolist()并突然拥有依赖于基础数据上下文生命的代码部分之前,这只是时间问题——这可能是调试的噩梦!使用iList,您不能意外地传递出对上下文的引用(至少是这样)。我认为它也很适合SOA,但我想这取决于个人偏好。;-)感谢您对这个问题的所有意见!
好吧,您并没有严格地与任何特定的提供者耦合,但是作为对这一点的补充:您不能轻易地测试代码,因为每个提供者都有不同的支持特性(意思是:为一个提供者工作的东西可能不为另一个提供者工作——甚至是像.Single()这样的东西)。
我不这么认为,如果您对不断变化的供应商有任何疑问,请参见上文。
它只是提供了一个装饰的包装器,在任何lambda上使用.Compile(),并使用linq来替代对象。注意,linq-to-objects比任何其他提供者都有更多的支持,所以这不会是一个问题,除非它意味着使用这种方法的任何"模拟"都不会真正测试您的实际代码,而且基本上是无意义的(imo)
是的,很棘手-见下文
是的,很棘手-见下文
就我个人而言,我更喜欢这里定义良好的API,它采用已知的参数并返回加载的List或IList或类似的结果;这为您提供了一个可测试/可模拟的API,并且不会让您受制于延迟执行(关闭连接地狱等)。它还意味着提供者之间的任何差异都在内部处理到数据层的实现。它也更适合于调用场景,如Web服务等。
简而言之,考虑到IEnumerable和IQueryable之间的选择,我既不选择使用IList也不选择List。如果我需要额外的过滤,那么:
我将通过参数将其添加到现有的API中,并在我的数据层中进行过滤。
我会接受过大的数据正在返回,然后我需要在呼叫者处过滤掉这些数据。
- 你能给我解释一下为什么你更喜欢ilist或list而不是ienumerable吗?我一直认为返回可变列表是一种不好的实践,因此返回IEnumerable是首选。想看看这个不同的观点吗
- @urbanesc为什么不返回列表?该列表对调用方是唯一的。如果他们想为了自己的目的而把别的东西放进去,那就是他们的问题。它不会影响其他任何东西。
- 我明白你的观点,并不完全反对。正如我所说,我一直认为最好的做法是使用最低的公共接口。所以最有可能的情况是,调用者希望得到一个可以反复访问的列表,而不需要修改这个列表,对吗?我对你的理解正确吗?你使用列表而不是IEnumerable的原因是你或多或少地更喜欢它,这不是一个技术问题。
- @urbanesc如果你没有在IList上出售,那么考虑退回IEnumerable,但实际情况是清单。它仍然解决了大多数问题(包括延迟执行)。这就是我们在"dapper"中所做的,正如它所发生的(除非当我们返回原始IEnumerable时,您设置buffered = false)
- 谢谢你的夸奖,是的,这是我们喜欢的方式。
- 回答得很好,马克帮我把事情弄清楚了。我想知道我是否在挖掘自己的漏洞,将返回类型限制为IEnumerable和iqueryable(如您所说,由于处理了上下文问题),但通过阅读类似问题的链接,我发现它们是最常见的/建议的。我所追求的只是以一种迂回的方式将这一层分离出来的最佳格式,我当然会这样做,谢谢!
- @smudge202列表的一个巨大优势-调用者知道他们可以很便宜地查询长度,并且可以根据自己的喜好多次迭代,并且可以很便宜地获取第三个项目,等等。
- 当然是@marc,但是由于我们当前的dal返回iqueryable,我们的业务逻辑开始依赖于能够附加到查询表达式。我猜,基于业务逻辑调整查询过滤是非常方便的,所以我现在正在考虑您最后两点中的哪一点是保持尽可能多的灵活性的最佳方法,同时提供我们正在寻找的解耦。
- @如果数据不是真正的查询,返回IQueryable的smudge202是没有意义的,并且增加了(一点)开销。所以我会在你的限制范围内
- @马克,明白了。AsQueryable()扩展在我们的项目中实际上并不经常使用——如果真的如此,那更多的是一个需要注意的外部情况。我们一直在做的是在业务逻辑中添加IQueryable,因为我们发现与在List上工作相比,性能有了相当大的改进。不过,不用担心,我认为我们可以解决剩下的问题。再次感谢!