调用MakeGenericMethod类的Invoke方法时发生StackOverflowException

时间:2013-06-18 05:44:44

标签: c# wcf reflection odata

我目前正在开发基于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;
        }
    }

1 个答案:

答案 0 :(得分:0)

我认为您的代码没有任何问题。发生此错误与内存有限有关。

请查看给定的链接:

http://www.dotnetperls.com/stackoverflowexception

谢谢, HITESH