如何实现每种不超过等,对字符串表达式建设时 [英] How to implement LessThan, etc., when building expressions on strings

查看:138
本文介绍了如何实现每种不超过等,对字符串表达式建设时的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个包中,我建立表达式树,用的EntityFramework使用,通过PredicateBuilder:

 公共表达式来; Func键< T,BOOL>> constructaPredicate< T>(ExpressionType操作,字符串字段名,表达式的值)
{
变种类型= typeof运算(T);
变量参数= Expression.Parameter(类型);
VAR成员= Expression.PropertyOrField(参数,字段名);
表达比较= Expression.MakeBinary(操作员,值);
VAR表达式= Expression.Lambda<&Func键LT; T,BOOL>>(相比之下,参数);
返回表达式;
}

这工作得很好,除了有GREATERTHAN比较字符串时,等在那情况下,我得到一个异常:

 的二元运算GREATERTHAN没有为类型System.String和系统定义。串'。 



这是很简单的。浏览四周,我发现在我在做什么的背景下对这一问题只有几个引用,没有。



的问题,当然,是不存在String.GreaterThan方法。通常的答案是使用String.CompareTo(),但我还没有想出如何使这项工作。



我一直在尝试使用过载Expression.MakeBinary接受一个MethodInfo的对象,但我没有意识到这一点。



帮助?



新增



所以,我一直在努力,特殊情况String.GreaterThan等,我仍然得到同样的错误:

 表达比较= NULL; 

如果(value.Type == typeof运算(字符串))
{
如果(操作== || ExpressionType.GreaterThanOrEqual
操作== ExpressionType.GreaterThan | |
操作== || ExpressionType.LessThanOrEqual
操作== ExpressionType.LessThan)
{
VAR方法= value.Type.GetMethod(的CompareTo,新的[] { typeof运算(字符串)});
变种零= Expression.Constant(0);

VAR的结果= Expression.Call(成员,方法,转换);

比较= Expression.MakeBinary(操作,因此,零);
}
}

如果(比较== NULL)
比较= Expression.MakeBinary(操作员,转换);

VAR波长= Expression.Lambda<&Func键LT; T,BOOL>>(相比之下,参数);



但我仍然看到完全相同的例外。这是没有意义的我,因为如果我在做什么,我认为我做的,唯一的GREATERTHAN的表达比较的Int32来的Int32。



多补充



我觉得非常奇怪的是,我看到了同样的错误,从我的表达式树卸掉后GREATERTHAN



我一直在运行此代码作为单元测试的一部分,与实体框架连接到所谓的努力。所以,我想这对SqlServer的。



我的原代码,这并没有特殊情况的字符串,但使用GREATERTHAN的一切,扔了没有定义大于例外,当对SqlServer的运行,以及在针对劲道运行。



我修改后的代码,即特例,字符串,工作只是对SqlServer的罚款,但扔了GREATERTHAN没有定义当针对工作量运行异常。



看来,当努力在表达式树看到一个的CompareTo(),将它转换成一个GREATERTHAN,并且结果在我们熟悉的异常



然而,更增添了



在不断探索的问题,我已经确定有一个bug在努力,可以用非常非常简单的例子来表明:

  VAR FOOS = myDbContext.Foos.Where(F => ; f.fooid.CompareTo(Z)大于0).ToList(); 

这工作得很好,当myDbContext连接到SQLServer数据库,它在连接到抛出我们最喜欢的异常一个努力数据库。我已经申请上的功夫论坛的bug报告。



对于那些谁读这篇文章,我的第二次尝试,我的第一个添加部分,上面是正确的解决方案。它的工作原理对SqlServer的,而且它并不反对努力是由于工作量的错误。



附录



有人问,什么是改建是指,在上面。



在事实上,我几乎不记得了。



发生了什么事在我的代码,就是我有我将这些比较一个表达式树。我使用Expression.Convert()将其转换为基础类型。



