C# 带有';或';各供应财产的条款

C# 带有';或';各供应财产的条款,c#,generics,lambda,expression-trees,C#,Generics,Lambda,Expression Trees,我已经为IQueryable创建了一个通用的搜索扩展方法,使您能够搜索单个属性,以查看其中是否包含搜索词 现在,我希望用户能够选择多个属性在每个属性中进行搜索,如果任何属性包含文本,则进行匹配 代码: 用户输入以下代码以执行此搜索: string searchTerm = "Essex"; context.Clubs.Search(searchTerm, club => club.Name, club => club.County) //Note: If possible I w

我已经为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<TSource> Search<TSource>(this IQueryable<TSource> source, string searchTerm, params Expression<Func<TSource, string>>[] 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<TSource, bool>>(orExpression, parameters));

        return source.Provider.CreateQuery<TSource>(methodCallExpression);
    }
公共静态IQueryable搜索(此IQueryable源、字符串搜索项、参数表达式[]stringProperties)
{
if(String.IsNullOrEmpty(searchTerm))
{
返回源;
}
//我想复制的lamda:
//其中(x=>x.[property1]。包含(searchTerm)
//| | x[property2]。包含(搜索术语)
//| | x[property3]。包含(搜索项)
//创建表示x的表达式[property1]。包含(searchTerm)
var searchTermExpression=Expression.Constant(searchTerm);
//构建参数
var parameters=stringProperties.SelectMany(prop=>prop.parameters);
表达式orExpression=null;
//为每个属性生成一个包含表达式
foreach(stringProperties中的var stringProperty)
{
var checkContainsExpression=Expression.Call(stringProperty.Body,typeof(string).GetMethod(“包含”),searchTermExpression);
if(orExpression==null)
{
orExpression=CheckContainesPression;
}
//每个属性的生成或表达式
orExpression=Expression.OrElse(orExpression,checkContainsExpression);
}
var methodCallExpression=Expression.Call(typeof(Queryable),
“哪里”,
新类型[]{source.ElementType},
来源.表达式,
Lambda(orExpression,parameters));
返回source.Provider.CreateQuery(methodCallExpression);
}
错误

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

Expression.Lambda<Func<TSource, bool>>(orExpression, parameters.First()));
Expression.Lambda(orExpression,parameters.First());
我得到一个新错误:

更新


我已经写信了。也是。

我们开始了;您非常接近-正如我在评论中指出的,这里的关键是使用
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 => x.A, x => x.B, x => x.C);

        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<TSource> Search<TSource>(this IQueryable<TSource> source, string searchTerm, params Expression<Func<TSource, string>>[] 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<Func<TSource, bool>>(orExpression, param);
        return source.Where(lambda);
    }
}
使用系统;
使用System.Linq;
使用System.Linq.Expressions;
静态类程序
{
静态void Main()
{
var data=new[]{new Foo{A=“x1”,B=“y1”,C=“y1”},new Foo{A=“y2”,B=“y2”,C=“y2”},
新的Foo{A=“y3”,B=“y3”,C=“x3”}.AsQueryable();
var result=data.Search(“x”,x=>x.A,x=>x.B,x=>x.C);
foreach(结果中的var行)
{
WriteLine(“{0},{1},{2}”,行A,行B,行C);
}
}
福班
{
公共字符串A{get;set;}
公共字符串B{get;set;}
公共字符串C{get;set;}
}
公共类交换访问者:ExpressionVisitor
{
私有只读表达式from、to;
公共SwapVisitor(表达式from,表达式to)
{
this.from=from;
这个;
}
公共重写表达式访问(表达式节点)
{
返回节点==从?到:基本访问(节点);
}
公共静态表达式交换(表达式主体、表达式发件人、表达式收件人)
{
返回新的SwapVisitor(从,到)。访问(身体);
}
}
公共静态IQueryable搜索(此IQueryable源、字符串searchTerm、参数表达式[]stringProperties)
{
if(String.IsNullOrEmpty(searchTerm))
{
返回源;
}
if(stringProperties.Length==0)返回source.Where(x=>false);
//我想复制的lamda:
//其中(x=>x.[property1]。包含(searchTerm)
//| | x[property2]。包含(搜索术语)
//| | x[property3]。包含(搜索项)
//创建表示x的表达式[property1]。包含(searchTerm)
var searchTermExpression=Expression.Constant(searchTerm);
var param=stringProperties[0]。参数。Single();
表达式orExpression=null;
//为每个属性生成一个包含表达式
foreach(stringProperties中的var stringProperty)
{
//使用要保留的参数重新写入属性
var body=SwapVisitor.Swap(stringProperty.body,stringProperty.Parameters.Single(),param);
var checkContainesPression=Expression.Call(
主体,typeof(string).GetMethod(“包含”),searchTermExpression);
if(orExpression==null)
{
orExpression=CheckContainesPression;
}
其他的
{//compose
orExpression=Expression.OrElse(orExpression,checkContainsExpression);
}
}
var lambda=Expression.lambda(orExpression,param);
返回源。其中(λ);
}
}

为此,您需要重新写入树,将“其他”lambda中的参数替换为要用作主节点的参数。ExpressionVisitor是实现这一点的工具-SwapExpressionVisitor非常容易编写(或使用此处的方法),更改方法签名以便只传入一个lambda会更容易吗?i、 e.
.Search(俱乐部=>新建){