I am using Hangfire and have a method to schedule jobs by assembly,type and method name. Using the default constructor works properly but all the methods have overloaded constructors that will be activated using Autofac.
//Works for a default constructor
Type type = Type.GetType("typestring, assemblystring");
var method = type.GetMethod("methodstring");
Expression[] args = new Expression[] { Expression.Constant(options,typeof(Options)) }; //All methods use the same parameters
var action = Expression.Lambda<Action>(Expression.Call(Expression.New(type), method, args));
RecurringJob.AddOrUpdate(action, "cronstring");
Trying to modify to support overloaded constructors (No default) I have this code.
Type type = Type.GetType("typestring, assemblystring");
var method = type.GetMethod("methodstring");
Expression[] args = new Expression[] { Expression.Constant(options,typeof(Options)) }; //All methods use the same parameters
var ctor = type.GetConstructors().ToList().FirstOrDefault();
var ctorParams = ctor.GetParameters();
var ctorArgs = new Expression[ctorParams.Length];
for (int i = 0; i != ctorParams.Length; ++i)
{
ParameterExpression param = Expression.Parameter(typeof(object), ctorParams[i].Name);
ctorArgs[i] = Expression.Convert(param, ctorParams[i].ParameterType);
}
var ctorExpress = Expression.New(ctor, ctorArgs);
var action = Expression.Lambda<Action>(Expression.Call(ctorExpress, method, args));
RecurringJob.AddOrUpdate(action, "cronstring");
I receive this error: InvalidOperationException: variable '{first constructor param}' of type 'System.Object' referenced from scope '', but it is not defined
I am not sure if I am missing something or am going the wrong way about this. I have limited experience using expressions.
If I'm not mistaken and there is another code that would compile your expression and calls it, providing as a parameters object[]
, and you have to put those parameters into constructor sequentially, then you can use sample code below:
static void Main(string[] args)
{
Type type = typeof(Foo);
var ctor = type.GetConstructor(new[] { typeof(int), typeof(string) });
var ctorParams = ctor.GetParameters();
var dynPar = Expression.Parameter(typeof(object[]), "d");
var ctorArgs = new Expression[ctorParams.Length];
for (int i = 0; i != ctorParams.Length; ++i)
{
var indVal = Expression.ArrayIndex(dynPar, Expression.Constant(i));
ctorArgs[i] = Expression.Convert(indVal, ctorParams[i].ParameterType);
}
//{ new Foo(Convert(d[0], Int32), Convert(d[1], String))}
var ctorExpress = Expression.New(ctor, ctorArgs);
//{ new Foo(Convert(d[0], Int32), Convert(d[1], String)).Run()}
var callRun = Expression.Call(ctorExpress, type.GetMethod("Run"));
//{ d => new Foo(Convert(d[0], Int32), Convert(d[1], String)).Run()}
var action = Expression.Lambda<Action<object[]>>(callRun, dynPar);
action.Compile()(new object[] { 1, "a" });
}
public class Foo
{
public Foo(int a, string b)
{
A = a;
B = b;
}
public int A { get; }
public string B { get; }
public void Run()
{
Console.WriteLine($"It is {A} and {B}");
}
}