Introduction
My application instantiates an object using method chaining so it is generated and configured like so:
var car = new Car("Ferrari").Doors(2).OtherProperties(x = x.Color("Red"));
Problem
I have a requirement to dynamically generate this object at runtime - the chained methods needed for configuration will be determined at runtime so everything must be dynamically assembled on the fly. I've used reflection in the past to create simple objects like new Car("Ferrari", 2, "Red")
- I'm cool with that - but never anything with chained methods containing lambda expressions as parameters - these two factors really have me stuck. I've looked into expression trees and believe this is part of the solution to create the dynamic expression parameters but am totally stuck trying to figure out how to stitch that together with reflection to create the base object and the additional chained methods.
Thanks and Appreciation
In advance for taking the time to look at my problem and for any guidance or information you might be able to provide.
UPDATE: Conclusion
Many thanks to dasblinkenlight and Jon Skeet for their answers. I picked dasblinkenlight's answer because his code sample got me off and running immediately. For the method chaining I basically used the same looping approach in the accepted answer so I won't repeat that code but below is the code I wrote to dynamically convert expression tree method calls into action delegates that could then be executed via reflection Invoke()
as outlined in dasblinkenlight's answer. This, as Jon pointed out was really the crux of the problem.
Helper class to store method meta data.
public struct Argument
{
public string TypeName;
public object Value;
}
public class ExpressionTreeMethodCall
{
public string MethodName { get; set; }
public IList<Argument> Arguments { get; set; }
public ExpressionTreeMethodCall()
{
Arguments = new List<Argument>();
}
}
Static method to assemble a lambda expression method call and then return it as an action delegate to be executed elsewhere (passed as argument to Invoke()
in my case).
public static Action<T> ConvertExpressionTreeMethodToDelegate<T>(ExpressionTreeMethodCall methodData)
{
ParameterExpression type = Expression.Parameter(typeof(T));
var arguments = new List<ConstantExpression>();
var argumentTypes = new List<Type>();
foreach (var a in methodData.Arguments)
{
arguments.Add(Expression.Constant(a.Value));
argumentTypes.Add(Type.GetType(a.TypeName));
}
// Creating an expression for the method call and specifying its parameter.
MethodCallExpression methodCall = Expression.Call(type, typeof(T).GetMethod(methodData.MethodName, argumentTypes.ToArray()), arguments);
return Expression.Lambda<Action<T>>(methodCall, new[] { type }).Compile();
}