2009-10-31 6 views
4

Comment écrire une méthode linq dynamique pour la clause Like. Pour référence, il y a Dynamic LINQ OrderBy on IEnumerable<T>. Je suis à la recherche d'un semblable pour la clause dynamique Like.Dynamic LINQ Like

je les méthodes d'extension suivantes comme:

public static IQueryable<T> Like<T>(this IQueryable<T> source, string propertyName, 
            string keyword) 
{ 
    var type = typeof(T); 
    var property = type.GetProperty(propertyName); 
    var parameter = Expression.Parameter(type, "p"); 
    var propertyAccess = Expression.MakeMemberAccess(parameter, property); 
    var constant = Expression.Constant("%" + keyword + "%"); 
    var methodExp = Expression.Call(
     null, 
     typeof(SqlMethods).GetMethod("Like", new[] { typeof(string), typeof(string) }), 
     propertyAccess, 
     constant); 
    var lambda = Expression.Lambda<Func<T, bool>>(methodExp, parameter); 
    return source.Where(lambda); 
} 

La méthode ci-dessus donne une erreur

Method 'Boolean Like(System.String, System.String)' cannot be used on the client; it is only for translation to SQL.

L'autre méthode qui est en quelque sorte modifié à partir Dynamic LINQ OrderBy on IEnumerable<T>:

public static IQueryable<T> ALike<T>(this IQueryable<T> source, string property, 
            string keyword) 
{ 
    string[] props = property.Split('.'); 
    Type type = typeof(T); 
    ParameterExpression arg = Expression.Parameter(type, "x"); 
    Expression expr = arg; 

    foreach (string prop in props) 
    { 
     // use reflection (not ComponentModel) to mirror LINQ 
     PropertyInfo pi = type.GetProperty(prop); 
     expr = Expression.Property(expr, pi); 
     type = pi.PropertyType; 
    } 
    var constant = Expression.Constant("%" + keyword + "%"); 
    var methodExp = Expression.Call(
     null, 
     typeof(SqlMethods).GetMethod("Like", new[] { typeof(string), typeof(string) }), 
     expr, 
     constant); 
    Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type); 
    LambdaExpression lambda = Expression.Lambda(delegateType, methodExp, arg); 
    object result = typeof(Queryable).GetMethods().Single(
      method => method.IsGenericMethodDefinition 
        && method.GetGenericArguments().Length == 2 
        && method.GetParameters().Length == 2) 
      .MakeGenericMethod(typeof(T), type) 
      .Invoke(null, new object[] { source, lambda }); 
    return (IQueryable<T>)result; 
} 

La méthode ci-dessus donne une erreur:

Expression of type 'System.Boolean' cannot be used for return type 'System.String'

Une idée à ce sujet?

+1

http://stackoverflow.com/a/2456070/1193727 – resnyanskiy

Répondre

3

Connaissez-vous SqlMethods.Like?

+0

Cela donne la même erreur mentionné ci-dessus: « Boolean Comme méthode » (System.String, System.String) 'ne peut pas être utilisé sur le client, mais seulement pour la traduction vers SQL. " –

+0

@Ryan Elkins, vous voulez la fonctionnalité de "j'aime", mais vous ne voulez pas appeler un serveur de base de données? –

6

Quelque chose comme:

static void Main() { 
    using(var ctx= new DataClasses1DataContext()) { 
     ctx.Log = Console.Out; 
     var qry = ctx.Customers.WhereLike("CompanyName", "a%s"); 

     Console.WriteLine(qry.Count()); 
    } 
} 
static IQueryable<T> WhereLike<T>(this IQueryable<T> source, 
     string propertyOrFieldName, string pattern) { 
    var param = Expression.Parameter(typeof(T), "row"); 
    var body = Expression.Call(
     null, 
     typeof(SqlMethods).GetMethod("Like", 
      new[] { typeof(string), typeof(string) }), 
     Expression.PropertyOrField(param, propertyOrFieldName), 
     Expression.Constant(pattern, typeof(string))); 
    var lambda = Expression.Lambda<Func<T, bool>>(body, param); 
    return source.Where(lambda); 
} 
static IQueryable<T> WhereLike<T>(this IQueryable<T> source, 
     string propertyOrFieldName, string pattern, char escapeCharacter) { 
    var param = Expression.Parameter(typeof(T), "row"); 
    var body = Expression.Call(
     null, 
     typeof(SqlMethods).GetMethod("Like", 
      new[] { typeof(string), typeof(string), typeof(char) }), 
     Expression.PropertyOrField(param, propertyOrFieldName), 
     Expression.Constant(pattern, typeof(string)), 
     Expression.Constant(escapeCharacter,typeof(char))); 
    var lambda = Expression.Lambda<Func<T, bool>>(body, param); 
    return source.Where(lambda); 
} 

Vous pouvez également envisager de plus réutilisable:

static void Main() { 
    using(var ctx= new DataClasses1DataContext()) { 
     ctx.Log = Console.Out; 
     var qry1 = ctx.Customers.WhereInvoke<Customer, string>(
      "CompanyName", s => s.Contains("abc")); 
     Console.WriteLine(qry1.Count()); 

     var qry2 = ctx.Customers.WhereInvoke<Customer, string>(
      "CompanyName", s => s.StartsWith("abc")); 
     Console.WriteLine(qry2.Count()); 

     var qry3 = ctx.Customers.WhereInvoke<Customer, string>(
      "CompanyName", s => s.EndsWith("abc")); 
     Console.WriteLine(qry3.Count()); 
    } 
} 
static IQueryable<TSource> WhereInvoke<TSource, TValue>(
     this IQueryable<TSource> source, 
     string propertyOrFieldName, 
     Expression<Func<TValue, bool>> func) { 
    var param = Expression.Parameter(typeof(TSource), "row"); 
    var prop = Expression.PropertyOrField(param, propertyOrFieldName); 
    if(prop.Type != typeof(TValue)) { 
     throw new InvalidOperationException("The property must be " + typeof(TValue).Name); 
    } 
    var body = Expression.Invoke(func, prop); 
    var lambda = Expression.Lambda<Func<TSource, bool>>(body, param); 
    return source.Where(lambda); 
} 
+0

Les méthodes ci-dessus donnent également l'erreur: La méthode 'Boolean Like (System.String, System.String)' ne peut pas être utilisée sur le client; c'est seulement pour la traduction en SQL. – Prasad

+0

Ah ... vous n'avez pas clarifié votre source. Il n'y a pas d'équivalent "LIKE" vraiment pratique à moins que vous ne référenciez les librairies VB, et cela fonctionnerait contre les fournisseurs de DB - est-ce que le Contains/StartsWith/EndsWith n'aide pas? –

+0

Contient/Démarre/Endeavec également des erreurs. Vous avez donné une vraie bonne solution pour l'extension Orderby que je cherchais depuis longtemps. La beauté de ce code gère aussi les propriétés imbriquées. J'essaie de similaire pour 'Like' ... – Prasad

0

eu le même problème que vous. SqlMethods.Like ne fonctionne que lors de l'exécution sur un serveur SQL, pas sur des collections de mémoire. J'ai donc fait un évaluateur Comme cela fonctionnera, sur les collections - voir mon blog here