LINQ.Contains超过2100记录报异常Exception的问题

今天遇到一个LINQ的异常: 传入的表格格式数据流(TDS)远程过程调用(RPC)协议流不正确。此 RPC 请求中提供了过多的参数。最多应为 2100。

代码是:

Table<MyEntity> tbl = dataContext.GetTable<MyEntity>();
tbl.
where(t => AnotherList.Contains(t.EntityID)).select(....)

报错的地方就是.Contains函数,当AnotherList的元素个数超过2100,LINQ的.Contains最大支持2100,超过就会报这个异常。这句LINQ背后的SQL语句可能是这样的:

SELECT [t0].....
FROM MyEntity AS [t0]
WHERE [t0].[EntityID] IN (@p0, @p1, @p2, @p3, @p4......2100)

所以就遇到SQL过长的问题,微软为了防止这个问题,就人为LINQ.Contains()限制了2100这个数量,很是奇怪。

解决办法很简单,只要将调用数据源的AsEnumerable()运算符就可以了。例如:

var tbl = dataContext.GetTable<MyEntity>().AsEnumerable();
tbl.
where(t => AnotherList.Contains(t.EntityID)).select(....)

其它方法也有,就是转变思路,用JOIN的方式,而不要用WHERE ... IN (...,...,...)的方式。


为什么.AsEnumerable()这么神奇?


从MSDN看到,调用这个方法会返回一个 IEnumerable<T> 对象,而大家知道,和IQueryable比较起来,IEnumerable<T>是用了LINQ2Object的方式,而不是LINQ2SQL的方式,前者数据是在内存中处理的,而后者是在server上运行的。换句话说,本地数据源用IEnumerable<T>,数据在内存中,并且查询的逻辑可以直接用你所定义的方法的逻辑(因为有上下文);远程数据源用IQueryable<T>,无法直接使用你所定义的方法的逻辑,必须先生成表达式树,查询由源对象处理;IQueryable<T>类型可以在最终的执行前加一些方法,然后再返回最终的IQueryable<T>;而IEnumerable<T>已经包含了你执行查询后的所有结果,这时你的添加方法都是在已经有的结果集里进行。一言以蔽之,上面的2100的问题解决方法就是避免了LINQ2SQL生成表达式树查询远程服务器!

这个方法有个副作用:调用dataContext.GetTable<MyEntity>().AsEnumerable()的副作用是吃内存,如果这个table很大,估计内存空间会消耗很大。

此外,调用AsEnumerable()会导致Enumerable.Where运行,而不是原生的.Where运行,看个例子:

1 public class ProductList : List<Product>
2 {
3 public IEnumerable<Product> Where(Func<Product, bool> predicate)
4 {
5 Console.WriteLine("Got Here");
6 foreach (var product in this)
7 {
8 if (predicate(product))
9 {
10 yield return product;
11 }
12 }
13 }
14 }
15
16
17 var products = productList
18 .Where(p => p.Name.Contains(productName))
19 .ToArray();
20
21  //Console输出 “Got Here”
22  
23 var products = productList
24 .AsEnumerable()
25 .Where(p => p.Name.Contains(productName))
26 .ToArray();
27  //Console没有输出
原文地址:https://www.cnblogs.com/Mainz/p/1971113.html