我有一个程序包,用于在其中构建表达式树,以通过PredicateBuilder与EntityFramework一起使用:

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

除了将字符串与GreaterThan等进行比较时,此方法工作正常。在这种情况下,我得到一个异常(exception):
The binary operator GreaterThan is not defined for the types 'System.String' and 'System.String'.

这很简单。到处浏览,我只发现了一些关于此问题的引用,而在我正在做的事情中却没有。

当然,问题在于没有String.GreaterThan方法。通常的答案是使用String.CompareTo(),但是我还没有弄清楚如何使它工作。

我一直在尝试使用带有methodinfo对象的Expression.MakeBinary的重载,但我没有弄清楚。

帮助?

添加

因此,我尝试对String.GreaterThan等进行特殊情况处理,但仍然出现相同的错误:
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);

但是我仍然看到完全相同的异常(exception)。这对我来说毫无意义,因为如果我在做我想做的事情,则表达式中唯一的GreaterThan就是将Int32与Int32进行比较。

更多添加

从表达式树中删除了GreaterThan之后,我发现同样的错误很奇怪。

我一直在将这段代码作为单元测试的一部分来运行,并将Entity Framework连接到名为Effort的内存数据库中。所以我尝试对SqlServer。

我的原始代码不是特殊情况的字符串,但是对所有内容都使用GreaterThan,在针对SqlServer运行时以及针对Effort运行时,抛出了“GreaterThan not defined”异常。

我修改后的代码(特殊情况下的字符串)在SqlServer上工作正常,但是在对Effort运行时抛出了“GreaterThan not defined”异常。

看来,当Effort在表达式树中看到一个CompareTo()时,它将其转换为GreaterThan,这将导致我们熟悉的异常。

还有更多

在继续探讨该问题时,我确定Effort中存在一个错误,可以通过一个非常简单的示例来揭示该错误:
var foos = myDbContext.Foos.Where(f => f.fooid.CompareTo("Z") > 0).ToList();

当myDbContext连接到SqlServer数据库时,此方法工作正常,当连接到Effort数据库时,它将引发我们喜欢的异常。我已经在“努力”论坛上提交了错误报告。

对于那些正在阅读本文的人来说,在上面的第一个“已添加”部分中进行第二次尝试是正确的解决方案。它适用于SqlServer,而不适用于Effort,这是由于Effort中的错误所致。

附录

在上面的问题中,“转换”指的是什么。

实际上,我几乎不记得了。

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

我不确定完整的方法是否有意义,而该类的其余部分则没有,但是这里是:
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));
    }
}

最佳答案

这有效:

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

关于c# - 在字符串上构建表达式时如何实现LessThan等,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/24814278/

10-09 09:30