1

Lets say I have:

public class ConcreteJob
{
    public void ExecuteConcreteJob(string someParam) { }
}

I am used to execute it on Hangfire scheduler:

var client = new BackgroundJobClient();
client.Enqueue<ConcreteJob>(job => job.ExecuteConcreteJob("test_string_param"));

Now I would like to replace concrete type ConcreteJob with its string representation "ConcreteJob". Use reflection and do something like this (very simple said):

client.Enqueue<"ConcreteJob">(job => job.ExecuteConcreteJob("test_string_param"));

I am getting lost in all the reflection...

Lambda as parameter makes this different from other threads on stack overflow.

What I have so far:

var jobType = Type.GetType("ConcreteJob");
MethodInfo methodInfo = typeof(BackgroundJobClient).GetMethod("Enqueue").MakeGenericMethod(jobType);
var funcDelegateType = typeof(Func<>).MakeGenericType(jobType);
dynamic lambda = Expression.Lambda(funcDelegateType,
        Expression.Call(
            Expression.Parameter(jobType, "job"),
            jobType.GetMethod("ExecuteCleanup"),
            Expression.Constant(UserPrincipalName))
    );
methodInfo.Invoke(client, new[] { (Action)(lambda) });
Miroslav Adamec
  • 1,060
  • 1
  • 15
  • 23
  • It would be better if we knew why you wanted this functionality? Surely you could just get the string name of the type with nameof(ConcreteJob) further down the line in processing? Again this is a complete guess without knowing why you actually want this functionality, I look forward to discussing :) – Zintom Aug 07 '20 at 10:25
  • You have stated perfectly what you want, so far so good, but show no attempt whatsoever at solving it, you only state "getting lost". Please show the code you have, the error that yields and what you have tried to solve it. You can't call `ExecuteConcreteJob()` on a type that you don't know at compile-time, you want to create that expression using strings as well? Therefore I closed as duplicate with the most basic instructions to start solving that. If you have a more specific problem, [edit] your question to show that problem. – CodeCaster Aug 07 '20 at 10:28
  • "You can't call ExecuteConcreteJob() on a type that you don't know at compile-time, you want to create that expression using strings as well" This is exactly the point. I will update the question with code I have now. – Miroslav Adamec Aug 07 '20 at 10:30
  • Why would you use reflection instead of interfaces? Interfaces with polymorphism is much cleaner and faster than refrelction. – pix Aug 07 '20 at 10:46
  • 1
    Using interfaces I am still facing the same problem. I have to point interface with its method. Or am I wrong? – Miroslav Adamec Aug 07 '20 at 11:22

1 Answers1

1

Lambda as parameter makes this different from other threads on stack overflow.

The ExecuteCleanup method accepts a delegate as a parameter, not a lambda expression. Hence, you need to compile your lambda expression to a delegate before passing it as a parameter.

Note that this is not a cheap operation. It makes sense to cache compiled delegates somewhere and re-use them.

Since your lambda expression accepts a parameter, you need to declare this parameter and pass it to the Expression.LambdaMethod.

MethodInfo methodInfo = typeof(BackgroundJobClient).GetMethod("Enqueue").MakeGenericMethod(jobType);
var funcDelegateType = typeof(Action<>).MakeGenericType(jobType);
ParameterExpression parameter = Expression.Parameter(jobType, "job");
LambdaExpression lambda = Expression.Lambda(funcDelegateType,
        Expression.Call(
            parameter,
            jobType.GetMethod("ExecuteCleanup"),
            Expression.Constant(UserPrincipalName)),
        parameter
    );
methodInfo.Invoke(client, new[] { lambda.Compile() });
Uranus
  • 1,690
  • 1
  • 12
  • 22