在LinQ to SQL构建动态条件表达式时,当构造>、>=、<、<=这4种条件时,当调用
Expression.GreaterThan、Expression.GreaterThanOrEqual、
Expression.LessThan、Expression.LessThanOrEqual
这4个方法时,如果遇到枚举类型属性,就会报错,提示如下:没有为类型“xxx”和“xxx”定义二进制运算符 GreaterThan”,
其中xxx是枚举类型的具体类型。原因好像是因为枚举类型没有实现上述4种操作符。我尝试用CompareTo(value)方法变相实现比较,虽然可以构造表达式,但是在运行时生成SQL失败。请问,有什么办法解决这个问题? 

解决方案 »

  1.   

    枚举值转换了吗
    Expression.GreaterThan((int)你的枚举值)
      

  2.   

    Expression.GreaterThan(left,right),left是参数表达式,right是要比较的值表达式,right转换int容易,left怎么转?
      

  3.   

    Expression.GreaterThan(left,right),left是参数表达式,right是要比较的值表达式,right转换int容易,left怎么转?
    我没有使用过这个语句,将你正在使用,并且报错的语句贴出来看一下
      

  4.   


    public class FilterSet<T>
        {
            public static List<ITransformProvider> TransformProviders { get; set; }        static FilterSet()
            {
                TransformProviders = new List<ITransformProvider>
                                         {
                                             new DateBlockTransformProvider(),
                                             new GreaterDateTimeTransformProvider(),
                                             new InTransformProvider(),
                                             new LessDateTimeTransformProvider(),
                                             new LikeTransformProvider()
                                         };
            }
            public IList<FilterItem> Items { get; set; } = new List<FilterItem>();        public Expression<Func<T, bool>> ToExpression()
            {
                //构建 c=>Body中的c
                ParameterExpression param = Expression.Parameter(typeof(T), "c");            if (Items.Count > 0)
                {
                    //构建c=>Body中的Body
                    var body = GetExpressoinBody(param, Items);
                    //将二者拼为c=>Body
                    return Expression.Lambda<Func<T, bool>>(body, param);
                }
                return Expression.Lambda<Func<T, bool>>(Expression.Constant(true), param);
            }
            
            private Expression GetExpressoinBody(ParameterExpression param, IEnumerable<FilterItem> items)
            {
                //仅支持全And条件
                var list = new List<Expression>();
                list.Add(GetGroupExpression(param, items, Expression.AndAlso));
                return list.Aggregate(Expression.AndAlso);
            }        private Expression GetGroupExpression(ParameterExpression param, IEnumerable<FilterItem> items, Func<Expression, Expression, Expression> func)
            {
                //获取最小的判断表达式
                var list = items.Select(item => GetExpression(param, item));
                //再以逻辑运算符相连
                return list.Aggregate(func);
            }        private Expression GetExpression(ParameterExpression param, FilterItem item)
            {
                //属性表达式
                LambdaExpression exp = GetPropertyLambdaExpression(item, param);
                //如果有特殊类型处理,则进行处理
                foreach (var provider in TransformProviders)
                {
                    if (provider.Match(item, exp.Body.Type))
                    {
                        return GetGroupExpression(param, provider.Transform(item, exp.Body.Type), Expression.AndAlso);
                    }
                }
                //常量表达式
                var constant = ChangeTypeToExpression(item, exp.Body.Type);
                //以判断符或方法连接
                return ExpressionDict[item.Method](exp.Body, constant);
            }        private LambdaExpression GetPropertyLambdaExpression(FilterItem item, ParameterExpression param)
            {
                //获取每级属性如c.Users.Proiles.UserId
                var props = item.FieldName.Split('.');
                Expression propertyAccess = param;
                var typeOfProp = typeof(T);
                int i = 0;
                do
                {
                    PropertyInfo property = typeOfProp.GetProperty(props[i]);
                    if (property == null)
                        return null;
                    typeOfProp = property.PropertyType;
                    propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
                    i++;
                } while (i < props.Length);            return Expression.Lambda(propertyAccess, param);
            }        #region ChangeType        /// <summary>
            /// 类型转换,支持非空类型与可空类型之间的转换
            /// </summary>
            /// <param name="value"></param>
            /// <param name="conversionType"></param>
            /// <returns></returns>
            public static object ChangeType(object value, Type conversionType)
            {
                if (value == null)
                    return null;
                return Convert.ChangeType(value, TypeUtil.GetUnNullableType(conversionType));
            }        /// <summary>
            /// 转换SearchItem中的Value的类型,为表达式树
            /// </summary>
            /// <param name="item"></param>
            /// <param name="conversionType">目标类型</param>
            public static Expression ChangeTypeToExpression(FilterItem item, Type conversionType)
            {
                if (item.Value == null)
                    return Expression.Constant(item.Value, conversionType);
                #region 数组
                if (item.Method == FilterMethod.StandardIn)
                {
                    var arr = (item.Value as Array);
                    var expList = new List<Expression>();
                    //确保可用
                    if (arr != null)
                    {
                        for (var i = 0; i < arr.Length; i++)
                        {
                            //构造数组的单元Constant
                            var newValue = ChangeType(arr.GetValue(i), conversionType);
                            expList.Add(Expression.Constant(newValue, conversionType));
                        }
                    }                    
                    //构造inType类型的数组表达式树,并为数组赋初值
                    return Expression.NewArrayInit(conversionType, expList);
                }            #endregion            var elementType = TypeUtil.GetUnNullableType(conversionType);            var value = (elementType.IsEnum ? Enum.Parse(conversionType, item.Value.ToString()) : Convert.ChangeType(item.Value, elementType));
                //var value = (elementType.IsEnum ? Convert.ChangeType(item.Value, typeof(int)) : Convert.ChangeType(item.Value, elementType));            return Expression.Constant(value, conversionType);
            }        #endregion        #region SearchMethod 操作方法        private static readonly Dictionary<FilterMethod, Func<Expression, Expression, Expression>> ExpressionDict =
                new Dictionary<FilterMethod, Func<Expression, Expression, Expression>>
                    {
                        { FilterMethod.Eq, (left, right) => { return Expression.Equal(left, right); } },
                        { FilterMethod.Gt, (left, right) => 
                            {
                                
                                //if (left.Type.IsEnum)
                                //{
                                //    var methodInfo = left.Type.GetMethod("CompareTo", new Type[]{ left.Type });
                                //    var value = (int)((ConstantExpression)right).Value;
                                //    var eLeft = Expression.Call(left, methodInfo, Expression.Constant(value, typeof(object)) );
                                //    var eRight = Expression.Constant(0, typeof(int));
                                //    return Expression.GreaterThan(eLeft, eRight);
                                //}
                                //var lProp = (LambdaExpression)left;
                                //var rValue = (int)((ConstantExpression)right).Value;
                                //var eRight = Expression.Constant(rValue);
                                return Expression.GreaterThan(left, right);
                            }
                        },
                        { FilterMethod.Ge, (left, right) => { return Expression.GreaterThanOrEqual(left, right); } },
                        { FilterMethod.Lt, (left, right) => { return Expression.LessThan(left, right); } },
                        { FilterMethod.Le, (left, right) => { return Expression.LessThanOrEqual(left, right); } },
                        { FilterMethod.Contains, (left, right) =>
                            {
                                if (left.Type != typeof (string))
                                    return null;
                                return Expression.Call(left, typeof (string).GetMethod("Contains"), right);
                            }
                        },
                        { FilterMethod.StandardIn, (left, right) =>
                            {
                                if (!right.Type.IsArray)
                                    return null;
                                //调用Enumerable.Contains扩展方法
                                return Expression.Call(typeof (Enumerable), "Contains", new[] {left.Type}, right, left);
                            }
                        },
                        { FilterMethod.Nq, (left, right) => { return Expression.NotEqual(left, right); } },
                        { FilterMethod.StartsWith, (left, right) =>
                            {
                                if (left.Type != typeof (string))
                                    return null;
                                return Expression.Call(left, typeof (string).GetMethod("StartsWith", new[] {typeof (string)}), right);
                            }
                        },
                        { FilterMethod.EndsWith, (left, right) =>
                            {
                                if (left.Type != typeof (string))
                                    return null;
                                return Expression.Call(left, typeof (string).GetMethod("EndsWith", new[] {typeof (string)}), right);
                            }
                        },
                        { FilterMethod.DateTimeLessThanOrEqual, (left, right) => { return Expression.LessThanOrEqual(left, right); } }
                    };        #endregion
        }代码中标红的语句,会报异常,提示枚举类型没有实现GreaterThan
      

  5.   

    代码中标记颜色不起作用啊,
    就是最后的字典实现中的:
    { FilterMethod.Gt, (left, right) => 
                            {
                                 
                                //if (left.Type.IsEnum)
                                //{
                                //    var methodInfo = left.Type.GetMethod("CompareTo", new Type[]{ left.Type });
                                //    var value = (int)((ConstantExpression)right).Value;
                                //    var eLeft = Expression.Call(left, methodInfo, Expression.Constant(value, typeof(object)) );
                                //    var eRight = Expression.Constant(0, typeof(int));
                                //    return Expression.GreaterThan(eLeft, eRight);
                                //}
                                //var lProp = (LambdaExpression)left;
                                //var rValue = (int)((ConstantExpression)right).Value;
                                //var eRight = Expression.Constant(rValue);
                                return Expression.GreaterThan(left, right);
                            }
                        },
    返回语句会报异常
      

  6.   

    我这里测试了并没有什么问题。
    static void Main(string[] args)
            {
                Expression greaterThanExpr = Expression.GreaterThan(
        Expression.Constant(1),
        Expression.Constant((int)Test.NO)
    );
                Console.WriteLine(greaterThanExpr.ToString());
                Console.WriteLine(
        Expression.Lambda<Func<bool>>(greaterThanExpr).Compile()());            greaterThanExpr = Expression.GreaterThan(
        Expression.Constant(1),
        Expression.Constant((int)Test.YES)
    );
                Console.WriteLine(greaterThanExpr.ToString());
                Console.WriteLine(
        Expression.Lambda<Func<bool>>(greaterThanExpr).Compile()());            Console.ReadLine();
            }        public enum Test
            { 
                NO=0,
                YES=1,
            }
      

  7.   

    你这里弄的两个常数,转int后进行比较,当然没有问题啊,我的这个类是用于动态构造条件的,是个泛型基类型,构造GreaterThan表达式的时候,是通过反射去取实体类的属性作为条件的左边参数,右边是接收用户输入的常数值作为比较条件,常数转int很简单,关键是左边,是一个属性参数,是一个在运行时才知道其类型的属性参数
      

  8.   

    你的left传递的是什么呢?如果是枚举值,那应该通过convertto把枚举值转int,先放进一个变量。
    然后把变量传递给Expression.GreaterThan
    因为Expression表达式树无法使用C#方法。
      

  9.   

    private Expression GetExpression(ParameterExpression param, FilterItem item)
            {
                //属性表达式
                LambdaExpression exp = GetPropertyLambdaExpression(item, param);
                //如果有特殊类型处理,则进行处理
                ...
                //常量表达式
                var constant = ChangeTypeToExpression(item, exp.Body.Type);
                //以判断符或方法连接
                return ExpressionDict[item.Method](exp.Body, constant);
            }
    上面最后一句调用Dictionary,传入的left就是上面的exp.Body,是获取属性的表达式,再看取属性的实现:
    private LambdaExpression GetPropertyLambdaExpression(FilterItem item, ParameterExpression param)
            {
                //获取每级属性如c.Users.Proiles.UserId
                var props = item.FieldName.Split('.');
                Expression propertyAccess = param;
                var typeOfProp = typeof(T);
                int i = 0;
                do
                {
                    PropertyInfo property = typeOfProp.GetProperty(props[i]);
                    if (property == null)
                        return null;
                    typeOfProp = property.PropertyType;
                    propertyAccess = Expression.MakeMemberAccess(propertyAccess, property);
                    i++;
                } while (i < props.Length);
                return Expression.Lambda(propertyAccess, param);
            }
    这里是通过反射,取运行时传入的某个业务实体类型的的属性,构造一个属性访问表达式,这个属性可能是整形浮点型,可能是枚举,可能是字符串,现在的问题是遇到枚举的时候,GreaterThan报错,因为枚举类型没有实现>、<、>=、<=这4个操作符