【发布时间】:2011-04-21 13:27:08
【问题描述】:
我正在使用 Linq to SQL 执行一个非常简单的查询。我正在创建表达式,然后将其传递给 Where() 扩展方法。当我尝试实际执行查询时,Linq 内部会抛出 StackOverflowException。代码如下:
int expectedCount = 4;
Expression<Func<Thing, bool>> expression = ...;
//Expression looks like (LocaleID = 1 && GenderID ==1 && (TimeFrameID == 2007 || TimeFrameID == 2008))
using (XYZDataContext context = new XYZDataContext())
{
int count = context.Things.Where(expression).Count();
//...
}
这里是表达式的DebugView:
.Lambda #Lambda1<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
.Invoke (.Lambda #Lambda2<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>)($o) & .Invoke (.Lambda #Lambda3<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>)($o)
}
.Lambda #Lambda2<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
.Invoke (.Lambda #Lambda4<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>)($o) & .Invoke (.Lambda #Lambda5<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>)($o)
}
.Lambda #Lambda3<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
.Invoke (.Lambda #Lambda6<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>)($o) | .Invoke (.Lambda #Lambda7<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>)($o)
}
.Lambda #Lambda4<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
$o.LocaleID == .Constant<System.Nullable`1[System.Int32]>(1)
}
.Lambda #Lambda5<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
$o.GenderID == .Constant<System.Nullable`1[System.Int32]>(1)
}
.Lambda #Lambda6<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
$o.TimeframeID == .Constant<System.Nullable`1[System.Int32]>(2007)
}
.Lambda #Lambda7<System.Func`2[XYZ.DataAccess.Thing,System.Boolean]>(XYZ.DataAccess.Thing $o)
{
$o.TimeframeID == .Constant<System.Nullable`1[System.Int32]>(2008)
}
这个表达式对我来说似乎是正确的,而且相当简单。当我阅读调试视图时,我看到:
((LocaleID == 1 && GenderID == 1) && (TimeFrameID == 2007 || TimeFrameID == 2008))
...这是正确的。
更新 1
删除 一个 的内部 or'd 子句,它工作正常。因此,同时拥有内部 or'd 子句会以某种方式破坏从 LINQ 到 SQL 的转换。
更新 2
我无法让调试器单步执行 .NET Framework 代码 - 我已尝试使用 Reflector 以及仅使用 Visual Studio 来执行此操作。我进去过一次,但一般来说是行不通的。我确实遇到 StackOverflowException 的一次发生在:
ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, object state, bool ignoreSyncCtx)
更新 3
这是用于创建表达式的代码。有太多代码要发布,但它的核心在下面。我有允许我构建复杂的多级查询并将其序列化为 JSON 和 XML 的类。在核心,查询的每个部分都是使用以下方法构建的,然后是 Or'd 和 And'd 一起:
public class LinqSearchField<T, V> : ISearchField
{
public string Name { get; private set; }
public Expression<Func<T, V>> Selector { get; private set; }
public Expression<Func<T, bool>> LessThan(V value)
{
return Expression.Lambda<Func<T, bool>>(Expression.LessThan(this.Selector.Body, GetConstant(value)), this.Selector.Parameters);
}
public Expression<Func<T, bool>> LessThanOrEqual(V value)
{
return Expression.Lambda<Func<T, bool>>(Expression.LessThanOrEqual(this.Selector.Body, GetConstant(value)), this.Selector.Parameters);
}
public Expression<Func<T, bool>> Equal(V value)
{
return Expression.Lambda<Func<T, bool>>(Expression.Equal(this.Selector.Body, GetConstant(value)), this.Selector.Parameters);
}
public Expression<Func<T, bool>> NotEqual(V value)
{
return Expression.Lambda<Func<T, bool>>(Expression.NotEqual(this.Selector.Body, GetConstant(value)), this.Selector.Parameters);
}
public Expression<Func<T, bool>> GreaterThan(V value)
{
return Expression.Lambda<Func<T, bool>>(Expression.GreaterThan(this.Selector.Body, GetConstant(value)), this.Selector.Parameters);
}
public Expression<Func<T, bool>> GreaterThanOrEqual(V value)
{
return Expression.Lambda<Func<T, bool>>(Expression.GreaterThanOrEqual(this.Selector.Body, GetConstant(value)), this.Selector.Parameters);
}
private ConstantExpression GetConstant(V value)
{
return Expression.Constant(value, typeof(V));
}
public Expression<Func<T, bool>> Null()
{
return Expression.Lambda<Func<T, bool>>(Expression.Equal(this.Selector.Body, Expression.Constant(null)), this.Selector.Parameters);
}
public Expression<Func<T, bool>> NotNull()
{
return Expression.Lambda<Func<T, bool>>(Expression.NotEqual(this.Selector.Body, Expression.Constant(null)), this.Selector.Parameters);
}
}
这是 And 代码(OR 代码相同,但使用 Expression.And 代替):
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
{
ParameterExpression[] parameters = expression1.Parameters.Union(expression2.Parameters).Distinct(new ParameterExpressionComparer()).ToArray();
InvocationExpression invocationExpression1 = Expression.Invoke(expression1, parameters);
InvocationExpression invocationExpression2 = Expression.Invoke(expression2, parameters);
Expression binaryExpression = null;
//And the current expression to the previous one.
binaryExpression = Expression.AndAlso(invocationExpression1, invocationExpression2); //Or OrElse.
//Wrap the expression in a lambda.
return Expression.Lambda<Func<T, bool>>(binaryExpression, parameters);
}
更新 4
它可能会不受欢迎,但这里是sample which reproduces this issue。我真的需要弄清楚这里发生了什么。
【问题讨论】:
-
如果您在
AreEqual方法之外评估expression是否也会出现Exception? -
是的,我会从这里的代码中删除该部分以使其更清晰。
-
您是否介意向我们展示您是如何实际构建表达式的。我怀疑您的构建不正确,因此会导致问题。您向我们展示的内容似乎与调试视图不对应,并且似乎由 lambdas 组成。
-
我在上面添加了更新 3。涉及大量代码,但这是用于构建查询的核心代码。
标签: linq-to-sql lambda expression-trees stack-overflow