Is it possible to add a Recurring job that calls a function via method.Invoke?

Im trying to add a function as a recurring job, but im trying to get the function name by code as follows…

      string className = task.Function.Name;
      Type t = Type.GetType(namespace + className);
      MethodInfo method = t.GetMethod("Execute");
     RecurringJob.AddOrUpdate(task.Function.Name, () => method.Invoke(null, null), task.CRON, TimeZoneInfo.Local);

It adds the task as a recurring job, but when it executes the task, the follow error appears

No parameterless constructor defined for this object.

System.MissingMethodException: No parameterless constructor defined for this object.
at System.RuntimeTypeHandle.CreateInstance(RuntimeType type, Boolean publicOnly, Boolean noCheck, Boolean& canBeCached, RuntimeMethodHandleInternal& ctor, Boolean& bNeedSecurityCheck)
at System.RuntimeType.CreateInstanceSlow(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.RuntimeType.CreateInstanceDefaultCtor(Boolean publicOnly, Boolean skipCheckThis, Boolean fillCache, StackCrawlMark& stackMark)
at System.Activator.CreateInstance(Type type, Boolean nonPublic)
at System.Activator.CreateInstance(Type type)
at Hangfire.JobActivator.ActivateJob(Type jobType)
at Hangfire.JobActivator.SimpleJobActivatorScope.Resolve(Type type)
at Hangfire.Server.CoreBackgroundJobPerformer.Perform(PerformContext context)
at Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass8_0.b__0()
at Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter filter, PerformingContext preContext, Func1 continuation) at Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass8_1.<PerformJobWithFilters>b__2() at Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter filter, PerformingContext preContext, Func1 continuation)
at Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass8_1.b__2()
at Hangfire.Server.BackgroundJobPerformer.PerformJobWithFilters(PerformContext context, IEnumerable`1 filters)
at Hangfire.Server.BackgroundJobPerformer.Perform(PerformContext context)
at Hangfire.Server.Worker.PerformJob(BackgroundProcessContext context, IStorageConnection connection, String jobId)

I don’t believe that even if you run just the method.Invoke(null, null) this will work, since the target is null, but when I tried:
method.Invoke(Activator.CreateInstance(t), null)
method executes, but Hangfire still throws the same exception.

After digging through code I think that the issue is that the: RecurringJob.AddOrUpdate will resolve the the type of the target on which we want our method to be executed to: System.Reflection.MethodBase which will upon execution provide described error.

There is an overload ofr AddOrUpdate: RecurringJob.AddOrUpdate<T> but I don’t think that this suite your needs since you are resolving the type dynamically.

I don’t think that what you are trying to do is possible without some modification to the approach, maybe making the dynamically constructed type inherit the same interface so you can try using the overload or something like that.

Update:
The target type that will be used to call “Invoke” method is sotred in sql table: “Hash”, you could find your type through this query:
SELECT * FROM [your-db-name].[HangFire].[Hash] WHERE [Key] = 'recurring-job:Invoke' AND Field = 'Job'

There is a possibility of doing something with .UseActivator extension, but I am still tinkering about it, but it provides an opening for injecting a custom IoC logic, maybe…

2 Likes

Try something like this:

string className = task.Function.Name;
Type type = Type.GetType(namespace + className);
MethodInfo method = type.GetMethod("Execute");
Job job = new Job(type, method /*, can also specify args (if method has those) */);
new RecurringJobManager().AddOrUpdate(task.Function.Name, job, task.CRON, TimeZoneInfo.Local);

But this is just an example. In the real-world application, you should get IRecurringJobManager instance from DI container (or at least cache it).

3 Likes

This worked for me. Nice! I went towards unnecessarily complex way to approach to this problem.

It works like a charm, thanks :smiley: