.NetCore 擴展封裝 Expression > 查詢條件遇到的問題


前面的文章封裝了查詢條件 自己去組裝條件,但是對 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)));
          
        }

 

本質上沒什么不同,最后連接都能拿到相關的表達式

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

按照如上寫法多寫幾個條件,2個查詢條件,2個值,感覺沒問題, 但是運行到Where的時候報錯誤 表到時Parameter參數的個數對應不上表達式參數的個數,參數丟失了?

參數的值跟隨表達式,在組合的時候需要重新組合參數,如果直接組合表達式,參數不會發生變化所以需要處理下參數問題,對(Expression<Func<T, bool>>) 的擴展就迎刃而解了

正確的處理方式:

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

使用方法就簡單多了

 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;

下面看下這段代碼其實給之前出現錯誤的原因是一樣的?

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



        }
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




 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM