HangFire.SimpleInjector icon indicating copy to clipboard operation
HangFire.SimpleInjector copied to clipboard

Recurring job, cannot resolve type

Open provanguard opened this issue 6 years ago • 7 comments

I took the Activator code from this solution and placed it inside my .NET Core 2.0 MVC Web API project. I added it to the Hangfire configuration (this part is not documented here, so could be wrong):

GlobalConfiguration.Configuration.UseActivator(new SimpleInjectorJobActivator(_container));
app.UseHangfireServer();

I found out that while simple recurring job to print out something to the Console works fine, when attempting to run a job that gets constructor parameters via SimpleInjector, it fails with an "Unable to resolve service" error:

Unable to resolve service for type 'xxx.Infrastructure.Persistence.IRepository' while attempting to activate 'xxx.xxx.xxx.Jobs.NewJob'.

Since the whole point of using this integration solution is to make it work with SimpleInjector, there appears to be a fundamental problem with it. Or simply, we are missing proper documentation.

provanguard avatar Apr 11 '18 06:04 provanguard

I am adding stack trace for reference.

System.InvalidOperationException
Unable to resolve service for type 'xxx.Infrastructure.Persistence.IRepository' while attempting to activate 'xxx.xxx.xxx.Jobs.NewJob'.

System.InvalidOperationException: Unable to resolve service for type 'xxx.Infrastructure.Persistence.IRepository' while attempting to activate 'xxx.xxx.xxx.Jobs.NewJob'.
   at Microsoft.Extensions.Internal.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider)
   at Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)
   at Hangfire.Server.CoreBackgroundJobPerformer.Perform(PerformContext context)
   at Hangfire.Server.BackgroundJobPerformer.<>c__DisplayClass8_0.<PerformJobWithFilters>b__0()
   at Hangfire.Server.BackgroundJobPerformer.InvokePerformFilter(IServerFilter filter, PerformingContext preContext, Func`1 continuation)
   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)

provanguard avatar Apr 11 '18 07:04 provanguard

hi @provanguard, sorry for this, but I did not do NET in a long time now, so maybe it needs update to work with latest simpleinjector? if you want to do PR that would be great ;)

devmondo avatar Apr 11 '18 10:04 devmondo

I got it to work eventually. I think it is the missing documentation here that stands in the way of using this library (apart from the missing NET Standard target for the nuget package). Since it was my first attempt at the topic of using Hangfire and SimpleInjector in an ASP.NET Core application, I don't think I can provide you with an authoritative guide how to get it to work in the best way.

provanguard avatar Apr 11 '18 11:04 provanguard

@provanguard glad you got it working, you are welcome to add docs or simple guide anytime.

devmondo avatar Apr 11 '18 12:04 devmondo

@provanguard Can you explain what you did to get it working? It'll help the rest of us that are struggling with it.

Thanks!

speige avatar Apr 24 '18 05:04 speige

I was able to get this working.

Neither of these worked

			GlobalConfiguration.Configuration.UseActivator(new SimpleInjectorJobActivator(_diContainer));
			JobActivator.Current = new SimpleInjectorJobActivator(_diContainer);

But this worked:

			services.AddHangfire(config =>
			{
				config.UseActivator(new SimpleInjectorJobActivator(_diContainer));
			});

speige avatar Apr 24 '18 05:04 speige

The below configuration method works for me. Looking at your answer @speige it seems like you found a more elegant way to do it. So merge of both solution would probably be best.

private void ConfigureJobs(IApplicationBuilder app)
{
	GlobalConfiguration.Configuration.UseActivator(new SimpleInjectorJobActivator(_container));
	GlobalConfiguration.Configuration.UseMongoStorage(
		Configuration.GetSection("MongoConnection:ConnectionString").Value,
		Configuration.GetSection("MongoConnection:DatabaseName").Value);
	GlobalJobFilters.Filters.Add(new AutomaticRetryAttribute { Attempts = 1 });
	
	_backgroundJobServer = new BackgroundJobServer(); // We use this instead of app.UseHangfireServer()
	//app.UseHangfireServer(); // This would cause Hangfire to use MS DI, which would fail to run the jobs
	app.UseHangfireDashboard();

	var recurringJobs = _container.GetTypesToRegister(typeof(IRecurringJob), new[] { typeof(IRecurringJob).Assembly });

	List<Registration> list = new List<Registration>();
	foreach (var type in recurringJobs) list.Add(Lifestyle.Singleton.CreateRegistration(type, _container));
	var recurringJobsRegistrations = list.ToArray(); // This call is needed to prevent double enumeration

	_container.RegisterCollection<IRecurringJob>(recurringJobsRegistrations);
}

There is more code needed in other places to complete jobs registration from the example above.

provanguard avatar Apr 24 '18 06:04 provanguard