2016-12-02 83 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