2016-12-02 17 views
2

EFコアが提供するIQuerableコレクションがいくつかあります。エンティティのSystem.Linq.Expressionsを使用してSQLに変換するための動的クエリを作成します。c#式ツリーを使用して複数のコレクションを照会

IQueryable<ADP> collection1 = _context.Adps; 
IQueryable<VHStr> collection2 = _context.VHStrParams; 

var q = (from ADP a in collection1 
     where collection2.Any(p => p.Value.Contains("I") && p.Entid == a.Id) 
     select a); 

var l = q.ToList(); 

これは正しく機能し、正しいSQLが生成されます。

質問は、どうやって式を使ってそのようなクエリを構築できますか?私はちょうど...私は最初の1の要素のために構築された表現で別のコレクションにアクセスする方法を

EDITを把握することはできません。誰かがそれを見つける場合は、解決策は次のとおりです。

 IQueryable<ADP> collection1 = _context.Adps; 
     IQueryable<VHStr> collection2 = _context.VHStrParams; 


     var q = (from ADP a in collection1 
       where collection2.Any(p => p.Value.Contains("I") && p.Entid == a.Id) 
       select a); 

     var paramExpA = Expression.Parameter(typeof(ADP), "a"); 
     var paramExpV = Expression.Parameter(typeof(VHStr), "v"); 

     var entIdExp = Expression.PropertyOrField(paramExpV, "Entid"); 
     var adpIdExp = Expression.PropertyOrField(paramExpA, "Id"); 
     var convertedAdpIdExp = Expression.Convert(adpIdExp, typeof(long?)); 

     var valueExp = Expression.PropertyOrField(paramExpV, "Value"); 
     var containsStringMethod = typeof(string).GetMethod("Contains", new[] {typeof(string)}); 
     var constValueExp = Expression.Constant("I", typeof(string)); 
     var containsExp = Expression.Call(valueExp, containsStringMethod, constValueExp); 

     var equalIdsExp = Expression.Equal(entIdExp, convertedAdpIdExp); 
     var andExp = Expression.AndAlso(containsExp, equalIdsExp); 

     var lambda1 = Expression.Lambda<Func<VHStr, bool>>(andExp, paramExpV); 
     var vhstrAnyMethod = 
      typeof(Queryable) 
       .GetTypeInfo() 
       .GetMethods() 
       .First(m => m.Name == "Any" && m.GetParameters().Count() == 2) 
       .MakeGenericMethod(typeof(VHStr)); 

     var collection2ConstExpr = Expression.Constant(collection2); 
     var anyCallExp = Expression.Call(vhstrAnyMethod, collection2ConstExpr, lambda1); 

     var collection1ConstExpr = Expression.Constant(collection1); 
     var lambda2 = Expression.Lambda<Func<ADP, bool>>(anyCallExp, paramExpA); 

     var whereExp = Expression.Call(
      typeof(Queryable), 
      "Where", 
      new Type[] { typeof(ADP) }, 
      collection1ConstExpr, 
      lambda2); 

     var lambda3 = Expression.Lambda<Func<IQueryable<ADP>>>(whereExp); 
     var resultFunc = lambda3.Compile(); 
     var resultQuerable = resultFunc(); 

     var resultList = resultQuerable.ToList(); 

答えて

1

私はまあ、コンパイル時のクエリは、外部変数を渡すクロージャークラスを使用しています...私は最初の1の要素のために構築された表現で別のコレクションにアクセスする方法を

を把握することはできませんクエリ。手動式ツリーを構築していますが、単に式に変数を有効にするExpression.Constantメソッドを使用することができますので、それをする必要がないときは、同じ操作を行うことができます。

var collection2expr = Expression.Constant(collection2); 
var anyParam = Expression.Parameter(collection2.ElementType, "p"); 
var anyPredicate = Expression.Lambda(
    dynamically_built_predicate_body, // p.Value.Contains("I") && p.Entid == a.Id 
    anyParam); 
var anyCall = Expression.Call(
    typeof(Queryable), "Any", new Type[] { anyParam.Type }, 
    collection2Expr, Expression.Quote(anyPredicate)); 
// etc... 
+0

をポイントである - 正確にどのように私は条件を追加することができます"p.Entid == a.Id"を 'dynamically_built_predicate_body'に追加しました。 – pushist1y

+0

私はそれが明らかだと思います。 'p'は上記のスニペットの' anyParam'です。 'a'はあなたが作成する別の' ParameterExpression'になります( 'var a = Expression.Parameter(collection1.ElementType、" a ");' 'Where'コール用の述部を生成するために使われます)' anyCall 'p.Entid == a.id'の場合は' Expression.Equal(Expression.PropertyOrField(anyParam、 "Entid")、Expression.PropertyOrField()があります。 (a、 "id"))など。 –

+0

ありがとう、Expression.Constantは正しい方向へのポイントでした。 – pushist1y

関連する問題