我不知道完整的方法多大意义,缺席的休息一流的,但在这里它是:

 公共表达式来; Func键< T,BOOL>> constructSinglePredicate< T>(对象上下文)
{
变种类型= typeof运算(T);
变量参数= Expression.Parameter(类型);
VAR成员= this.getMember< T>(类型,参数);
VAR值= this.constructConstantExpression< T>(this.rightHandSide,背景);
ExpressionType操作;
如果(!operationMap.TryGetValue(this.selectionComparison,退出运行))
抛出新ArgumentOutOfRangeException(selectionComparison,this.selectionComparison,无效的过滤操作);


{
无功转换=(value.Type!= member.Type)
? (表情)Expression.Convert(值,member.Type)
:(表情)值;

表达比较= NULL;

如果(value.Type == typeof运算(字符串))
{
如果(操作== || ExpressionType.GreaterThanOrEqual
操作== ExpressionType.GreaterThan | |
操作== || ExpressionType.LessThanOrEqual
操作== ExpressionType.LessThan)
{
MethodInfo的方法= value.Type.GetMethod(的CompareTo,新的[] { typeof运算(字符串)});
变种零= Expression.Constant(0);
VAR的结果= Expression.Call(成员,方法,转换);
比较= Expression.MakeBinary(操作,因此,零);
}
}

如果(比较== NULL)
比较= Expression.MakeBinary(操作员,转换);

VAR波长= Expression.Lambda<&Func键LT; T,BOOL>>(相比之下,参数);

返回的lambda;
}
赶上(例外)
{
抛出新的InvalidOperationException异常(
的String.Format(无法将值\{0} \类型\\ \\{1} \到现场\类型为{2} \\{3} \,this.rightHandSide,
value.Type,this.fieldName,会员。类型));
}
}


解决方案

这作品:

 表达比较= NULL; 

如果(value.Type == typeof运算(字符串))
{
如果(操作== || ExpressionType.GreaterThanOrEqual
操作== ExpressionType.GreaterThan | |
操作== || ExpressionType.LessThanOrEqual
操作== ExpressionType.LessThan)
{
VAR方法= value.Type.GetMethod(的CompareTo,新的[] { typeof运算(字符串)});
变种零= Expression.Constant(0);

VAR的结果= Expression.Call(成员,方法,转换);

比较= Expression.MakeBinary(操作,因此,零);
}
}

如果(比较== NULL)
比较= Expression.MakeBinary(操作员,转换);

VAR波长= Expression.Lambda<&Func键LT; T,BOOL>>(相比之下,参数);


I have a package in which I'm building expression trees, to use with EntityFramework, via PredicateBuilder:

public Expression<Func<T, bool>> constructaPredicate<T>(ExpressionType operation, string fieldName, Expression value)
{
    var type = typeof(T);
    var parameter = Expression.Parameter(type);
    var member = Expression.PropertyOrField(parameter, fieldName);
    Expression comparison = Expression.MakeBinary(operation, member, value);
    var expression = Expression.Lambda<Func<T, bool>>(comparison, parameter);
    return expression;
}

This works fine, except for when comparing strings with GreaterThan, etc. In that case, I get an exception:

The binary operator GreaterThan is not defined for the types 'System.String' and 'System.String'.

Which is simple enough. Browsing around, I've found only a few references to this issue, and none in the context of what I'm doing.

The problem, of course, is that there is no String.GreaterThan method. The usual answer is to use String.CompareTo(), but I've not figured out how to make that work.

I've been trying to use the overload of Expression.MakeBinary that takes a methodinfo object, but I've not figured it out.

Help?

Added

So, I've tried to special-case String.GreaterThan, etc., and I'm still getting the same error:

Expression comparison = null;

if (value.Type == typeof (string))
{
    if (operation == ExpressionType.GreaterThanOrEqual ||
        operation == ExpressionType.GreaterThan ||
        operation == ExpressionType.LessThanOrEqual ||
        operation == ExpressionType.LessThan)
    {
        var method = value.Type.GetMethod("CompareTo", new[] {typeof (string)});
        var zero = Expression.Constant(0);

        var result = Expression.Call(member, method, converted);

        comparison = Expression.MakeBinary(operation, result, zero);
    }
}

if (comparison == null)
    comparison = Expression.MakeBinary(operation, member, converted);

var lambda = Expression.Lambda<Func<T, bool>>(comparison, parameter);

