2017-07-07 3 views
1

J'essaye de créer une extension WhereLike à IQueryable mais je ne peux pas connaître le type de la propriété à l'exécution.Extension IQueryable WhereLike avec le type de propriété inconnu

Voici mon code:

public static IQueryable WhereLike(this IQueryable source, string propertyName, string pattern) 
    { 
     if (source == null) throw new ArgumentNullException("source"); 
     if (propertyName == null) throw new ArgumentNullException("propertyName"); 

     var a = Expression.Parameter(typeof(object), "a"); 
     var prop = Expression.Property(a, propertyName); 

     return source.Provider.CreateQuery(
      Expression.Call(
       typeof(SqlMethods), "Like", 
       null, 
       prop, Expression.Constant(pattern))); 
    } 

Je reçois l'exception: la propriété instance 'foo' est pas défini pour le type 'System.Object'

Connaissez-vous un moyen de gérer paramètre de propriété sans Connaître le type de cible au moment de la compilation?

+0

ne pas le SQL comme opérateur ne fonctionne sur les chaînes? – Fran

+0

@Fran Pourquoi est-ce important? – Servy

+0

@Servy parce que s'il ne peut pas déterminer le type de la propriété, il pourrait essayer d'utiliser le WhereLike sur une propriété qui ne le supporte pas (DateTime, Double, ....) alors il obtiendrait une exception d'exécution quand ça arrive. Peut-être essaie-t-il de saisir le ElementType source et de rechercher le type de propriété en utilisant la réflexion. – Fran

Répondre

1

Si vous êtes en mesure d'utiliser le IQueryable<T> générique variante, cela devient un problème beaucoup plus facile puisque vous ne avez plus besoin CreateQuery et vous pouvez exécuter directement contre la source IQueryable<T>.

public static IQueryable<T> WhereLike<T>(this IQueryable<T> source, string propertyName, 
    string pattern) 
{ 
    if (source == null) throw new ArgumentNullException("source"); 
    if (propertyName == null) throw new ArgumentNullException("propertyName"); 

    var a = Expression.Parameter(typeof(T), "a"); 
    var prop = Expression.PropertyOrField(a, propertyName); 

    var expr = Expression.Call(
      typeof(SqlMethods), "Like", 
      null, 
      prop, Expression.Constant(pattern)); 

    var lambda = Expression.Lambda<Func<T, bool>>(expr, a); 
    return source.Where(lambda); 
} 

Remarque deux points clés:

au lieu de propriétés que l'accaparement, si nous utilisons PropertyOrField nous pouvons soutenir correctement le code généré pour Linq-2-SQL qui peuvent exposerons les champs.

En outre, puisque nous exécutons contre la source IQueryable<T>, nous devons créer une expression lambda à partir des résultats de notre "Like" MethodCallExpression.


Si vous avez besoin de la variante non-générique, vous pouvez accomplir toujours la même chose, même si vous avez besoin d'envelopper votre Comme MethodCallExpression dans un cas MethodCallExpression afin qu'il soit bien structuré:

public static IQueryable WhereLike(this IQueryable source, string propertyName, 
    string pattern) 
{ 
    if (source == null) throw new ArgumentNullException("source"); 
    if (propertyName == null) throw new ArgumentNullException("propertyName"); 

    var a = Expression.Parameter(source.GetType().GetGenericArguments().First(), "a"); 
    var prop = Expression.PropertyOrField(a, propertyName); 

    var expr = Expression.Call(
      typeof(SqlMethods), "Like", 
      null, 
      prop, Expression.Constant(pattern)); 

    MethodCallExpression whereCallExpression = Expression.Call(
      typeof(Queryable), 
      "Where", 
      new Type[] { source.ElementType }, 
      source.Expression, 
      Expression.Lambda(expr, a)); 

    return source.Provider.CreateQuery(whereCallExpression); 
} 

Vous pouvez appeler les deux variantes avec des jokers:

var data = source.WhereLike("ColumnName", "%o%"); 
-1

Détermine si une chaîne de caractères spécifique correspond à un modèle spécifié. Un motif peut inclure des caractères réguliers et des caractères génériques. Pendant la correspondance de modèle, les caractères réguliers doivent correspondre exactement aux caractères spécifiés dans la chaîne de caractères. Cependant, les caractères génériques peuvent être associés à des fragments arbitraires de la chaîne de caractères. L'utilisation de caractères génériques rend l'opérateur LIKE plus flexible que l'utilisation des opérateurs de comparaison de chaînes = et! =. Si l'un des arguments n'est pas du type de données de chaîne de caractères, le moteur de base de données SQL Server le convertit en type de données de chaîne de caractères, si cela est possible. MSDN

Comme l'opérateur travaillera uniquement sur le type string. Si c'est ce que vous vouliez faire, vous pouvez réaliser avec la méthode Contains seulement, il y a aussi StartsWith et EndsWith équivalent.

Vous pouvez utiliser Où seule méthode dans cette méthode d'extension

var containsParam = Expression.Parameter(typeof(T), "p"); 
MemberExpression multiSelectmember = Expression.Property(containsParam,propertyname); 
    var lstValues = stringvalue; 

ConstantExpression multiSelectConstant = Expression.Constant(stringvalue); 
var callExpression = Expression.Call(typeof(String), "Contains", 
new[] { typeof(string) }, multiSelectConstant, multiSelectmember); 
var containsexp = Expression.Lambda<Func<T, bool>>(callExpression, 
               containsParam); 
+0

J'ai aussi essayé avec StartsWith, EndsWith, etc ... mais avec des patterns complexes ("% AA_BB% CC%"), cette solution semble limitée – eli0tt