I'm executing a pretty simple query using Linq to SQL. I'm creating the expression and then passing it to the Where() extension method. The Linq internals are throwing a StackOverflowException when I attempt to actually execute the query. Here is the code:
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();
//...
}
And here is the DebugView of the expression:
.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)
}
The expression appears correct to me and it is fairly trivial. When I read the debug view I see:
((LocaleID == 1 && GenderID == 1) && (TimeFrameID == 2007 || TimeFrameID == 2008))
...which is correct.
Update 1
Removing one of the inner or'd clauses, it works fine. So having both inner or'd clauses is breaking the translation from LINQ to SQL, somehow.
Update 2
I'm having trouble getting the debugger to step into .NET Framework code - I've tried using Reflector to do it as well as just Visual Studio. I got in once but in general stepping in is not working. The one time I did get in the StackOverflowException was occurring in:
ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, object state, bool ignoreSyncCtx)
Update 3
Here is the code that is used to create the Expression. There is way too much code to post but the heart of it is below. I have classes which allow me to build a complex multi-level query and serialize it to JSON and XML. At the core, each piece of the query is built using the following methods and then is Or'd and And'd together:
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);
}
}
Here is the And code (the OR code is the same but with Expression.And instead):
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);
}
Update 4
It will probably be frowned upon but here is a sample which reproduces this issue. I really need to figure out what's going on here.