带有“OR”子句的通用表达式树,用于每个提供的属性

我为IQueryable创建了一个通用的搜索扩展方法,使您可以搜索单个属性以查看其中是否包含搜索词。

http://jnye.co/Posts/6/c%23-generic-search-extension-method-for-iqueryable

我现在想让用户选择多个属性在每个属性中进行搜索,如果任何属性包含文本,则匹配。

代码:

用户输入以下代码以执行此搜索:

string searchTerm = "Essex"; context.Clubs.Search(searchTerm, club => club.Name, club => club.County) //Note: If possible I would rather something closer to the following syntax... context.Clubs.Search(club => new[]{ club.Name, club.County}, searchTerm); // ... or, even better, something similar to this... context.Clubs.Search(club => new { club.Name, club.County}, searchTerm); 

这将返回任何名为“Essex”的高尔夫俱乐部或郡。

  public static IQueryable Search(this IQueryable source, string searchTerm, params Expression<Func>[] stringProperties) { if (String.IsNullOrEmpty(searchTerm)) { return source; } // The lamda I would like to reproduce: // source.Where(x => x.[property1].Contains(searchTerm) // || x.[property2].Contains(searchTerm) // || x.[property3].Contains(searchTerm)...) //Create expression to represent x.[property1].Contains(searchTerm) var searchTermExpression = Expression.Constant(searchTerm); //Build parameters var parameters = stringProperties.SelectMany(prop => prop.Parameters); Expression orExpression = null; //Build a contains expression for each property foreach (var stringProperty in stringProperties) { var checkContainsExpression = Expression.Call(stringProperty.Body, typeof(string).GetMethod("Contains"), searchTermExpression); if (orExpression == null) { orExpression = checkContainsExpression; } //Build or expression for each property orExpression = Expression.OrElse(orExpression, checkContainsExpression); } var methodCallExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { source.ElementType }, source.Expression, Expression.Lambda<Func>(orExpression, parameters)); return source.Provider.CreateQuery(methodCallExpression); } 

错误

错误

如果我将提供的参数数量更改为1:

 Expression.Lambda<Func>(orExpression, parameters.First())); 

我收到一个新错误:

第二个错误

UPDATE

我已经写了一篇关于这个问题讨论的工作的post 。 也可以在GitHub上查看 。

开始了; 你非常接近 – 正如我在评论中指出的那样,这里的关键部分是使用ExpressionVisitor根据你想要保留的单个参数重新编写树:

 using System; using System.Linq; using System.Linq.Expressions; static class Program { static void Main() { var data = new[] { new Foo { A = "x1", B = "y1", C = "y1" }, new Foo { A = "y2", B = "y2", C = "y2" }, new Foo { A = "y3", B = "y3", C = "x3" } }.AsQueryable(); var result = data.Search("x", x => xA, x => xB, x => xC); foreach (var row in result) { Console.WriteLine("{0}, {1}, {2}", row.A, row.B, row.C); } } class Foo { public string A { get; set; } public string B { get; set; } public string C { get; set; } } public class SwapVisitor : ExpressionVisitor { private readonly Expression from, to; public SwapVisitor(Expression from, Expression to) { this.from = from; this.to = to; } public override Expression Visit(Expression node) { return node == from ? to : base.Visit(node); } public static Expression Swap(Expression body, Expression from, Expression to) { return new SwapVisitor(from, to).Visit(body); } } public static IQueryable Search(this IQueryable source, string searchTerm, params Expression>[] stringProperties) { if (String.IsNullOrEmpty(searchTerm)) { return source; } if (stringProperties.Length == 0) return source.Where(x => false); // The lamda I would like to reproduce: // source.Where(x => x.[property1].Contains(searchTerm) // || x.[property2].Contains(searchTerm) // || x.[property3].Contains(searchTerm)...) //Create expression to represent x.[property1].Contains(searchTerm) var searchTermExpression = Expression.Constant(searchTerm); var param = stringProperties[0].Parameters.Single(); Expression orExpression = null; //Build a contains expression for each property foreach (var stringProperty in stringProperties) { // re-write the property using the param we want to keep var body = SwapVisitor.Swap(stringProperty.Body, stringProperty.Parameters.Single(), param); var checkContainsExpression = Expression.Call( body, typeof(string).GetMethod("Contains"), searchTermExpression); if (orExpression == null) { orExpression = checkContainsExpression; } else { // compose orExpression = Expression.OrElse(orExpression, checkContainsExpression); } } var lambda = Expression.Lambda>(orExpression, param); return source.Where(lambda); } }