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

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

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

旨在实现例如以下效果:

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

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();
 }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值