2017-05-20 3 views
0

J'ai IQueryable type de collecte de données, j'ai fait abstraction d'une méthode, utilisée comme une requête floue plein champ, au début, j'ai été un par un nom d'attribut à écrire. Comme le code suivant:Comment créer une arborescence d'expression pour représenter 'p => p.a.tostring(). Contient ("s")' en C#?

private IQueryable<Tables1> FilterResult(string search, List<Tables1> dtResult) 
    { 
     IQueryable<Tables1> results = dtResult.AsQueryable(); 
     results = results.Where(p => (
      search == null || (
       p.Name != null && p.Name.Contains(search) || 
       p.age != null && p.age.ToString().Contains(search) || 
       p.sex != null && p.sex.Contains(search) || 
       p.content1 != null && p.content1.Contains(search) || 
       p.content2 != null && p.content2.Contains(search) || 
       p.content3 != null && p.content3.Contains(search) 
       ) 
      )); 
     return results; 
    } 

Mais cette écriture si le type de changements de collection Liste de réception, tous les attributs physiques doivent ré-écriture. J'ai donc changé le type de T:

private IQueryable<T> FilterResult(string search, List<T> dtResult,T t) 
    { 
     IQueryable<T> results = dtResult.AsQueryable(); 
     //do something 
     return results; 
    } 

L'idée derrière ceci est d'obtenir tous les attributs du type T entrant par réflexion. Puis construisez l'expression Lambda à travers l'arbre d'expression.

La question est de savoir comment construire un 'p => p.age.ToString() .Contains (search)' par Expression Tree?

Ce qui suit est le code complet:

private IQueryable<T> FilterResult(string search, List<T> dtResult, T t) 
    { 
     List<Expression> tempExp = new List<Expression>(); 
     var parameter = Expression.Parameter(typeof(T), "p"); 
     foreach (var mi in t.GetType().GetProperties()) 
     { 
      Expression left = Expression.Property(parameter, t.GetType().GetProperty(mi.Name)); 
      Expression right = Expression.Constant(search, typeof(string)); 
      MethodInfo method; 
      MethodCallExpression exp; 
      if (mi.PropertyType == typeof(Int32) || mi.PropertyType == typeof(Int64)) 
      { 
       //this code is wrong 
       var exp1 = Expression.Call(Expression.Convert(left, typeof(string)), typeof(object).GetMethod("ToString")); 
       method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); 
       exp = Expression.Call(exp1, method, right); 

      } 
      else 
      { 
       method = typeof(string).GetMethod("Contains", new[] { typeof(string) }); 
       exp = Expression.Call(left, method, right); 
      } 
      tempExp.Add(exp); 
     } 
     Expression all = Expression.Or(Expression.Equal(Expression.Constant(search), null), tempExp[0]); 
     for (int i = 1; i < tempExp.Count; i++) 
     { 
      all = Expression.Or(all, tempExp[i]); 
     } 
     var lambda = Expression.Lambda<Func<T, bool>>(all, parameter); 
     var results = dtResult.Where(lambda.Compile()).AsQueryable(); ; 
     return results; 
    } 
+0

pourquoi vous retournez un IQueryable? – MBoros

Répondre

0

Je dirais que cela peut être plus facile. J'ai simplifié votre modèle Tables pour la présentation.

public class Tables 
{ 
    public int Age { get; set; } 
    public string Name { get; set; } 
    public string Content { get; set; } 
} 

public class Matcher 
{ 
    private static readonly PropertyInfo[] Properties = typeof(Tables).GetRuntimeProperties().ToArray(); 

    public IQueryable<Tables> FilterResult(string search, List<Tables> dtResult) 
    { 
     if(search == null) //Consider using string.IsNullOrWhiteSpace(search) but I wasn't sure if you want to avoid searching for spaces 
     { 
      return dtResult.AsQueryable(); 
     } 
     return dtResult.Where(p => IsMatch(p, search)).AsQueryable(); 
    } 

    private static bool IsMatch(Tables tables, string search) 
    { 
     foreach (var propertyInfo in Properties) 
     { 
      var value = propertyInfo.GetValue(tables); 
      if (value != null && value.ToString().Contains(search)) 
      { 
       return true; 
      } 
     } 

     return false; 
    } 
} 

Et nous pouvons le mettre en œuvre:

class Program 
{ 
    public static void Main() 
    { 
     const string search = "Bob"; 
     var matcher = new Matcher(); 
     var items = new List<Tables> 
     { 
      new Tables {Content = string.Empty, Name = "Bob"}, //This will match 
      new Tables {Content = "Bob is the best guy.", Name = "Joe"}, //This will also match 
      new Tables {Content = "Something", Name = null} // This won't null name to verify that nothing unexpected will happen 
     }; 

     var results = matcher.FilterResult(search, items); 
     foreach (var result in results) 
     { 
      Console.WriteLine($"Matched the guy named {result.Name}"); 
     } 
     Console.ReadKey(); 
    } 
} 

Edit: Voici la version générique

public class Matcher<T> 
{ 
    private static readonly PropertyInfo[] Properties = typeof(T).GetRuntimeProperties().ToArray(); 

    public IQueryable<T> FilterResult(string search, List<T> items) 
    { 
     if (search == null) //Consider using string.IsNullOrWhiteSpace(search) but I wasn't sure if you want to avoid searching for spaces 
     { 
      return items.AsQueryable(); 
     } 

     return items.Where(p => IsMatch(p, search)).AsQueryable(); 
    } 

    private static bool IsMatch(T item, string search) 
    { 
     foreach (var propertyInfo in Properties) 
     { 
      var value = propertyInfo.GetValue(item); 
      if (value != null && value.ToString().Contains(search)) 
      { 
       return true; 
      } 
     } 

     return false; 
    } 
} 
+0

Merci beaucoup pour la réponse, le résultat de la course est ce que je veux. Peut-être que mon esprit est trop rigide et merci beaucoup – SmallProgram