关于C#动态表达式树扩展的一些应用和处理办法(本文主要解决高级搜索/自定义搜索等相关问题)

为了更好的处理高级搜索功能,可以根据用户自定义搜索条件执行动态表达式,以下是动态表达式扩展的核心代码,请铁子们验收,下边还总结了重要的核心知识点,各位可以点击进行阅读学习。

核心知识点:Expression反射

//自定义对象
public class AdvanceSearchInput
    {
        public enum SqlOperator
        {
            Contains,
            NotContains,
            Equal,
            NotEqual,
            Greater,
            GreaterEqual,
            Less,
            LessEqual,
            InSetOf,
            NotInSetOf
        }

        public string FieldName
        {
            get;
            set;
        }

        public string FieldValue
        {
            get;
            set;
        }

        public SqlOperator Operator
        {
            get;
            set;
        }

        public bool AndAlso
        {
            get;
            set;
        }
    }
//核心代码扩展
public static class ExpressionExtension
    {
        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) => first.AndAlso<T>(second, Expression.AndAlso);
        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second) => first.AndAlso<T>(second, Expression.OrElse);
        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)
            {
                this.oldValue = oldValue;
                this.newValue = newValue;
            }
            public override Expression Visit(Expression node)
            {
                if (node == this.oldValue)
                {
                    return this.newValue;
                }
                return base.Visit(node);
            }
        }
    }
    public class ExpressionVisitorExtensions : Collection<AdvanceSearchInput>
    {
        public Expression<Func<T, bool>> AsExpression<T>() where T : class
        {
            var type = typeof(T);
            var typeInfo = type.GetTypeInfo();
            var parameter = Expression.Parameter(type, "m");
            Expression expression = null;
            static Expression func(bool andAlso, Expression exp1, Expression exp2)
            {
                if (exp1 == null)
                {
                    return exp2;
                }
                return andAlso ? Expression.AndAlso(exp1, exp2) : Expression.OrElse(exp1, exp2);
            }
            foreach (var item in this)
            {
                var property = typeInfo.GetProperty(item.FieldName, BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
                if (property == null || !property.CanRead || string.IsNullOrWhiteSpace(item.FieldValue))
                {
                    continue;
                }
                var valueType = Nullable.GetUnderlyingType(property.PropertyType) ?? property.PropertyType;
                Expression<Func<object>> valueLambda = () => GetValue(item.FieldValue, valueType);

                switch (item.Operator)
                {
                    case AdvanceSearchInput.SqlOperator.Equal:
 if (valueType == typeof(string))
                        {
                            var nameToLower = Expression.Call(Expression.Property(parameter, item.FieldName), typeof(String).GetMethod("ToLower", new Type[] { }));
                            var valueToLower = Expression.Call(Expression.Convert(valueLambda.Body, property.PropertyType), typeof(String).GetMethod("ToLower", new Type[] { }));
                            expression = func(item.AndAlso, expression, Expression.Call(nameToLower, "Equal", null, valueToLower));
                        }
                        else
                        {
                            expression = func(item.AndAlso, expression, Expression.Equal(Expression.Property(parameter, item.FieldName), valueType.IsEnum ? Expression.Constant(Enum.Parse(valueType, item.FieldValue)) : Expression.Convert(valueLambda.Body, property.PropertyType)));
                        }
                        break;
                    case AdvanceSearchInput.SqlOperator.NotEqual:
 
                        if (valueType == typeof(string))
                        {
                            var nameToLower = Expression.Call(Expression.Property(parameter, item.FieldName), typeof(String).GetMethod("ToLower", new Type[] { }));
                            var valueToLower = Expression.Call(Expression.Convert(valueLambda.Body, property.PropertyType), typeof(String).GetMethod("ToLower", new Type[] { }));
                            expression = func(item.AndAlso, expression, Expression.Call(nameToLower, "NotEqual", null, valueToLower));
                        }
                        else
                        {
                            expression = func(item.AndAlso, expression, Expression.NotEqual(Expression.Property(parameter, item.FieldName), Expression.Convert(valueLambda.Body, property.PropertyType)));
                        }
                        break;
                    case AdvanceSearchInput.SqlOperator.Contains:
                         var nullCheck = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, Expression.Property(parameter, item.FieldName)));
                            var afterToLower = Expression.Call(Expression.Property(parameter, item.FieldName), typeof(String).GetMethod("ToLower", new Type[] { }));
                            var beforeToLower = Expression.Call(Expression.Convert(valueLambda.Body, property.PropertyType), typeof(String).GetMethod("ToLower", new Type[] { }));//ToUpper
                            var contains = Expression.Call(afterToLower, "Contains", null, beforeToLower);
                            expression = func(item.AndAlso, expression, Expression.AndAlso(nullCheck, contains));
                        break;
                    case AdvanceSearchInput.SqlOperator.NotContains:
                        var nullCheck_n = Expression.Not(Expression.Call(typeof(string), "IsNullOrEmpty", null, Expression.Property(parameter, item.FieldName)));
                            var fieldNameToLower = Expression.Call(Expression.Property(parameter, item.FieldName), typeof(String).GetMethod("ToLower", new Type[] { }));
                            var fieldValueToLower = Expression.Call(Expression.Convert(valueLambda.Body, property.PropertyType), typeof(String).GetMethod("ToLower", new Type[] { }));//ToUpper
                            var contains_n = Expression.Call(fieldNameToLower, "Contains", null, fieldValueToLower);
                            expression = func(item.AndAlso, expression, Expression.Not(Expression.AndAlso(nullCheck_n, contains_n)));
                        break;
                    case AdvanceSearchInput.SqlOperator.Greater:
                        expression = func(item.AndAlso, expression, Expression.GreaterThan(Expression.Property(parameter, item.FieldName), Expression.Convert(valueLambda.Body, property.PropertyType)));
                        break;
                    case AdvanceSearchInput.SqlOperator.GreaterEqual:
                        expression = func(item.AndAlso, expression, Expression.GreaterThanOrEqual(Expression.Property(parameter, item.FieldName), Expression.Convert(valueLambda.Body, property.PropertyType)));
                        break;
                    case AdvanceSearchInput.SqlOperator.Less:
                        expression = func(item.AndAlso, expression, Expression.LessThan(Expression.Property(parameter, item.FieldName), Expression.Convert(valueLambda.Body, property.PropertyType)));
                        break;
                    case AdvanceSearchInput.SqlOperator.LessEqual:
                        expression = func(item.AndAlso, expression, Expression.LessThanOrEqual(Expression.Property(parameter, item.FieldName), Expression.Convert(valueLambda.Body, property.PropertyType)));
                        break;
                    case AdvanceSearchInput.SqlOperator.InSetOf:
                         Expression leftExpression = null;
                        if (valueType.IsEnum)
                        {
                            var values = item.FieldValue.Split(",").Select(s => (int)Enum.Parse(valueType, s)).ToList();
                            leftExpression = Expression.Constant(values, values.GetType());
                            expression = func(item.AndAlso, expression, Expression.Call(leftExpression, "Contains", null, Expression.Convert(Expression.Property(parameter, item.FieldName), typeof(int))));
                        }
                        else
                        {
                            if (valueType == typeof(int))
                            {
                                List<int> values = item.FieldValue.Split(",").Select(s => Convert.ToInt32(s)).ToList();
                                leftExpression = Expression.Constant(values, values.GetType());
                            }
                            else if (valueType == typeof(string))
                            {
                                List<string> values = item.FieldValue.Split(",").ToList<String>();
                                leftExpression = Expression.Constant(values, values.GetType());
                            }
                            else if (valueType == typeof(long))
                            {
                                List<long> values = item.FieldValue.Split(",").Select(s => Convert.ToInt64(s)).ToList();
                                leftExpression = Expression.Constant(values, values.GetType());
                            }
                            else if (valueType == typeof(float) || valueType == typeof(double))
                            {
                                List<double> values = item.FieldValue.Split(",").Select(s => Convert.ToDouble(s)).ToList();
                                leftExpression = Expression.Constant(values, values.GetType());
                            }
                            if(leftExpression != null)
                                expression = func(item.AndAlso, expression, Expression.Call(leftExpression, "Contains", null, Expression.Property(parameter, item.FieldName)));
                        }
                        break;
                    case AdvanceSearchInput.SqlOperator.NotInSetOf:
                        Expression leftExpressionNo = null;
                        if (valueType.IsEnum)
                        {
                            var values = item.FieldValue.Split(",").Select(s => (int)Enum.Parse(valueType, s)).ToList();
                            leftExpressionNo = Expression.Constant(values, values.GetType());
                            expression = func(item.AndAlso, expression, Expression.Not(Expression.Call(leftExpressionNo, "Contains", null, Expression.Convert(Expression.Property(parameter, item.FieldName), typeof(int)))));
                        }
                        else
                        {
                            if (valueType == typeof(int))
                            {
                                List<int> values = item.FieldValue.Split(",").Select(s => Convert.ToInt32(s)).ToList();
                                leftExpressionNo = Expression.Constant(values, values.GetType());
                            }
                            else if (valueType == typeof(string))
                            {
                                List<string> values = item.FieldValue.Split(",").ToList<String>();
                                leftExpressionNo = Expression.Constant(values, values.GetType());
                            }
                            else if (valueType == typeof(long))
                            {
                                List<long> values = item.FieldValue.Split(",").Select(s => Convert.ToInt64(s)).ToList();
                                leftExpressionNo = Expression.Constant(values, values.GetType());
                            }
                            else if (valueType == typeof(float) || valueType == typeof(double))
                            {
                                List<double> values = item.FieldValue.Split(",").Select(s => Convert.ToDouble(s)).ToList();
                                leftExpressionNo = Expression.Constant(values, values.GetType());
                            }
                            if (leftExpressionNo != null)
                                expression = func(item.AndAlso, expression, Expression.Not(Expression.Call(leftExpressionNo, "Contains", null, Expression.Property(parameter, item.FieldName))));
                        }
                        break;
                }
            }
            if (expression == null)
            {
                Expression<Func<T, bool>> defaultExpression = x => true;
                return defaultExpression;
            }
            return (Expression<Func<T, bool>>)Expression.Lambda(expression, parameter);
        }
    }

    private object GetValue(string value, Type type)
    {
        if (type == typeof(bool))
        {
            bool boolValue;
            if (bool.TryParse(value, out boolValue))
                return boolValue;
            else
            {
                int intValue;
                if (int.TryParse(value, out intValue))
                    return Convert.ChangeType(intValue, type);
                else
                    return null;
            }
        }
        else
        {
            return System.Convert.ChangeType(value, type);
        }
    }

 //使用案例
 ExpressionVisitorExtensions advanceSearchInputs = new ExpressionVisitorExtensions();
                foreach (var item in listCondition.AdvanceConditions)
                {
                    advanceSearchInputs.Add(item);
                }
                var reply = this.DBContext.T.Where(advanceSearchInputs.AsExpression<T>())

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值