2013-09-01 6 views
0

Je suis en train de créer un générateur d'expression dynamique et d'essayer d'implémenter la fonction 'like'. Avant d'écrire le mien, j'ai recherché toute fonction existante et j'en ai trouvé une proche de mon besoin. Après plusieurs expériences, je ne pouvais pas l'amener à courir pour d'autres types de cordes.Entity Framework - requête dynamique

Quand je passe un paramètre de type int puis-je obtenir cette erreur:

Method 'System.String ToString()' declared on type 'System.String' cannot be called with instance of type 'System.Int32'

Mon code ressemble à ceci:

private static MethodCallExpression GetLowerCasePropertyAccess(MemberExpression propertyAccess) 
{ 
    //return Expression.Call(Expression.Call(propertyAccess, "ToString", new Type[0]), typeof(string).GetMethod("ToLower", new Type[0])); 
    return Expression.Call(Expression.Call(propertyAccess, typeof(string).GetMethod("ToString", System.Type.EmptyTypes)), typeof(string).GetMethod("ToLower", System.Type.EmptyTypes)); 
} 

private static readonly MethodInfo ContainsMethod = typeof(String).GetMethod("Contains", new Type[] { typeof(String) }); 

public static Expression<Func<T, bool>> Create<T>(string propertyName, ComparisonOperators comparisonOperator, dynamic comparedValue1, dynamic comparedValue2 = null) 
{ 
    ParameterExpression parameterExpression = Expression.Parameter(typeof(T), "x"); 
    MemberExpression memberExpression = Expression.MakeMemberAccess(parameterExpression, typeof(T).GetProperty(propertyName)); 
    ConstantExpression constantExpression = Expression.Constant(comparedValue1, comparedValue1.GetType()); 
    Expression expressionBody = null; 

    switch (comparisonOperator) 
    { 
     ... 

     case ComparisonOperators.Contains: 
      //var indexOf = Expression.Call(memberExpression, "IndexOf", null, Expression.Constant(comparedValue1, typeof(string)), Expression.Constant(StringComparison.InvariantCultureIgnoreCase)); 
      //expressionBody = Expression.GreaterThanOrEqual(indexOf, Expression.Constant(0)); 
      expressionBody = Expression.Call(GetLowerCasePropertyAccess(memberExpression), ContainsMethod, Expression.Constant(comparedValue1.ToLower())); 
      break; 
    } 

    return Expression.Lambda<Func<T, bool>>(expressionBody, new ParameterExpression[] { parameterExpression }); 
} 

Répondre

0

Je suis sûr que je comprends pas bien ce que vous êtes faire, mais je pense que votre erreur est causée par cette ligne:

Qui va toujours essayer un nd appelez la méthode ToString pour un type string, donc si vous essayez d'utiliser une propriété Int32, vous essayez alors d'appeler String.ToString(), puisque l'implémentation de ToString() sera différente pour les différents types et les deux implémentations ne seront pas forcément compatibles, vous aurez l'exception que vous voyez:

Method 'System.String ToString()' declared on type 'System.String' cannot be called with instance of type 'System.Int32' 

De quoi il ressemble que vous faites, je pense que cela peut être ce que vous êtes après:

return Expression.Call(Expression.Call(propertyAccess, propertyAccess.Type.GetMethod("ToString", System.Type.EmptyTypes)), typeof(string).GetMethod("ToLower", System.Type.EmptyTypes)); 

qui utilisera le mise en œuvre correcte de ToString (avec le type obta ined from propertyAccess.Type).

+0

J'ai essayé votre solution, mais maintenant j'obtenir une nouvelle erreur: System.NotSupportedException: LINQ to Entities ne reconnaît pas la méthode 'System.String ToString()' méthode, et cette méthode ne peut pas être traduite dans une expression de magasin. – sysboard

0

Linq aux entités ne prend pas en charge la méthode .ToString. Pour convertir des valeurs numériques en chaîne, vous devez utiliser la méthode SqlFunctions.StringConvert. J'ai fixé votre code et maintenant vous pouvez faire like sur la corde et les colonnes numériques:

private static Expression GetConvertToStringExpression(Expression e) 
{ 
    // if property string - no cast needed 
    // else - use SqlFunction.StringConvert(double?) or SqlFunction.StringConvert(decimal?); 
    Expression strExpression = null; 
    if (e.Type == typeof(string)) 
     strExpression = e; 

    var systemType = Nullable.GetUnderlyingType(e.Type) ?? e.Type; 

    if (systemType == typeof(int) 
     || systemType == typeof(long) 
     || systemType == typeof(double) 
     || systemType == typeof(short) 
     || systemType == typeof(byte)) // continue 
    { 
     // cast int to double 
     var doubleExpr = Expression.Convert(e, typeof (double?)); 
     strExpression = Expression.Call(StringConvertMethodDouble, doubleExpr); 
    } 

    if (systemType == typeof (decimal)) 
    { 
     // call decimal version of StringConvert method 
     // cast to nullable decimal 
     var decimalExpr = Expression.Convert(e, typeof (decimal?)); 
     strExpression = Expression.Call(StringConvertMethodDecimal, decimalExpr); 
    } 

    return strExpression; 
} 

private static MethodCallExpression GetLowerCasePropertyAccess(Expression propertyAccess) 
{ 
    var stringExpression = GetConvertToStringExpression(propertyAccess); 
    if (stringExpression == null) 
     throw new Exception(string.Format("Not supported property type {0}", propertyAccess.Type)); 

    return Expression.Call(stringExpression, 
     typeof (string).GetMethod("ToLower", Type.EmptyTypes)); 
} 

private static readonly MethodInfo StringConvertMethodDouble = typeof (SqlFunctions).GetMethod("StringConvert", 
    new Type[] {typeof (double?)}); 
private static readonly MethodInfo StringConvertMethodDecimal = typeof(SqlFunctions).GetMethod("StringConvert", 
    new Type[] { typeof(decimal?) }); 
+0

Salut Désolé mais je dois supprimer la réponse de marque. Je reçois toujours une erreur lors des tests. D'une certaine manière, la fonction 'GetConvertToStringExpression' ne fonctionne toujours pas. L'erreur que je reçois: 'int n'a pas de définition pour ToLower' – sysboard

Questions connexes