2014-06-27 64 views
0

我有一个SelectionCriteria类用于构建基于PredicateBuilder的实体框架查询表达式。在其限制内,它工作正常。我想扩展它,以便它可以查询一个字段是否包含一个子字符串。我的问题是,我看不到如何构建所需的表达式对象。在表达式树中调用lambda表达式

我的实际课程支持和,或和,但不是,但它们与我的问题无关。所以,我已经简化我的例子代码只处理一个单一的二进制运算:

public class SelectionCriteria 
{ 
    public SelectionComparison selectionComparison { get; set; } 
    public string fieldName { get; set; } 
    public object fieldValue { get; set; } 

    public Expression<Func<T, bool>> constructSinglePredicate<T>() 
    { 
     var type = typeof(T); 

     if (type.GetProperty(this.fieldName) == null && type.GetField(this.fieldName) == null) 
      throw new MissingMemberException(type.Name, this.fieldName); 

     ExpressionType operation; 
     if (!operationMap.TryGetValue(this.selectionComparison, out operation)) 
      throw new ArgumentOutOfRangeException("selectionComparison", this.selectionComparison, "Invalid filter operation"); 

     var parameter = Expression.Parameter(type); 
     var member = Expression.PropertyOrField(parameter, this.fieldName); 
     var value = (this.fieldValue == null) ? Expression.Constant(null) : Expression.Constant(this.fieldValue, this.fieldValue.GetType()); 

     try 
     { 
      var converted = (value.Type != member.Type) 
       ? (Expression) Expression.Convert(value, member.Type) 
       : (Expression) value; 

      var comparison = Expression.MakeBinary(operation, member, converted); 

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

      return lambda; 
     } 
     catch (Exception) 
     { 
      throw new InvalidOperationException(
       String.Format("Cannot convert value \"{0}\" of type \"{1}\" to field \"{2}\" of type \"{3}\"", this.fieldValue, 
        value.Type, this.fieldName, member.Type)); 
     } 
    } 

    private static Dictionary<SelectionComparison, ExpressionType> operationMap = 
     new Dictionary<SelectionComparison, ExpressionType> 
     { 
      { SelectionComparison.Equal, ExpressionType.Equal }, 
      { SelectionComparison.GreaterThan, ExpressionType.GreaterThan }, 
     }; 
} 

public enum SelectionComparison 
{ 
    Equal, 
    GreaterThan, 
    Contains, 
}; 

用法很简单:

var criteria = new SelectionCriteria 
{ 
    selectionComparison = SelectionComparison.GreaterThan, 
    fieldName = "worktobegindate", 
    fieldValue = DateTime.Now.AddDays(-2) 
}; 

var predicate = criteria .constructPredicate<job>(); 
var jobs = myDbContext.jobs.Where(predicate); 

所以,我的问题 - 我需要一个方法,像我constructSinglePredictate()以上,返回一个表达式并应用一个.Contains(给定一个字符串来比较它,将Contains()应用到一个字符串是很简单的,但我很难理解如何在一个表达式中做同样的事情。

想法?

回答

0

和往常一样,我正在错误地考虑事情。我不需要这样一个字符串调用的方法的拉姆达,我需要一个MethodExpression的(从methodMap字典这里提取):

public Expression<Func<T, bool>> constructMethodCallPredicate<T>() 
{ 
    var type = typeof(T); 

    if (type.GetProperty(this.fieldName) == null && type.GetField(this.fieldName) == null) 
     throw new MissingMemberException(type.Name, this.fieldName); 

    MethodInfo method; 
    if (!methodMap.TryGetValue(this.selectionComparison, out method)) 
     throw new ArgumentOutOfRangeException("selectionComparison", this.selectionComparison, "Invalid filter operation"); 

    var parameter = Expression.Parameter(type); 
    var member = Expression.PropertyOrField(parameter, this.fieldName); 
    var value = (this.fieldValue == null) ? Expression.Constant(null) : Expression.Constant(this.fieldValue, this.fieldValue.GetType()); 

    try 
    { 
     var converted = (value.Type != member.Type) 
      ? (Expression)Expression.Convert(value, member.Type) 
      : (Expression)value; 

     var methodExpression = Expression.Call(member, method, converted); 

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

     return lambda; 
    } 
    catch (Exception) 
    { 
     throw new InvalidOperationException(
      String.Format("Cannot convert value \"{0}\" of type \"{1}\" to field \"{2}\" of type \"{3}\"", this.fieldValue, 
       value.Type, this.fieldName, member.Type)); 
    } 
} 

private static readonly Dictionary<SelectionComparison, MethodInfo> methodMap = 
    new Dictionary<SelectionComparison, MethodInfo> 
    { 
     { SelectionComparison.Contains, typeof(string).GetMethod("Contains", new[] { typeof(string) }) }, 
     { SelectionComparison.StartsWith, typeof(string).GetMethod("StartsWith", new[] { typeof(string) }) }, 
     { SelectionComparison.EndsWith, typeof(string).GetMethod("EndsWith", new[] { typeof(string) }) }, 
    }; 

public enum SelectionComparison 
{ 
    Equal, 
    NotEqual, 
    LessThan, 
    LessThanOrEqual, 
    GreaterThan, 
    GreaterThanOrEqual, 
    Contains, 
    StartsWith, 
    EndsWith, 
}; 

获得一个实际的“Like”比较配合使用,SqlFunctions.PatIndex是有点复杂,PatIndex()返回一个int值,我需要将它包装在> 0表达式中,但它也可以很好地工作。