2016-09-19 80 views
2

我有一个主要方法为给定实体创建基础搜索条件。在这种方法中,我因此在将其应用于查询之前检查默认值。使用帮助器方法创建动态lambda

E.g.

 if (!string.IsNullOrEmpty(value)) 
      qry = qry.Where(x => x.PropA.Contains(value)); 

     if (!string.IsNullOrEmpty(anotherValue)) 
      qry = qry.Where(x => x.PropB.Contains(anotherValue)); 

不过,我想重构这个和使用一个辅助方法来代替,但因为我的知识和经验的表达式是比较有限的,我有困难完成任务。

我有这样的锅炉代码,我认为说明了什么,我试图完成:

IQueryable<T> Test<T, TV>(IQueryable<T> qry, Expression<Func<T, TV>> prop, TV value) 
    { 
     if (EqualityComparer<TV>.Default.Equals(value, default(TV))) 
      return qry; 

     var right = Expression.Constant(value); 

     var body = Expression.Equal(prop, right); 
     var lambda = Expression.Lambda<Func<T, bool>>(body); 

     return qry.Where(lambda); 
    } 

应使我作出这样的电话:

qry = Test(qry, x=>PropA, value); 
qry = Test(qry, x=>PropB, anotherValue); 

问题不过是, body变量导致BinaryExpression,我完全不知道如何从这里开始。

+0

如果有什么的默认值是'-1'而不是'0'或'null'?这种方法是否真的提高了可读性?我更喜欢你的简单代码,我在那里看到'if'而不是隐藏的'if'“单行”。只是我的观点。 –

回答

3

您必须将该方法转换为表达式,然后将其作为lambda的主体。

所以,从锅炉的代码开始,上述变化后,它应该看起来像

IQueryable<T> Test<T, TV>(IQueryable<T> qry, Expression<Func<T, TV>> prop, string propertyValue) 
    { 

     MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); 
     var someValue = Expression.Constant(propertyValue, typeof(string)); 
     var body = Expression.Call(prop, method, someValue); // pseudocode, to be refined below 

     var lambda = Expression.Lambda<Func<T, bool>>(body); 

     return qry.Where(lambda); 
    } 

现在让我用一个字符串访问

static IQueryable<T> Test<T>(IQueryable<T> qry, string propertyName, string propertyValue) 
    { 
     var parameterExp = Expression.Parameter(typeof(T), "type"); 
     var propertyExp = Expression.Property(parameterExp, propertyName); 
     MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); 
     var someValue = Expression.Constant(propertyValue, typeof(string)); 
     var containsMethodExp = Expression.Call(propertyExp, method, someValue); 
     var lambda = Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp); 
     return qry.Where(lambda); 
    } 

最后一个简单的使用例子

改换
class MyClass 
    { 
     public string Myname { get; set; } 
    } 

    static void Main(string[] args) 
    { 
     var check = new MyClass() { Myname = "11 aa 22" }; 
     var check2 = new MyClass() { Myname = "11 bb 22" }; 
     var x = new List<MyClass>(); 
     x.Add(check); 
     x.Add(check2); 
     var q = x.AsQueryable(); 
     var qry = Test(q, "Myname", "bb"); 
    } 

那么,如果你喜欢一个属性选择器,帮手将变成

static IQueryable<T> Test<T>(IQueryable<T> qry, Expression<Func<T, string>> selector, string propertyValue) 
    { 
     var parameterExp = Expression.Parameter(typeof(T), "type"); 

     var memberExpression = (MemberExpression)selector.Body; 
     var parameterTProperty = (PropertyInfo)memberExpression.Member; 
     var propertyExp = Expression.Property(parameterExp, parameterTProperty); 

     MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); 
     var someValue = Expression.Constant(propertyValue, typeof(string)); 
     var containsMethodExp = Expression.Call(propertyExp, method, someValue); 
     var lambda = Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp); 
     return qry.Where(lambda); 
    } 

用作

 var qry = Test(q, z => z.Myname , "bb");