2011-08-25 88 views
1

嵌套對方法調用中的方法調用我想在調用包含它之前將DateTime轉換爲字符串。然而,儘管我把一個表達的結果放到另一個表達的努力中,但我失敗了。我如何使用Expression.Call

代碼是從這個question jqgrid的最高回答得出的,使用asp.net webmethod和json處理排序,分頁,搜索和LINQ - 但需要動態運算符。

假設我已經從question以下方法作爲StringExtension:

public static class StringExtensions 
{ 
    public static MemberExpression ToMemberExpression(this string source, ParameterExpression p) 
    { 
    if (p == null) 
     throw new ArgumentNullException("p"); 

    string[] properties = source.Split('.'); 

    Expression expression = p; 
    Type type = p.Type; 

    foreach (var prop in properties) 
    { 
     var property = type.GetProperty(prop); 
     if (property == null) 
      throw new ArgumentException("Invalid expression", "source"); 

     expression = Expression.MakeMemberAccess(expression, property); 
     type = property.PropertyType; 
    } 

    return (MemberExpression)expression; 
    } 
} 

因此,我已經從question我所然後適於日期時間以下的方法也。

public virtual Expression<Func<T, bool>> CreateExpression<T>(string searchField, string searchString, string searchOper) 
    { 
     Expression exp = null; 
     var p = Expression.Parameter(typeof(T), "p"); 

     Expression propertyAccess = searchField.ToMemberExpression(p); 

     switch (searchOper) 
     { 
      case "bw": 
       exp = Expression.Call(propertyAccess, typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) }), Expression.Constant(searchString)); 
       break; 

      // New code by me 
      case "cn": 

       if (propertyAccess.Type == typeof(DateTime)) 
       { 
        // My faulty logic - from Jon Skeet answer below 

        Expression toStringCall = Expression.Call(
         propertyAccess, "ToString", 
         null, 
         new[] { Expression.Constant("D") }); 

        Expression containsCall = Expression.Call(
         toStringCall, "Contains", 
         null, 
         new[] { Expression.Constant(searchString) }); 

        exp = containsCall; 
       } 
       else 
       { 
        // Unchanged 
        exp = Expression.Call(propertyAccess, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), Expression.Constant(searchString)); 
       } 
       break; 
      case "ew": 
       exp = Expression.Call(propertyAccess, typeof(string).GetMethod("EndsWith", new Type[] { typeof(string) }), Expression.Constant(searchString)); 
       break; 
      case "gt": 
       exp = Expression.GreaterThan(propertyAccess, Expression.Constant(searchString, propertyAccess.Type)); 
       break; 
      case "ge": 
       exp = Expression.GreaterThanOrEqual(propertyAccess, Expression.Constant(searchString, propertyAccess.Type)); 
       break; 
      case "lt": 
       exp = Expression.LessThan(propertyAccess, Expression.Constant(searchString, propertyAccess.Type)); 
       break; 
      case "le": 
       exp = Expression.LessThanOrEqual(propertyAccess, Expression.Constant(searchString, propertyAccess.Type)); 
       break; 
      case "eq": 
       exp = Expression.Equal(propertyAccess, Expression.Constant(searchString.ToType(propertyAccess.Type), propertyAccess.Type)); 
       break; 
      case "ne": 
       exp = Expression.NotEqual(propertyAccess, Expression.Constant(searchString, propertyAccess.Type)); 
       break; 
      default: 
       return null; 
     } 

     return (Expression<Func<T, bool>>)Expression.Lambda(exp, p); 
    } 

我收到以下異常。

LINQ to Entities不能識別方法'System.String ToString(System.String)'方法,並且此方法不能轉換爲存儲表達式。

+1

爲什麼不呢?怎麼了? – SLaks

+0

什麼是'propertyAccess'? –

回答

2

嫌疑你想這樣的事情(從以前的版本固定):

using System; 
using System.Linq.Expressions; 

public class Person 
{ 
    public DateTime DateOfBirth { get; set; } 
} 

public class Test 
{ 
    static void Main() 
    { 
     var expr = Foo<Person>("DateOfBirth", "1976"); 

     Person p = new Person 
     { 
      DateOfBirth = new DateTime(1976, 6, 19) 
     }; 

     Console.WriteLine(expr.Compile()(p)); 
    } 

    static Expression<Func<T, bool>> Foo<T>(string propertyName, 
              string searchValue) 
    { 
     ParameterExpression parameter = Expression.Parameter(typeof(T), "x"); 
     Expression property = Expression.Property(parameter, propertyName); 
     Expression toStringCall = Expression.Call(
      property, "ToString", 
      null, 
      new[] { Expression.Constant("D") }); 

     Expression containsCall = Expression.Call(
      toStringCall, "Contains", 
      null, 
      new[] { Expression.Constant(searchValue) }); 

     return Expression.Lambda<Func<T, bool>>(containsCall, parameter); 
    } 
} 

注意「空」值,以表明它是一個非泛型方法調用。

+0

這看起來像答案,但在我標記它之前,你可以澄清新[] {Expression.Constant(「D」)}中的D是什麼。我假設這是格式化的DateTime對象。 其次可以將參數new [] {Expression.Constant(「D」)}爲空,空或缺失? 或者,Expression.Constant(「D」)可以是Expression.Constant(「」)或Expression.Constant(null)? – Neil

+0

