我目前正在开发基于 WCF 数据服务工具包库的 OData API。
当 Lambda 表达式有许多运算符时,就会发生我当前的问题。
根据我的实验,当表达式的运算符超过 356 个时会发生 StackOverflowException。
以下语句是发生错误的地方。
return orderByMethod.Invoke(enumerable, new object[] { enumerable, operand.Compile() });
以下代码是整个代码。
/// <summary>
/// Executes a Linq2Objects expression to a given <see cref="IEnumerable" /> object.
/// </summary>
/// <param name="methodName">A string that indicates the name of the method.</param>
/// <param name="enumerable">An <see cref="IEnumerable" /> object that will be filtered.</param>
/// <param name="expression">An <see cref="Expression" /> to be applied to the <see cref="IEnumerable" /> object.</param>
/// <returns>A filtered <see cref="IEnumerable" /> object.</returns>
public static object ExecuteLinq2ObjectsImplementation(string methodName, IEnumerable<object> enumerable, Expression expression)
{
var orderByClause = expression as UnaryExpression;
var operand = null == orderByClause ? expression as LambdaExpression : orderByClause.Operand as LambdaExpression;
// The following conditional statement is added to avoid the stack overflow exception.
int operatorNumber = (operand.ToString().Split('"').Length - 1) / 2;
// The number is evaluated by executing vary queries. It means that dimension members can be selected up to 356 in a dimension.
const int maximumOperatorNumber = 356;
if (operatorNumber <= maximumOperatorNumber)
{
var whereInfo = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(mi => mi.Name == methodName && mi.GetParameters()[1].ParameterType.GetGenericArguments().Count() == 2);
var currentType = enumerable.GetType();
var seedElementType = currentType.IsArray ? currentType.GetElementType() : currentType.GetGenericArguments().ElementAt(0);
var genericArguments = new List<Type> { seedElementType };
if (whereInfo.GetGenericArguments().Count() > 1)
{
genericArguments.Add(operand.Body.Type);
}
var orderByMethod = whereInfo.MakeGenericMethod(genericArguments.ToArray());
return orderByMethod.Invoke(enumerable, new object[] { enumerable, operand.Compile() });
}
else
{
throw new StackOverflowException("The OData query is too long.");
}
}
你对此有什么想法吗?
更新...
这是我为可能遇到相同问题的人提供的解决方案。
/// <summary>
/// Executes a Linq2Objects expression to a given <see cref="IEnumerable" /> object.
/// </summary>
/// <param name="methodName">A string that indicates the name of the method.</param>
/// <param name="enumerable">An <see cref="IEnumerable" /> object that will be filtered.</param>
/// <param name="expression">An <see cref="Expression" /> to be applied to the <see cref="IEnumerable" /> object.</param>
/// <returns>A filtered <see cref="IEnumerable" /> object.</returns>
public static object ExecuteLinq2ObjectsImplementation(string methodName, IEnumerable<object> enumerable, Expression expression)
{
var orderByClause = expression as UnaryExpression;
var operand = null == orderByClause ? expression as LambdaExpression : orderByClause.Operand as LambdaExpression;
var whereInfo = typeof(Enumerable).GetMethods(BindingFlags.Static | BindingFlags.Public).First(mi => mi.Name == methodName && mi.GetParameters()[1].ParameterType.GetGenericArguments().Count() == 2);
var currentType = enumerable.GetType();
var seedElementType = currentType.IsArray ? currentType.GetElementType() : currentType.GetGenericArguments().ElementAt(0);
var genericArguments = new List<Type> { seedElementType };
if (whereInfo.GetGenericArguments().Count() > 1)
{
genericArguments.Add(operand.Body.Type);
}
// The following conditional statement is added to avoid the stack overflow exception.
int operatorNumber = (operand.ToString().Split('"').Length - 1) / 2;
// If the number of selected members in a dimension is equal or less than 356, then the data will be sorted based on the current dimmension with the $orderby query.
// Otherwise, the method will not perform sorting to avoid StackOverflowException.
// For your guidance the number is evaluated by executing vary queries.
const int maximumOperatorNumber = 356;
if (operatorNumber <= maximumOperatorNumber)
{
var orderByMethod = whereInfo.MakeGenericMethod(genericArguments.ToArray());
return orderByMethod.Invoke(null, new object[] { enumerable, operand.Compile() });
}
else {
return enumerable;
}
}