2011-08-25 4 views
1

J'essaie de convertir un DateTime en une chaîne avant de l'appeler contient. Cependant, malgré mes efforts pour mettre le résultat d'une expression dans une autre, j'échoue misérablement.Comment imbriquer un appel à une méthode dans un appel à une méthode à l'aide Expression.Call

Le code est dérivé de la réponse la plus élevée à ce question jqgrid avec asp.net webmethod et json travaillant avec le tri, la pagination, la recherche et LINQ - mais nécessite des opérateurs dynamiques.

Supposons que je le procédé suivant comme StringExtension du question:

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; 
    } 
} 

J'ai donc la méthode suivante aussi de la question que je puis adapté pour DateTime.

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); 
    } 

Je reçois l'exception suivante.

LINQ to Entities ne reconnaît pas la méthode System.String ToString (System.String) de méthode, et cette méthode ne peut pas être traduite dans une expression de magasin.

+1

Pourquoi pas? Ce qui se produit? – SLaks

+0

Qu'est-ce que 'propertyAccess'? –

Répondre

2

Je suspect vous voulez quelque chose comme ceci (fixe de la version précédente):

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); 
    } 
} 

Notez que les valeurs « nulles » doivent montrer qu'il est un appel de méthode non générique.

+0

Cela ressemble à la réponse mais avant de la marquer, pourriez-vous clarifier ce que le D dans new [] {Expression.Constant ("D")}. Je suppose que c'est le formatage pour l'objet DateTime. Deuxièmement, le paramètre new [] {Expression.Constant ("D")} peut-il être nul, vide ou manquant? Alternativement, Expression.Constant ("D") pourrait être Expression.Constant ("") ou Expression.Constant (null)? – Neil

+0

@Neil: C'est la chaîne de format "date longue". J'ai supposé que vous vouliez formater 'DateTime' avec le format long, étant donné le nom de l'appel de la méthode. Oui, il peut s'agir d'un tableau vide ou (éventuellement) null - pas sûr sur ce dernier front. Utiliser Expression.Constant ("") ou Expression.Constant (null) équivaudrait à appeler 'Foo.ToString (" ")' ou 'Foo.ToString (null)'. (Vous devez probablement dire à Expression.Constant quel type vous voulez que l'expression ait aussi.) –

+0

Nah, ne fonctionne toujours pas, veuillez voir ma réponse plus complète et révisée. – Neil

0

essayer ce code .....

Appel ToExpression (...) ... Ex. ToExpression (null, Product.Name, "==", "Test"); Ici. Product.Name est une propriété imbriquée.

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); 
    } 
Questions connexes