用户云卷云舒 发表于 2022-8-9 14:41:32

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

前言

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

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

https://p3-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/cb6cdf963ddd4dac8dca52b3f39d4cf3~tplv-k3u1fbpfcp-watermark.image?
相关资料

MSDN Expression类
MSDNExpressionVisitor类

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!
页: [1]
查看完整版本: C# Lambda、Linq表达式树动态构建、合并扩展方法