2017-05-16 43 views
2

問題:SQL ServerデータベースでSQLMetalを使用してDataContextを生成しました。データベースには、Int64識別子を持つエンティティを含むTableAがあります。私のクエリは、いくつかのセットのIDを持つすべての要素を照会するケースを処理する必要があります。データセットが増えたため、このセットには2100を超えるIDが含まれています。LINQ to SQL 2100の最大パラメータ制約の回避箇所

私はこの質問がトピックで尋ねられた他の人に似ていることを認識していますが、私はこの問題を回避するための拡張メソッドを構築するための助けを探しています。

関連した質問:

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(); 

そして、このエラーを生成します:
Avoiding the 2100 parameter limit in LINQ to SQL
Hitting the 2100 parameter limit (SQL Server) when using Contains()

私のコードは次のようなものになります

The incoming tabular data stream (TDS) remote procedure call (RPC) protocol stream is incorrect. Too many parameters were provided in this RPC request. The maximum is 2100.

を、私はこの問題Aに実行されている予想されます各種データとしてのロットセットが成長し、任意のテーブルに使用できる汎用拡張を作成したいと考えています。この考え方は、クエリをより小さな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; 
} 

を呼び出し元のコードがに変更されます:今、これは「universeSlice.Contains」行に失敗している

var ids = new List<long>{ 1, 2, 3, /*...,*/ 2101}; 
var database = new MyDatabaseClass(connection) 
var items = database 
.TableA 
.WhereMemberInUniverse(x=>x.RecordID, ids); 

ここで私は考えていますかを示すために私の試みの一つであります

Method 'System.Object DynamicInvoke(System.Object[])' has no supported translation to SQL.

私が試したすべての試みは、同様のSQL変換エラーで終了するようです。

+0

XMLアプローチを試しましたか? [SQL Server Central](https://www.sqlservercentral.com/Forums/Topic352589-149-1.aspx) –

答えて

3

サポートされていない構文は、LINQクエリ式ツリー内のmemberSelector(x)呼び出しです。

(これは任意のIQueryableプロバイダに基本的に適用されます)LINQクエリ、翻訳を行うには、あなたが

Expression<Func<TSource, TUniverse>> memberSelector 

にパラメータの型

Func<TSource, TUniverse> memberSelector 

を変更して

x => universeSlice.Contains(memberSelector(x)) 
を構築する必要があります

動的に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; 
} 
+0

LINQKitのようなものを使用すると、これをもっと簡単にすることができます。 – NetMage

+0

@NetMage絶対に!またはヘルパーを構成する表現。 –

+0

答えが表現と関係していると感じました。これはまさに私が探していたものです。アイヴァンありがとう! – c31983

0

使用しているSQL Serverのバージョンに応じて、あなたはおそらくTable Valued Parametersになります。

Table-valued parameters provide an easy way to marshal multiple rows of data from a client application to SQL Server without requiring multiple round trips or special server-side logic for processing the data. You can use table-valued parameters to encapsulate rows of data in a client application and send the data to the server in a single parameterized command. The incoming data rows are stored in a table variable that can then be operated on by using Transact-SQL.

まず第一に、あなたは、あなたのSQL Serverで何かをタイプを作成する必要があるだろう

CREATE TYPE [dbo].[MyIdTable] AS TABLE(
    [Id] [int] NOT NULL 
) 

次にあなたがSystem.Data.DataTableを作成し、あなたのIDを移入することができます:のような

var table = new DataTable("MyIdTable"); 
table.Columns.Add("Id", typeof(int)); 
foreach (var id in ids) 
    table.Rows.Add(id); 

そして、SQL /ストアドプロシージャにパラメータとして送信します。しかし、私が知る限り、TVPはL2SQLではサポートされていません。したがって、おそらく未処理のADO.netに落とす必要があります。 Here's正しい方向にあなたを指すことができるスタックオーバーフローの質問。