현재 WCF Data Service Toolkit 라이브러리를 기반으로 OData API를 개발하고 있습니다.MakeGenericMethod 클래스의 Invoke 메서드를 호출 할 때 StackOverflowException이 발생했습니다.
현재 람다식이 많은 연산자를 사용하면 현재 문제가 발생합니다.
표현식에 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;
}
}
첫 번째 문제는 : 당신은 정적 메소드에 null 이외의 대상을 통과 한 것으로 나타났습니다. 'Invoke'의 첫번째 인자는'null'이어야합니다. 나는 그것이 차이를 만들지 않을 것이라고 생각하지만, 내가 당신이라면 여전히 고칠 것입니다. –
@ 존 쉘스 나는 당신과 동의하지 않습니다. MSDN에 따르면 null이거나 생성자를 정의하는 클래스의 인스턴스 일 수 있습니다. http://msdn.microsoft.com/en-us/library/a89hcwhh.aspx 첫 번째 인수를 null로 변경하면 잘 작동합니다. 그러나 여전히 동일한 오류가 있습니다. – user2495856
@ user2495856 문서에서 실제로 static * method * (있는 그대로)를 호출하는 경우 첫 번째 매개 변수는 똑바로 무시됩니다. – dlev