2017-04-12 5 views
2

linq(nhibernateを使用)を使用して動的クエリを実行する必要があるシナリオをここに示します。任意の内部式を呼び出してデリゲートを渡します

long[] values = { ... }; 

var result = Queryable<Entity>.Where(x => x.Documents.Any(d => values.Contains(d.Id))) 
           .ToList(); 

ジェネリックEntityと変更することができ、それは一部のユーザー設定によって定義されますDocumentsプロパティ:最終クエリは次のようになります。コレクションDocumentsのタイプはICollection<T>であり、TDocumentタイプです。私はこれらのステートメントを動的に定義するためにExpressionツリーを作成しようとしていますが、いくつかの問題が発生しています。私が試したことのコードとコメントを見てください。

私はAnyメソッド内で使用したいdelagateを返すために、この関数を作成します:

public static Func<T, bool> GetFunc<T>(long[] values) 
     where T : Entity 
    { 
     return x => values.Contains(x.Id); 
    } 

そして、私は(コードとコメントを参照)、このような表現をするためにExpressionクラスを使用しています:

// define my parameter of expression 
var parameter = Expression.Parameter(typeof(T), "x"); 

// I get an array of IDs (long) as argument and transform it on an Expression 
var valuesExpression = Expression.Constant(values); 

// define the access to my collection property. propertyFilter is propertyinfo for the `Documents` of the sample above. 
// I get an expression to represent: x.Documents 
var collectionPropertyExpression = Expression.Property(parameter, propertyFilter); 

// get the T generic type of the ICollection<T> from propertyFilter. I get the `Documents` of sample above. 
var entityFilterType = propertyFilter.PropertyType.GetGenericArguments()[0]; 

// get the definition of `Any` extension method from `Enumerable` class to make the expression 
var anyMethod = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public) 
                 .First(x => x.Name == "Any" && x.GetParameters().Length == 2) 
                 .MakeGenericMethod(entityFilterType); 

// get a methodBase for GetFunc to get the delagete to use inside the Any 
// using the `Document` generic type 
var collectionBody = typeof(LookUpHelper).GetMethod("GetFunc", BindingFlags.Public | BindingFlags.Static) 
                  .MakeGenericMethod(entityFilterType); 

// call the any passing the collection I need and convert it to a Delegate 
// I get something like: x => values.Contains(x.Id) ... where x if the `Document` 
var func = (Delegate)collectionBody.Invoke(null, new object[] { values }); 

// get the func as an expression .. maybe the problem is here 
var funcExpression = Expression.Constant(func); 

// call the any passing the collection and my delagate as arguments 
var f = Expression.Call(anyMethod, collectionPropertyExpression, funcExpression); 

// I already have an expression and concatenate it using `AndAlso` operator. 
body = Expression.AndAlso(body, f); 

// finally, I built up to lambda expression and apply it on my queryable 
var filterExpression = Expression.Lambda<Func<T, bool>>(body, parameter); 

var result = Queryable.Where(filterExpression).ToList(); 

ToListメソッドでクエリが実行されるまで実行されます。次のエラーが表示されています:

Could not parse expression 'x.Documents.Any(value(System.Func`2[Project.Document,System.Boolean]))': The object of type 'System.Linq.Expressions.ConstantExpression' cannot be converted to type 'System.Linq.Expressions.LambdaExpression'. If you tried to pass a delegate instead of a LambdaExpression, this is not supported because delegates are not parsable expressions.

私は間違っているのか分かりません。誰かが私を助けることができますか?

ありがとうございます。

答えて

1

Expression<Func>が期待されるFuncを渡しています。前者は代理人であり、後者は表現です。

public static Expression<Func<T, bool>> GetFunc<T>(long[] values) 
     where T : Entity 
{ 
    return x => values.Contains(x.Id); 
} 

これで、式をすでに持っているので、式ヘルパークラスを使用して手動で式を構築する必要はありません。

+0

私はそれを修正し、期待どおりに動作していません。私はFuncとExpressionについて混乱させていました。私は目が見えず、このFuncをExpressionに変換する方法を探していました。この非動的メソッドを変更することさえ考えませんでした。 Davidに感謝します。 –

+0

これは簡単な落とし穴です。お力になれて、嬉しいです! –

関連する問題