首页 文章详情

.NetCore 扩展封装 Expression<Func<T, bool>> 查询条件遇到的问题

llovebo | 498 2021-03-11 12:29 0 0 0
UniSMS (合一短信)

前面的文章封装了查询条件 自己去组装条件,但是对 And  Or  这种组合支持很差,但是也不是不能支持,只是要写更多的代码看起来很臃肿

根据 Where(Expression<Func<T, bool>>) 我们直接来处理这个,在处理这个之前其实看了下

Expression这个对象的处理,本生里面是包含了 AndAlso 、 Or 的处理   先来看看这个会遇到什么问题?为什么不行?

比如:

Expression.AndAlso(first,second)

来一段之前的扩展

 public static Expression AndExpression(this Expression expression, Expression right)
{
return Expression.AndAlso(expression, right);

}
public static Expression OrExpression(this Expression expression, Expression right)
{
return Expression.Or(expression, right);

}
public static Expression<Func<T,bool>> ToFilter<T>(this Expression expression)
{
return Expression.Lambda<Func<T, bool>>(expression, Expression.Parameter(typeof(T)));

}

 

本质上没什么不同,最后连接都能拿到相关的表达式

6aae6aeb4db5ee2bb85729c61964582b.webp

Expression filter= Expression.Constant(true, typeof(bool));
if (!string.IsNullOrEmpty(username))
{
filter
= filter.AndExpression(new UosoConditions {
Key
= "UserName",
Operator
= UosoOperatorEnum.Contains,
Value
= username,
ValueType
= "string"
}.Parser
<IdentityUser>());
}

6aae6aeb4db5ee2bb85729c61964582b.webp

按照如上写法多写几个条件,2个查询条件,2个值,感觉没问题, 但是运行到Where的时候报错误 表到时Parameter参数的个数对应不上表达式参数的个数,参数丢失了?

参数的值跟随表达式,在组合的时候需要重新组合参数,如果直接组合表达式,参数不会发生变化所以需要处理下参数问题,对(Expression<Func<T, bool>>) 的扩展就迎刃而解了

正确的处理方式:

6aae6aeb4db5ee2bb85729c61964582b.webp

   public static class ExpressionExtensions
{
/// <summary>
/// 添加And条件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="first"></param>
/// <param name="second"></param>
/// <returns></returns>
public static Expression<Func<T, bool>> And<T>(
this Expression<Func<T, bool>> first,
Expression
<Func<T, bool>> second)
{
return first.AndAlso<T>(second, Expression.AndAlso);
}
/// <summary>
/// 添加Or条件
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="first"></param>
/// <param name="second"></param>
/// <returns></returns>
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> first,
Expression
<Func<T, bool>> second)
{
return first.AndAlso<T>(second, Expression.OrElse);
}
/// <summary>
/// 合并表达式以及参数
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="expr1"></param>
/// <param name="expr2"></param>
/// <param name="func"></param>
/// <returns></returns>
private static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression
<Func<T, bool>> expr2,
Func
<Expression, Expression, BinaryExpression> func)
{
var parameter = Expression.Parameter(typeof(T));

var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);

var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);

return Expression.Lambda<Func<T, bool>>(
func(left, right), parameter);



}


private class ReplaceExpressionVisitor
: ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;

public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue
= oldValue;
_newValue
= newValue;
}

public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
}

6aae6aeb4db5ee2bb85729c61964582b.webp

使用方法就简单多了

6aae6aeb4db5ee2bb85729c61964582b.webp

 Expression<Func<IdentityUser, bool>> filter = u => true;

if (!string.IsNullOrEmpty(username))
{
filter
= filter.And(c => c.UserName.Contains(username));
}
if (!string.IsNullOrEmpty(phone))
{
filter
= filter.And(c => c.PhoneNumber.Contains(phone));
}
if (!string.IsNullOrEmpty(email))
{
filter
= filter.And(c => c.Email.Contains(email));
}


这里值得注意的是 一定要重新赋值到 filter ,按理说扩展了Expression<Func<T, bool>> 也返回了 Expression<Func<T, bool>>  好像可以不用重新赋值,然而这里并不是这样

如果我们直接

filter.And(c => c.UserName.Contains(username));

这样添加 会发现之中都是第一个参数的条件 都是 true,这是为什么呢?

Expression<Func<IdentityUser, bool>> filter = u => true;

下面看下这段代码其实给之前出现错误的原因是一样的?

6aae6aeb4db5ee2bb85729c61964582b.webp

  private static Expression<Func<T, bool>> AndAlso<T>(
this Expression<Func<T, bool>> expr1,
Expression
<Func<T, bool>> expr2,
Func
<Expression, Expression, BinaryExpression> func)
{
var parameter = Expression.Parameter(typeof(T));

var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);

var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);

return Expression.Lambda<Func<T, bool>>(
func(left, right), parameter);



}

6aae6aeb4db5ee2bb85729c61964582b.webp

var parameter = Expression.Parameter(typeof(T)); 是对 T 类中做的反射,本生合并两个带 T 的应该是没问题的,只是因为

与 Expression<Func<IdentityUser, bool>> filter = u => true; 组合后  

Expression.Lambda<Func<T, bool>>(
func(left, right), parameter);

一直都是True,导致最后的条件都是返回 True 查询条件就无效了,所以需要重新引用赋值 filter

出处:https://www.cnblogs.com/liyouming/p/9447984.html


支持小微:

腾讯云 搞活动了?玩服务器的可以搞搞。  就这几天时间。

轻量 1C2G 50GB SSD盘 255元/3年

链接:https://curl.qcloud.com/qINmPBX9


版权申明:本文来源于网友收集或网友提供,如果有侵权,请转告版主或者留言,本公众号立即删除。

右下角,您点一下在看图片809ac38ff6859159a4db29196b5ee777.webp

小微工资涨1毛

商务合作QQ:185601686


5d1fd815a70fa33676701ae0f10b30c6.webp


good-icon 0
favorite-icon 0
收藏
回复数量: 0
    暂无评论~~
    Ctrl+Enter