@Neil:這是「長日期時間」格式字符串。給定方法調用名稱,我假定你想用長格式格式化DateTime。是的,它可能是一個空數組或(可能)爲空 - 不確定是否在後面。使用Expression.Constant(「」)或Expression.Constant(null)就像調用'Foo.ToString(「」)'或'Foo.ToString(null)'。 (你可能需要告訴Expression.Constant你想要表達式的類型是什麼。) –

+0

Nah,仍然無法工作,請看我現在更全面,更新的答案。 – Neil

0

試試這個代碼.....

呼叫ToExpression(...)... 例。 ToExpression(NULL,Product.Name, 「==」, 「測試」); 這裏。 Product.Name是嵌套屬性..

public static Expression<Func<T, bool>> ToExpression<T>(string andOrOperator, string propName, string opr, string value, Expression<Func<T, bool>> expr = null) 
    { 
     Expression<Func<T, bool>> func = null; 
     try 
     { 
      ParameterExpression paramExpr = Expression.Parameter(typeof(T)); 
      var arrProp = propName.Split('.').ToList(); 
      Expression binExpr = null; 
      string partName = string.Empty; 
      arrProp.ForEach(x => 
      { 
       Expression tempExpr = null; 
       partName = partName.IsNull() ? x : partName + "." + x; 
       if (partName == propName) 
       { 
        var member = NestedExprProp(paramExpr, partName); 
        var type = member.Type.Name == "Nullable`1" ? Nullable.GetUnderlyingType(member.Type) : member.Type; 
        tempExpr = ApplyFilter(opr, member, Expression.Convert(ToExprConstant(type, value), member.Type)); 
       } 
       else 
        tempExpr = ApplyFilter("!=", NestedExprProp(paramExpr, partName), Expression.Constant(null)); 
       if (binExpr != null) 
        binExpr = Expression.AndAlso(binExpr, tempExpr); 
       else 
        binExpr = tempExpr; 
      }); 
      Expression<Func<T, bool>> innerExpr = Expression.Lambda<Func<T, bool>>(binExpr, paramExpr); 
      if (expr != null) 
       innerExpr = (andOrOperator.IsNull() || andOrOperator == "And" || andOrOperator == "AND" || andOrOperator == "&&") ? innerExpr.And(expr) : innerExpr.Or(expr); 
      func = innerExpr; 
     } 
     catch { } 
     return func; 
    } 

    private static MemberExpression NestedExprProp(Expression expr, string propName) 
    { 
     string[] arrProp = propName.Split('.'); 
     int arrPropCount = arrProp.Length; 
     return (arrPropCount > 1) ? Expression.Property(NestedExprProp(expr, arrProp.Take(arrPropCount - 1).Aggregate((a, i) => a + "." + i)), arrProp[arrPropCount - 1]) : Expression.Property(expr, propName); 
    } 

    private static Expression ToExprConstant(Type prop, string value) 
    { 
     if (value.IsNull()) 
      return Expression.Constant(value); 
     object val = null; 
     switch (prop.FullName) 
     { 
      case "System.Guid": 
       val = value.ToGuid(); 
       break; 
      default: 
       val = Convert.ChangeType(value, Type.GetType(prop.FullName)); 
       break; 
     } 
     return Expression.Constant(val); 
    } 

    private static Expression ApplyFilter(string opr, Expression left, Expression right) 
    { 
     Expression InnerLambda = null; 
     switch (opr) 
     { 
      case "==": 
      case "=": 
       InnerLambda = Expression.Equal(left, right); 
       break; 
      case "<": 
       InnerLambda = Expression.LessThan(left, right); 
       break; 
      case ">": 
       InnerLambda = Expression.GreaterThan(left, right); 
       break; 
      case ">=": 
       InnerLambda = Expression.GreaterThanOrEqual(left, right); 
       break; 
      case "<=": 
       InnerLambda = Expression.LessThanOrEqual(left, right); 
       break; 
      case "!=": 
       InnerLambda = Expression.NotEqual(left, right); 
       break; 
      case "&&": 
       InnerLambda = Expression.And(left, right); 
       break; 
      case "||": 
       InnerLambda = Expression.Or(left, right); 
       break; 
      case "LIKE": 
       InnerLambda = Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right); 
       break; 
      case "NOTLIKE": 
       InnerLambda = Expression.Not(Expression.Call(left, typeof(string).GetMethod("Contains", new Type[] { typeof(string) }), right)); 
       break; 
     } 
     return InnerLambda; 
    } 

    public static Expression<Func<T, object>> PropExpr<T>(string PropName) 
    { 
     ParameterExpression paramExpr = Expression.Parameter(typeof(T)); 
     var tempExpr = Extentions.NestedExprProp(paramExpr, PropName); 
     return Expression.Lambda<Func<T, object>>(Expression.Convert(Expression.Lambda(tempExpr, paramExpr).Body, typeof(object)), paramExpr); 

    } 
    public static IQueryOver<T, T> OrderBy<T>(this IQueryOver<T, T> Collection, string sidx, string sord) 
    { 
     return sord == "asc" ? Collection.OrderBy(NHibernate.Criterion.Projections.Property(sidx)).Asc : Collection.OrderBy(NHibernate.Criterion.Projections.Property(sidx)).Desc; 
    } 

    public static Expression<Func<T, TResult>> And<T, TResult>(this Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2) 
    { 
     var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>()); 
     return Expression.Lambda<Func<T, TResult>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters); 
    } 

    public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2) 
    { 
     var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>()); 
     return Expression.Lambda<Func<T, bool>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters); 
    }