c31*_*983 4 c# linq sql-server linq-to-sql
问题:我在 SQL Server 数据库上使用 SQLMetal 生成了一个 DataContext。数据库有 TableA,其中包含具有 Int64 标识符的实体。我的查询需要处理查询某个集合中具有 ID 的所有元素的情况。随着数据集的增长,该集偶尔包含超过 2100 个 ID。
我意识到这个问题与有关该主题的其他问题类似,但我正在寻求帮助构建扩展方法来解决该问题。
相关问题:
避免 LINQ to SQL 中的 2100 个参数限制
使用 Contains() 时达到 2100 个参数限制 (SQL Server)
我的代码看起来像这样:
var ids = new List<long>{ 1, 2, 3, /*...,*/ 2101};
var database = new MyDatabaseClass(connection)
var items = database
.TableA
.Where(x=>ids.Contains(x.RecordID))
.ToList();
Run Code Online (Sandbox Code Playgroud)
并产生此错误:
传入的表格数据流 (TDS) 远程过程调用 (RPC) 协议流不正确。此 RPC 请求中提供了太多参数。最大值为 2100。
随着各种数据集的增长,我预计会经常遇到这个问题,并且我想创建一个可用于任何表的通用扩展。其想法是将查询分解为更小的Where Contains 查询,然后聚合结果。这是我试图展示我的想法的尝试之一:
public static List<TSource> WhereMemberInUniverse<TSource, TUniverse>(this IQueryable<TSource> source, Func<TSource, TUniverse> memberSelector, IEnumerable<TUniverse> universe)
{
var distinctUniverse = universe.Distinct().ToList();
int batchSize = 2000;
var result = new List<TSource>();
for (int i = 0; i < distinctUniverse.Count; i += batchSize)
{
var universeSlice = distinctUniverse.Skip(i).Take(batchSize);
var partialRes = source
.Where(x => universeSlice.Contains(memberSelector(x)));
result.AddRange(partialRes);
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
调用代码将修改为:
var ids = new List<long>{ 1, 2, 3, /*...,*/ 2101};
var database = new MyDatabaseClass(connection)
var items = database
.TableA
.WhereMemberInUniverse(x=>x.RecordID, ids);
Run Code Online (Sandbox Code Playgroud)
现在,这在“universeSlice.Contains”行上失败了:
方法“System.Object DynamicInvoke(System.Object[])”不支持对 SQL 的转换。
似乎我所做的每一次尝试都会以类似的 SQL 翻译错误告终。
不受支持的构造是memberSelector(x)LINQ 查询表达式树内的调用。
要使 LINQ 查询可翻译(这基本上适用于任何IQueryable提供程序),您需要更改参数类型
Func<TSource, TUniverse> memberSelector
Run Code Online (Sandbox Code Playgroud)
到
Expression<Func<TSource, TUniverse>> memberSelector
Run Code Online (Sandbox Code Playgroud)
然后构建
x => universeSlice.Contains(memberSelector(x))
Run Code Online (Sandbox Code Playgroud)
动态使用System.Linq.Expressions.Expression类方法:
public static List<TSource> WhereMemberInUniverse<TSource, TUniverse>(this IQueryable<TSource> source, Expression<Func<TSource, TUniverse>> memberSelector, IEnumerable<TUniverse> universe)
{
var distinctUniverse = universe.Distinct().ToList();
int batchSize = 2000;
var result = new List<TSource>();
for (int i = 0; i < distinctUniverse.Count; i += batchSize)
{
var universeSlice = distinctUniverse.Skip(i).Take(batchSize);
//x => universeSlice.Contains(memberSelector(x))
var predicate = Expression.Lambda<Func<TSource, bool>>(
Expression.Call(
typeof(Enumerable), "Contains", new Type[] { typeof(TUniverse) },
Expression.Constant(universeSlice), memberSelector.Body
),
memberSelector.Parameters
);
var partialRes = source.Where(predicate);
result.AddRange(partialRes);
}
return result;
}
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
8126 次 |
| 最近记录: |