現在、WCF Data Service Toolkit Library に基づいて OData API を開発しています。
現在の問題は、ラムダ式に多くの演算子がある場合に発生します。
私の実験では、式に 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;
}
}