加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

c# – 每个提供的属性带有’OR’子句的通用表达式树

发布时间:2020-12-15 05:37:22 所属栏目:百科 来源:网络整理
导读:我为IQueryable创建了一个通用搜索扩展方法,使您可以搜索单个属性以查看其中是否包含搜索词. http://jnye.co/Posts/6/c%23-generic-search-extension-method-for-iqueryable 我现在想让用户选择多个属性来搜索每个属性,匹配是否有任何属性包含文本. 代码:
我为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,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);
    }

错误

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

Expression.Lambda<Func<TSource,parameters.First()));

我收到一个新错误:

UPDATE

我也写了a post on the work discussed in this question. Check it out on 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 => 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>>[] 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,searchTermExpression);

            if (orExpression == null)
            {
                orExpression = checkContainsExpression;
            }
            else
            {   // compose
                orExpression = Expression.OrElse(orExpression,checkContainsExpression);
            }
        }

        var lambda = Expression.Lambda<Func<TSource,param);
        return source.Where(lambda);
    }
}

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读