Steve Coleman
Steve Coleman

Reputation: 2027

C# Expression Call, Can't seem to get the method info and parameters correct

Expression> lambda = Expression.Lambda >(methodCall, allParameters);Here is the error:

An exception of type 'System.ArgumentException' occurred in... Additional information: Expression of type 'System.Func`2[Business.Entities.SlateEmployee,System.Boolean]' cannot be used for return type 'System.Boolean'

Can anyone see what is wrong with my code.

    public class ClearValueAction : ActionRuleBase
    {
        /// <summary>
        /// Virtual method to call during actions
        /// </summary>
        /// <returns></returns>
        public override bool ExecuteAction<T>(T dataObject)
        {
            PropertyInfo propertyInfo = typeof (T).GetProperty(Left);
            if (propertyInfo != null)
            {
                ReflectionUtils.SetObjectValue(dataObject, null, propertyInfo);
                return true;
            }
            return false;
        }

        /// <summary>
        /// Making an expression to call the ExecuteAction function when this is compiled
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public override Func<T, bool> CompileRule<T>()
        {

            //return Expression.Lambda<Func<T, bool>>().Compile();
                ParameterExpression allParameters;
                var methodCall = CreateLambda<T>("ExecuteAction", "dataObject", out allParameters);


    //ERROR IS HERE//////////////////////////////////////////////
               Expression<Func<T, bool>> lambda = Expression.Lambda <Func<T, bool>>(methodCall, allParameters);
            return lambda.Compile();

        }

        private LambdaExpression CreateLambda<T>(string methodName, string methodInputParamName, out ParameterExpression allParameters)
        {

            allParameters = Expression.Parameter(typeof(T), methodInputParamName);
            var c = Expression.Constant(methodInputParamName);
            MethodInfo methodInfo = typeof(ClearValueAction).GetMethods().First(m => m.Name == methodName && m.IsGenericMethod).MakeGenericMethod(typeof(T));
            MethodCallExpression generateCallExpression = GenerateCallExpression(this, methodInfo, allParameters);
            return Expression.Lambda(generateCallExpression, allParameters);
        }

        /// <summary>
        /// Generate expression call
        /// </summary>
        /// <param name="instance">If instance is NULL, then it method will be treated as static method</param>
        private static MethodCallExpression GenerateCallExpression(object instance, MethodBase method, ParameterExpression allParameters)
        {
            var methodInfo = method as MethodInfo;
            // it's non static method
            if (instance != null)
            {
                var instanceExpr = Expression.Convert(Expression.Constant(instance), instance.GetType());
                return Expression.Call(instanceExpr, methodInfo, allParameters);
            }

            // it's static method
            return Expression.Call(methodInfo, allParameters);
        }

        private static List<Expression> GenerateParameters(MethodBase method, out ParameterExpression allParameters)
        {
            allParameters = Expression.Parameter(typeof(object[]), "params");
            ParameterInfo[] methodMarameters = method.GetParameters();
            List<Expression> parameters = new List<Expression>();
            for (int i = 0; i < methodMarameters.Length; i++)
            {
                var indexExpr = Expression.Constant(i);
                var item = Expression.ArrayIndex(allParameters, indexExpr);
                var converted = Expression.Convert(item, methodMarameters[i].ParameterType);
                parameters.Add(converted);
            }

            return parameters;
        }
}

Upvotes: 2

Views: 1939

Answers (1)

StriplingWarrior
StriplingWarrior

Reputation: 156524

Your CreateLambda<T>() function is already generating a lambda expression. By calling Expression.Lambda<>() on the result, you're trying to create a lambda expression that returns a lambda expression.

Just change it so that you know at compile-time what type of expression it's generatng:

private Expression<Func<T, bool>> CreateLambda<T>(string methodName, string methodInputParamName, out ParameterExpression allParameters)
{
    allParameters = Expression.Parameter(typeof(T), methodInputParamName);
    var c = Expression.Constant(methodInputParamName);
    MethodInfo methodInfo = typeof(ClearValueAction).GetMethods().First(m => m.Name == methodName && m.IsGenericMethod).MakeGenericMethod(typeof(T));
    MethodCallExpression generateCallExpression = GenerateCallExpression(this, methodInfo, allParameters);
    return Expression.Lambda<Func<T, bool>>(generateCallExpression.Dump(), allParameters);
}

Then compile that result directly:

public Func<T, bool> CompileRule<T>()
{
    ParameterExpression allParameters;
    var methodCall = CreateLambda<T>("ExecuteAction", "dataObject", out allParameters);
    return methodCall.Compile();
}

Upvotes: 3

Related Questions