But I'm still seeing the exact same exception. Which makes no sense to me, because if I'm doing what I think I'm doing, the only GreaterThan in the expression is comparing Int32 to Int32.

More Added

I found it very odd that I'd see the same error, after having removed GreaterThan from my expression tree.

I've been running this code as a part of a unit test, with Entity Framework connected to an in-memory database called Effort. So I tried it against SqlServer.

My original code, that didn't special-case string, but used GreaterThan for everything, threw the "GreaterThan not defined" exception, when run against SqlServer, and when run against Effort.

My modified code, that special-cased string, worked just fine against SqlServer, but threw the "GreaterThan not defined" exception when run against Effort.

It seems that when Effort sees a CompareTo() in an expression tree, it converts it to a GreaterThan, and that results in our familiar exception.

Yet More Added

In continuing to explore the issue, I've determined that there is a bug in Effort, that can be revealed with a very much simpler example:

var foos = myDbContext.Foos.Where(f => f.fooid.CompareTo("Z") > 0).ToList();

This works fine, when myDbContext is connected to a SqlServer database, it throws our favorite exception when connected to an Effort database. I've filed a bug report on the Effort discussion forum.

For those who are reading this, my second attempt, in my first "Added" section, above, is the correct solution. It works against SqlServer, and that it doesn't against Effort is due to a bug in Effort.

Addendum

The question was asked, what does "converted" refer to, in the above.

In truth, I hardly remember.

What's happening in my code, is that I have a expression tree that I'm applying these comparisons to. I'm using Expression.Convert() to convert this to the underlying type.

I'm not sure the complete method will make much sense, absent the rest of the class, but here it is:

public Expression<Func<T, bool>> constructSinglePredicate<T>(object context)
{
    var type = typeof(T);
    var parameter = Expression.Parameter(type);
    var member = this.getMember<T>(type, parameter);
    var value = this.constructConstantExpression<T>(this.rightHandSide, context);
    ExpressionType operation;
    if (!operationMap.TryGetValue(this.selectionComparison, out operation))
        throw new ArgumentOutOfRangeException("selectionComparison", this.selectionComparison, "Invalid filter operation");

    try
    {
        var converted = (value.Type != member.Type)
            ? (Expression)Expression.Convert(value, member.Type)
            : (Expression)value;

        Expression comparison = null;

        if (value.Type == typeof(string))
        {
            if (operation == ExpressionType.GreaterThanOrEqual ||
                operation == ExpressionType.GreaterThan ||
                operation == ExpressionType.LessThanOrEqual ||
                operation == ExpressionType.LessThan)
            {
                MethodInfo method = value.Type.GetMethod("CompareTo", new[] { typeof(string) });
                var zero = Expression.Constant(0);
                var result = Expression.Call(member, method, converted);
                comparison = Expression.MakeBinary(operation, result, zero);
            }
        }

        if (comparison == null)
            comparison = Expression.MakeBinary(operation, member, converted);

        var lambda = Expression.Lambda<Func<T, bool>>(comparison, parameter);

        return lambda;
    }
    catch (Exception)
    {
        throw new InvalidOperationException(
            String.Format("Cannot convert value \"{0}\" of type \"{1}\" to field \"{2}\" of type \"{3}\"", this.rightHandSide,
                value.Type, this.fieldName, member.Type));
    }
}

解决方案

This works:

Expression comparison = null;

if (value.Type == typeof (string))
{
    if (operation == ExpressionType.GreaterThanOrEqual ||
        operation == ExpressionType.GreaterThan ||
        operation == ExpressionType.LessThanOrEqual ||
        operation == ExpressionType.LessThan)
    {
        var method = value.Type.GetMethod("CompareTo", new[] {typeof (string)});
        var zero = Expression.Constant(0);

        var result = Expression.Call(member, method, converted);

        comparison = Expression.MakeBinary(operation, result, zero);
    }
}

if (comparison == null)
    comparison = Expression.MakeBinary(operation, member, converted);

var lambda = Expression.Lambda<Func<T, bool>>(comparison, parameter);

这篇关于如何实现每种不超过等,对字符串表达式建设时的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