C# 动态构建表达式(Expression)

问题来源:前端文本输入框以分隔符(比如"aa|bb|cc")传进来的字符串,针对一个字段做的查询条件;由于该字符串分隔符数量不确定,因此需要动态构建出来;

旨在实现例如以下效果:

1
var users = await _context.Users.Where(s=>s.Name.Contains("aa") || s.Name.Contains("bb") || s.Name.Contains("cc")).ToListAsync();

  

以下是后端代码的相关实现

1.新建两个类如下:

复制代码
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);
    }
}


 /// <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;
     }
 }
View Code
复制代码

 

2.在对应的接口中调用就能实现功能了(这里的search.Name以分隔符|举例,比如"aa|bb|cc")

复制代码
 //在文件夹下所搜多个文件名包含的查询条件
 if (!string.IsNullOrWhiteSpace(search.Name))
 {
     var names = search.Name.Split('|').ToList();
     Expression<Func<FileInfomation, bool>> expression = default;
     foreach (var name in names)
     {
         if (names.IndexOf(name) == 0)
             expression = s => s.Name.Contains(name);
         else
         {
             Expression<Func<FileInfomation, bool>> expressionNew = s => s.Name.Contains(name);
             expression = expression.MergeOr(expressionNew);
         }
     }
     fileInfos = fileInfos.Where(expression.Compile()).ToList();
 }
View Code
复制代码

 

posted @   情深缘浅_hy  阅读(51)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
点击右上角即可分享
微信分享提示