2013-06-18 4 views
1

В настоящее время я разрабатываю OData API на основе библиотеки инструментов Data Service Toolkit.StackOverflowException при вызове метода Invoke класса MakeGenericMethod

Моя текущая проблема возникает, когда выражение Lambda имеет много операторов.

Из моих экспериментов StackOverflowException возникает, когда выражение содержит более 356 операторов.

Приведенная ниже инструкция содержит ошибку.

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; 
     } 
    } 
+0

Первая проблема: вы, кажется, передавая непустой цель статический метод. Первый аргумент 'Invoke' должен быть' null'. Я подозреваю, что это не изменит ситуацию, но я все равно исправлю это, если бы я был вами. –

+0

@JonSkeet Я не согласен с тобой. Согласно MSDN, это может быть нуль или экземпляр класса, который определяет конструктор. http://msdn.microsoft.com/en-us/library/a89hcwhh.aspx Когда я изменил первый аргумент как null, он также работает. Однако есть еще одна ошибка. – user2495856

+0

@ user2495856 Документы фактически говорят, что если вы вызываете статический * метод * (как и вы), тогда первый параметр игнорируется. – dlev

ответ

0

Я думаю, что в вашем коде нет никаких проблем. Эта ошибка возникла в связи с ограниченной памятью.

Пожалуйста, посмотрите на данную ссылку:

http://www.dotnetperls.com/stackoverflowexception

Спасибо, Hitesh

+0

Я прочитал эту статью, но спасибо за обмен. Как мы можем преодолеть его ограничение? – user2495856

Смежные вопросы