添加链接
link之家
链接快照平台
  • 输入网页链接,自动生成快照
  • 标签化管理网页链接

日常开发时,使用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;

PredicateExpressionVisitor类

    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; }

为了代码更简洁,我们还可以在上述例子中加多两个方法,参数判断条件condition

public static class PredicateExtensions
    /// <summary>
    /// 若符合条件则以And合并单个表达式,反之不合并
    /// 此处采用AndAlso实现“最短路径”,避免掉额外且不需要的比较运算式
    /// </summary> 
    public static Expression<Func<T, bool>> MergeAndIF<T>(this Expression<Func<T, bool>> leftExpress,
        Expression<Func<T, bool>> rightExpress, bool condition)
        => condition ? leftExpress.MergeAnd(rightExpress) : leftExpress;
    /// <summary>
    /// 若符合条件则以And合并多个表达式,反之不合并
    /// 此处采用AndAlso实现“最短路径”,避免掉额外且不需要的比较运算式
    /// </summary> 
    public static Expression<Func<T, bool>> MergeAndIF<T>(this Expression<Func<T, bool>> express, bool condition,
        params Expression<Func<T, bool>>[] arrayExpress)
        => condition ? express.MergeAnd(arrayExpress) : express;
    /// <summary>
    /// 若符合条件则以Or合并单个表达式,反之不合并
    /// 此处采用OrElse实现“最短路径”,避免掉额外且不需要的比较运算式
    /// </summary> 
    public static Expression<Func<T, bool>> MergeOr<T>(this Expression<Func<T, bool>> leftExpress
        , Expression<Func<T, bool>> rightExpress, bool condition)
        => condition ? leftExpress.MergeAnd(rightExpress) : leftExpress;
    /// <summary>
    /// 若符合条件则以Or合并多个表达式,反之不合并
    /// 此处采用AndAlso实现“最短路径”,避免掉额外且不需要的比较运算式
    /// </summary> 
    public static Expression<Func<T, bool>> MergeOr<T>(this Expression<Func<T, bool>> express, bool condition,
        params Expression<Func<T, bool>>[] arrayExpress)
        => condition ? express.MergeAnd(arrayExpress) : express;

MSDN Expression类
MSDN ExpressionVisitor类

魔王不造反 函数式编程
  • 13.9w
私信