C# Linq、Lambda表达式树动态构建、合并条件扩展方法

前言

日常开发时,使用Linq和EF经常会在存在多条件查询,或者说动态条件查询时,便存在合并表达式树的情况。基于这种情况结合一些资料,写了个扩展类,代码如下:

代码实现

    /// <summary>
    /// Linq表达式扩展方法
    /// </summary> 
    public static class PredicateExtensions
    {
        /// <summary>
        /// 以And合并单个表达式
        /// 此处采用AndAlso实现“最短路径”,避免掉额外且不需要的比较运算式
        /// </summary> 
        public static Expression<Func<T, bool>> MergeAnd<T>(this Expression<Func<T, bool>> leftExpress, Expression<Func<T, bool>> rightExpress)
        {
            //声明传递参数(也就是表达式树里面的参数别名s)
            ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
            //统一管理参数,保证参数一致,否则会报错 
            var visitor = new PredicateExpressionVisitor(parameter);
            //表达式树内容
            Expression left = visitor.Visit(leftExpress.Body);
            Expression right = visitor.Visit(rightExpress.Body);
            //合并表达式
            return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left, right), parameter);
        }

        /// <summary>
        /// 以And合并多个表达式
        /// 此处采用AndAlso实现“最短路径”,避免掉额外且不需要的比较运算式
        /// </summary> 
        public static Expression<Func<T, bool>> MergeAnd<T>(this Expression<Func<T, bool>> express, params Expression<Func<T, bool>>[] arrayExpress)
        {
            if (!arrayExpress?.Any() ?? true) return express;
            //声明传递参数(也就是表达式树里面的参数别名s)
            ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
            //统一管理参数,保证参数一致,否则会报错 
            var visitor = new PredicateExpressionVisitor(parameter);
            Expression<Func<T, bool>> result = null;
            //合并表达式
            foreach (var curExpression in arrayExpress)
            {
                //表达式树内容
                Expression left = visitor.Visit(result.Body);
                Expression right = visitor.Visit(curExpression.Body);
                result = Expression.Lambda<Func<T, bool>>(Expression.AndAlso(left, right), parameter);
            }
            return result;
        }

        /// <summary>
        /// 以Or合并表达式
        /// 此处采用OrElse实现“最短路径”,避免掉额外且不需要的比较运算式
        /// </summary> 
        public static Expression<Func<T, bool>> MergeOr<T>(this Expression<Func<T, bool>> leftExpress, Expression<Func<T, bool>> rightExpress)
        {
            //声明传递参数(也就是表达式树里面的参数别名s)
            ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
            //统一管理参数,保证参数一致,否则会报错 
            var visitor = new PredicateExpressionVisitor(parameter);
            //表达式树内容
            Expression left = visitor.Visit(leftExpress.Body);
            Expression right = visitor.Visit(rightExpress.Body);
            //合并表达式
            return Expression.Lambda<Func<T, bool>>(Expression.OrElse(left, right), parameter);
        }

        /// <summary>
        /// 以Or合并多个表达式
        /// 此处采用AndAlso实现“最短路径”,避免掉额外且不需要的比较运算式
        /// </summary> 
        public static Expression<Func<T, bool>> MergeOr<T>(this Expression<Func<T, bool>> express, params Expression<Func<T, bool>>[] arrayExpress)
        {
            if (!arrayExpress?.Any() ?? true) return express;
            //声明传递参数(也就是表达式树里面的参数别名s)
            ParameterExpression parameter = Expression.Parameter(typeof(T), "s");
            //统一管理参数,保证参数一致,否则会报错 
            var visitor = new PredicateExpressionVisitor(parameter);
            Expression<Func<T, bool>> result = null;
            //合并表达式
            foreach (var curExpression in arrayExpress)
            {
                //表达式树内容
                Expression left = visitor.Visit(result.Body);
                Expression right = visitor.Visit(curExpression.Body);
                result = Expression.Lambda<Func<T, bool>>(Expression.OrElse(left, right), parameter);
            }
            return result;
        }
    }

    public class PredicateExpressionVisitor : ExpressionVisitor
    {
        public ParameterExpression _parameter { get; set; }

        public PredicateExpressionVisitor(ParameterExpression parameter)
        {
            _parameter = parameter;
        }
        protected override Expression VisitParameter(ParameterExpression p)
        {
            return _parameter;
        }

        public override Expression Visit(Expression expression)
        {
            //Visit会根据VisitParameter()方法返回的Expression进行相关变量替换
            return base.Visit(expression);
        }
    }

使用例子

    class Program
    {

        static void Main(string[] args)
        { 
            var models = new List<JsonData>() { new JsonData() { Id = "001", Name = "One" }, new JsonData() { Id = "002", Name = "Tow" } };

            Console.WriteLine($"未处理集合:{string.Join(',', models.Select(o => o.Id))}");
            //表达式1
            Expression<Func<JsonData, bool>> expression1 = t => t.Id == "001";
            //表达式2
            Expression<Func<JsonData, bool>> expression2 = t => t.Name == "Tow";
            //合并成 t => t.Id=="001" && t.Name=="One"
            Expression<Func<JsonData, bool>> allEexpression = expression1.MergeAnd(expression2);
            Console.WriteLine(allEexpression.Body.ToString());
            Console.WriteLine($"已处理集合(And):{string.Join(',', models.Where(expression1.MergeAnd(expression2).Compile()).Select(o => o.Id))}");
            //合并成 t => t.Id=="001" || t.Name=="One"
            allEexpression = expression1.MergeOr(expression2);
            Console.WriteLine(allEexpression.Body.ToString()); 
            Console.WriteLine($"已处理集合(Or):{string.Join(',', models.Where(allEexpression.Compile()).Select(o => o.Id))}");

            Console.ReadKey();
        } 
    }

     
    public class JsonData
    {
        public string Id { get; set; }
        public string Name { get; set; }
    }

结果

image.png

相关资料

MSDN Expression类
MSDN ExpressionVisitor类

posted @ 2022-06-23 18:55  Hi_Carl  阅读(487)  评论(0编辑  收藏  举报