我有一个包,我正在构建表达式树,通过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等。在这种情况下,我得到一个例外:
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);
但我仍然看到完全相同的异常。这对我来说没有意义,因为如果我正在做我认为我正在做的事情,那么表达式中唯一的GreaterThan就是将Int32与Int32进行比较。
我发现从表达式树中删除GreaterThan后,我发现同样的错误很奇怪。
我一直在运行此代码作为单元测试的一部分,实体框架连接到名为Effort的内存数据库。所以我尝试对抗SqlServer。
我的原始代码,没有特殊情况字符串,但是对于所有内容都使用了GreaterThan,在针对SqlServer运行时抛出“GreaterThan not defined”异常,并且在针对Effort运行时抛出。
我的修改后的代码,即特殊字符串,对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));
}
}
答案 0 :(得分:6)
这有效:
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);