0

私はPredicateBuilderが本当に好きです。これにより、あらゆる種類のクエリを非常に動的に構築できます。述語変数はさまざまなオブジェクトに渡すことができ、それらは知っている値などを追加できます。ハッシュされたコレクションに.Containsを使用する必要がある場合を除きます。Bzzt!クラッシュして燃えます。

たとえば(例/擬似コード、これはコンパイル/実行される場合とされない場合があります):

protected Expression<Func<MyClass, bool>> GetWherePredicate()
{
    string[] selectedValues = Request.Form.GetValues("checkbox1") ?? new string[0];
    HashSet<int> selectedIDs = new HashSet<int>(selectedValues.Cast<int>());

    Expression<Func<MyClass, bool>> predicate = PredicateBuilder.True<MyClass>();
    predicate = predicate.And(s => selectedIDs.Contains(s.ID));

    return predicate;
}

protected void Retrieve()
{
    Expression<Func<MyClass, bool>> predicate = GetWherePredicate();
    IEnumerable<MyClass> retrievedValues = MyDataContext.GetTable<MyClass>.Where(predicate);
}

これを実行しようとすると、NotSupportedExceptionが発生します。メソッド'Boolean contains(Int32)'は、selectedIDs HashSetがスコープ内にないため、SQLへの変換がサポートされていません。これをすべて同じ方法で行うと、正常に機能します。

HashSetが宣言されている場所とは異なるスコープで使用できるように、述語を解決またはコンパイルするための正しい方法を知る必要があります。何か助けはありますか?

更新:私はこれをかなり間違っていました。以下のコードは正常に機能するため、スコープの競合はありません。ジェイありがとう。

string[] selectedValues = Request.Form.GetValues("checkbox1") ?? new string[0];
Expression<Func<MyClass, bool>> predicate = PredicateBuilder.True<MyClass>();
predicate = predicate.And(s => selectedValues.Contains(s.ID.ToString()));
4

2 に答える 2

2

あなたが引用する例外から、スコープがここでの要因である可能性は低いようです。

これに対する私の答えは、代わりにselectedIDsとして宣言する必要があるということです(または、呼び出す前にキャストするだけですが、すべて同じメソッドで機能することを考慮していないため、わかりません。IEnumerable<int>HashSet<int>Contains()

この動作を示す実際のコードが表示されない場合、これ以上のトラブルシューティングは困難になります。

于 2010-08-05T03:46:35.743 に答える
0

名前に目がくらむようなことはしないでくださいContains...その名前が付けられたメソッドはたくさんありますが、SQLへの変換をサポートしているメソッドは多くありません。

  • Enumerable.Contains<T>およびList<T>.Containsは、サポートされている翻訳を使用するメソッドです。
  • HashSet<T>.Containsサポートされている翻訳がないメソッドです。

多くの解決策がありますが、私はお勧めします:

IEnumerable<string> selectedValueQuery =
  Request.Form.GetValues("checkbox1") ?? Enumerable.Empty<string>();
List<string> selectedIds = selectedValueQuery
  .Cast<int>()
  .Distinct()
  .ToList();

より簡単な解決策は次のようになりますが:

IEnumerable<int> selectedIDs = new HashSet<int>(selectedValues.Cast<int>()); 

編集:Containsの具体的な実装についてはまったく関係ありません。これは、linqtosqlクエリプロバイダーがメソッドを識別し、それをIN(リスト)SQL式に変換できるかどうかに関するものです。認識コードは、式で使用されるパラメーターのタイプを調べます。認識コードは、ポリモーフィズム/実装を使用せず、他の可能性を探して継承ツリーをウォークしません。

List<int> myList = new List<int>(){1, 2, 3};
IList<int> myIList = myList;
IEnumerable<int> myIEnumerable = myList;

  //works by List<T>.Contains()
db.Customers.Where(c => myList.Contains(c.CustomerID));

  //doesn't work, no translation for IList<T>.Contains
db.Customers.Where(c => myIList.Contains(c.CustomerID));

  //works by Enumerable.Contains<T>()
db.Customers.Where(c => myIEnumerable.Contains(c.CustomerID));

  //works by Enumerable.Contains<T>()
db.Customers.Where(c => Enumerable.Contains(myIEnumerable, c.CustomerID));

これらのパラメーターは同じインスタンスを参照しますが、パラメーターのタイプが異なるため、異なる変換動作が発生します。

.Contains()は翻訳されているため呼び出されないため、その実装は関係ありません。それができるthrow NotImplementedExceptionreturn true;

于 2010-08-05T17:47:24.053 に答える