I've installed and configured Hangfire in my .NET Core web application's Startup class as follows (with a lot of the non-Hangfire code removed):
public class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseHangfireServer();
//app.UseHangfireDashboard();
//RecurringJob.AddOrUpdate(() => DailyJob(), Cron.Daily);
}
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddOptions();
services.Configure<AppSettings>(Configuration);
services.AddSingleton<IConfiguration>(Configuration);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IPrincipal>((sp) => sp.GetService<IHttpContextAccessor>().HttpContext.User);
services.AddScoped<IScheduledTaskService, ScheduledTaskService>();
services.AddHangfire(x => x.UseSqlServerStorage(connectionString));
this.ApplicationContainer = getWebAppContainer(services);
return new AutofacServiceProvider(this.ApplicationContainer);
}
}
public interface IScheduledTaskService
{
void OverduePlasmidOrdersTask();
}
public class ScheduledTaskService : IScheduledTaskService
{
public void DailyJob()
{
var container = getJobContainer();
using (var scope = container.BeginLifetimeScope())
{
IScheduledTaskManager scheduledTaskManager = scope.Resolve<IScheduledTaskManager>();
scheduledTaskManager.ProcessDailyJob();
}
}
private IContainer getJobContainer()
{
var builder = new ContainerBuilder();
builder.RegisterModule(new BusinessBindingsModule());
builder.RegisterModule(new DataAccessBindingsModule());
return builder.Build();
}
}
As you can see, I'm using Autofac for DI. I've set things up to inject a new container each time the Hangfire job executes.
Currently, I have UseHangfireDashboard()
as well as the call to add my recurring job commented out and I'm receiving the following error on the line referencing IPrincipal
:
System.NullReferenceException: 'Object reference not set to an instance of an object.'
I understand that Hangfire does not have an HttpContext
. I'm not really sure why it's even firing that line of code for the Hangfire thread. I'm ultimately going to need to resolve a service account for my IPrincipal dependency.
How can I address my issue with Hangfire and HttpContext?
Resolve dependencies using IServiceProvider You can use the IServiceCollection interface to create a dependency injection container. Once the container has been created, the IServiceCollection instance is composed into an IServiceProvider instance. You can use this instance to resolve services.
Hangfire is an open-source and well-documented task scheduler for ASP.NET and ASP.NET Core. It's multi-threaded, easily scalable, and offers a variety of job types. It's well structured, simple to use, and gives a powerful performance.
It stores the request and response information, such as the properties of request, request-related services, and any data to/from the request or errors, if there are any. ASP.NET Core applications access the HTTPContext through the IHttpContextAccessor interface. The HttpContextAccessor class implements it.
After performing these steps, open your browser and hit the http://<your-app>/hangfire URL to see the Dashboard. By default Hangfire allows access to Dashboard pages only for local requests. In order to give appropriate rights for production use, please see the Configuring Authorization section.
The main problem I'm having now is when I add UseHangfireServer, I then need to resolve HttpContext too
Found here Using IoC containers
HttpContext
is not availableRequest information is not available during the instantiation of a target type. If you register your dependencies in a request scope (
InstancePerHttpRequest
in Autofac,InRequestScope
in Ninject and so on), an exception will be thrown during the job activation process.So, the entire dependency graph should be available. Either register additional services without using the request scope, or use separate instance of container if your IoC container does not support dependency registrations for multiple scopes.
resolving scoped dependencies in .net core would require a request which is not available during startup when registering and activating jobs. Therefore make sure that your service required for activation during startup are not registered using scoped lifetimes.
services.AddTransient<IScheduledTaskManager, ScheduledTaskManageImplementation>();
All that is left now is to configure the application to use that service with the recurring job,
public class Startup {
public IContainer ApplicationContainer { get; private set; }
public Startup(IHostingEnvironment env) {
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public void Configuration(IApplicationBuilder app) {
// app.AddLogger...
//add hangfire features
app.UseHangfireServer();
app.UseHangfireDashboard();
//Add the recurring job
RecurringJob.AddOrUpdate<IScheduledTaskManager>(task => task.ProcessDailyJob(), Cron.Daily);
//app.UseMvc...
//...other code
}
public IServiceProvider ConfigureServices(IServiceCollection services) {
// Adding custom services
services.AddTransient<IScheduledTaskManager, ScheduledTaskManageImplementation>();
//add other dependencies...
// add hangfire services
services.AddHangfire(x => x.UseSqlServerStorage("<connection string>"));
//configure Autofac
this.ApplicationContainer = getWebAppContainer(services);
//get service provider
return new AutofacServiceProvider(this.ApplicationContainer);
}
IContainer getWebAppContainer(IServiceCollection service) {
var builder = new ContainerBuilder();
builder.RegisterModule(new BusinessBindingsModule());
builder.RegisterModule(new DataAccessBindingsModule());
builder.Populate(services);
return builder.Build();
}
//...other code
}
References
Hangfire 1.6.0
Integrate HangFire With ASP.NET Core
Using IoC containers
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With