LinQ to SQL构建动态条件表达式,Expression.GreaterThan方法遇到枚举类型属性报异常

anliuty 2018-01-02 02:13:35
在LinQ to SQL构建动态条件表达式时,当构造>、>=、<、<=这4种条件时,当调用
Expression.GreaterThan、Expression.GreaterThanOrEqual、
Expression.LessThan、Expression.LessThanOrEqual
这4个方法时,如果遇到枚举类型属性,就会报错,提示如下:没有为类型“xxx”和“xxx”定义二进制运算符 GreaterThan”,
其中xxx是枚举类型的具体类型。原因好像是因为枚举类型没有实现上述4种操作符。

我尝试用CompareTo(value)方法变相实现比较,虽然可以构造表达式,但是在运行时生成SQL失败。

请问,有什么办法解决这个问题?
...全文
535 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
anliuty 2018-01-02
  • 打赏
  • 举报
回复
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个操作符
正怒月神 版主 2018-01-02
  • 打赏
  • 举报
回复
引用 7 楼 anliuty 的回复:
你这里弄的两个常数,转int后进行比较,当然没有问题啊,我的这个类是用于动态构造条件的,是个泛型基类型,构造GreaterThan表达式的时候,是通过反射去取实体类的属性作为条件的左边参数,右边是接收用户输入的常数值作为比较条件,常数转int很简单,关键是左边,是一个属性参数,是一个在运行时才知道其类型的属性参数
你的left传递的是什么呢?如果是枚举值,那应该通过convertto把枚举值转int,先放进一个变量。 然后把变量传递给Expression.GreaterThan 因为Expression表达式树无法使用C#方法。
anliuty 2018-01-02
  • 打赏
  • 举报
回复
你这里弄的两个常数,转int后进行比较,当然没有问题啊,我的这个类是用于动态构造条件的,是个泛型基类型,构造GreaterThan表达式的时候,是通过反射去取实体类的属性作为条件的左边参数,右边是接收用户输入的常数值作为比较条件,常数转int很简单,关键是左边,是一个属性参数,是一个在运行时才知道其类型的属性参数
正怒月神 版主 2018-01-02
  • 打赏
  • 举报
回复
我这里测试了并没有什么问题。
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,
}

anliuty 2018-01-02
  • 打赏
  • 举报
回复
代码中标记颜色不起作用啊, 就是最后的字典实现中的: { 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); } }, 返回语句会报异常
anliuty 2018-01-02
  • 打赏
  • 举报
回复

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
正怒月神 版主 2018-01-02
  • 打赏
  • 举报
回复
引用 2 楼 anliuty 的回复:
[quote=引用 1 楼 hanjun0612 的回复:] 枚举值转换了吗 Expression.GreaterThan((int)你的枚举值)
Expression.GreaterThan(left,right),left是参数表达式,right是要比较的值表达式,right转换int容易,left怎么转?[/quote] 我没有使用过这个语句,将你正在使用,并且报错的语句贴出来看一下
正怒月神 版主 2018-01-02
  • 打赏
  • 举报
回复
枚举值转换了吗 Expression.GreaterThan((int)你的枚举值)
anliuty 2018-01-02
  • 打赏
  • 举报
回复
引用 1 楼 hanjun0612 的回复:
枚举值转换了吗 Expression.GreaterThan((int)你的枚举值)
Expression.GreaterThan(left,right),left是参数表达式,right是要比较的值表达式,right转换int容易,left怎么转?

62,046

社区成员

发帖
与我相关
我的任务
社区描述
.NET技术交流专区
javascript云原生 企业社区
社区管理员
  • ASP.NET
  • .Net开发者社区
  • R小R
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告

.NET 社区是一个围绕开源 .NET 的开放、热情、创新、包容的技术社区。社区致力于为广大 .NET 爱好者提供一个良好的知识共享、协同互助的 .NET 技术交流环境。我们尊重不同意见,支持健康理性的辩论和互动,反对歧视和攻击。

希望和大家一起共同营造一个活跃、友好的社区氛围。

试试用AI创作助手写篇文章吧