c# 经验谈:巧用Expression表达式 解决类似于sql中 select in 的查询(适合于中小型项目)


我们在项目经常会碰到一些特殊需求 例如下拉框是复选的,查询条件是根据下拉框中复选项进行拼接

看到此图后大家肯定会说,这很简单嘛

将所有的选项 拼成“'1-3','5-9'”  然后放到 in 的字句后面,一查就出来了。

这样做的确在逻辑上没有问题,可是大家有没有想过这个问题,过度的和业务耦合虽然能够解决

现在的需求但是却牺牲了代码优雅和可维护性

 

其实本文的目的是想利用Expression表达式在linq查询中实现一个优雅的解决方案,

同时也会给大家一个用Expression去拼接sql的思路

 

先上代码

复制代码
      public static Expression<Func<T, bool>> GetConditionExpression<T>(string[] options, string fieldName)
        {
            ParameterExpression left = Expression.Parameter(typeof(T), "c");//c=>
            Expression expression = Expression.Constant(false);
            foreach (var optionName in options)
            {
                Expression right = Expression.Call
                       (
                          Expression.Property(left, typeof(T).GetProperty(fieldName)),  //c.DataSourceName
                          typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),// 反射使用.Contains()方法                         
                         s Expression.Constant(optionName)           // .Contains(optionName)
                       );
                expression = Expression.Or(right, expression);//c.DataSourceName.contain("") || c.DataSourceName.contain("") 
            }
            Expression<Func<T, bool>> finalExpression
                = Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left });
            return finalExpression;
        }
复制代码

我想用逆推的方式说明下这段代码,其实我们查询的目的要实现这样的效果 , someList.where(c=>c.Name.contains("someName")||c.Name.Contains("someName")||...)

1. 首先我们要确定返回什么样的表达式,根据经验.where后面是需要一个Expression<Func<T, bool>> 这样的一个表达式,所以方法的返回类型已经能确定下来了

2. 接下来的任务是拼接类似于c=>c.Name.Contains("") 这样的表达式,按照自左向右的原则,左侧表达式参数c很好理解 就是T,那么这个表达式的参数也就搞定了,

可以用Expression.Parameter方法来实现,该方法目的是将类型反射并且映射给表达式中的匿名变量 “c” (也可以理解成将参数常量封装成表达式)

3. 接着是表达式右侧的拼接

 再次仔细看下这段代码

Expression right = Expression.Call 

 (                          

     Expression.Property(left, typeof(T).GetProperty(fieldName)),  //c.DataSourceName     首先是反射获取c的一个属性                

     typeof(string).GetMethod("Contains", new Type[] { typeof(string) }),// 声明一个string.Contains的方法     c.DataSourceName.Contains()                反射使用.Contains()方法  

     Expression.Constant(optionName)           //  c.DataSourceName.Contains(optionName)               封装常量       

 );

  为什么要使用Expression.Call ?

(因为c.Name.contains 属于string.contains()这个方法所以我们必须将该方法封装成表达式,Expression.Call的功能就是将方法封装成表达式

 这时候大家会问contains什么呢? 当然常量option虽然是string类型,但是仍需封装成表达式,Expression.Constant(optionName) 起到了封装常量的作用

 于是c=>c.属性.Contains(常量) 这个表达式搞定,可是还是有问题:怎么加上“||” ,聪明的你已经有了答案,Expression.Or()

 

4 最后一步当然非常关键,就像产品需要通过流水线进行包装组合,表达式也不例外:

Expression.Lambda<Func<T, bool>>(expression, new ParameterExpression[] { left });

对于整个表达式来说,左侧是参数表达式(ParameterExpression),Expression.Lambda就是=>符号,就右侧表达式和参数表达式通过lambda符号进行组合,搞定

 

这样的话,你只需传入一个字符串数组就能在Linq中实现类似于sql中select in 的效果了,

很多朋友肯定会问,既然能够用自定义表达式搞定,那么可不可以将表达式的思路用于拼接sql?

答案是肯定的。但是如果业务逻辑非常复杂,而且难以把握,还是建议用ado 配合存过实现

 

 

感谢女友一直陪到深夜,让我坚持把这篇博文写完,文中如有错误,也请大家海涵并且能够及时告诉我,谢谢!

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值