2016-12-02 84 views
2

我有一些由EF Core提供的IQuerable集合,我想爲它們構建一個動態查詢,使用System.Linq.Expressions for Entity將它轉換爲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。

問題是 - 我如何使用表達式建立這樣的查詢?我只是想不通我怎麼能在第一個的元素構建的表達式來訪問另一個集合...

編輯:萬一有人發現的,解決的辦法是:

 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

我只是想不通我怎麼能訪問另一個集合中的第一個要素爲基礎的表情...

那麼,編譯時查詢使用closure類來傳遞外部變量的查詢。手動構建表達式樹時,你可以這樣做,但沒有必要這麼做,因爲你可以簡單地使用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」);'並且將被用於使用'anyCall產生'Where'調用的謂詞'Expression as body。你應該能夠從那裏生成具體條件,對於'p.Entid == a.id',它將是'Expression.Equal(Expression.PropertyOrField(anyParam,「Entid」),Expression.PropertyOrField (a,「id」))等。 –

+0

謝謝,Expression.Constant是一個指向正確方向的點。 – pushist1y