I am scheduling a daily recurring job in my ASP.NET Core application using Hangfire. My background task needs to access an Entity Framework Core DbContext to update database records.
The Dilemma with Scoped Services
Since DbContext is registered as a scoped service, resolving it directly in a background job often leads to an ObjectDisposedException if the scope closes early.
Example Job Code
// Class defining the job logic
public class EmailReportJob
{
private readonly IApplicationDbContext _context;
// Injecting scoped db context
public EmailReportJob(IApplicationDbContext context)
{
_context = context;
}
// Method triggered by Hangfire
public async Task ExecuteAsync()
{
// Perform operations on database
var pending = await _context.Reports.Where(r => !r.IsSent).ToListAsync();
// Send email logic here
}
}How does Hangfire handle service scope resolution when executing jobs? Does Hangfire create a new service scope for every job automatically, or do I need to explicitly create a scope using IServiceScopeFactory to avoid memory leaks and scoped services lifetime issues?
Hangfire can create your recurring job class through ASP.NET Core’s dependency injection container. That means you can inject services into the job’s constructor and let Hangfire resolve them when it runs the job—rather than building a service provider or calling the job yourself.
Register the job and its dependencies
With the
Hangfire.AspNetCoreintegration, register your job and any services it needs in the usual way. The following example uses SQL Server for Hangfire storage:The job can receive its dependencies through its constructor:
The recurring job expression records which method Hangfire should run. Hangfire creates the job instance through its activator when the scheduled execution occurs, so constructor injection works just as it does for other ASP.NET Core services.
Watch the service lifetimes
Scoped dependencies, such as a database context, are appropriate for job work. Hangfire creates a scope for job activation, so those services are resolved within that scope and disposed after execution. Avoid capturing scoped services in a singleton or manually resolving them from
app.Services.If Hangfire reports that it cannot create the job, check that the job type and its dependencies are registered, that the ASP.NET Core Hangfire integration is configured, and that
AddHangfireServer()is present to process jobs.The core challenge
When you register a recurring job with Hangfire, the job method is typically resolved by Hangfire’s own background process, not by your ASP.NET Core DI container. That means if you try to inject a service directly into the job method signature, Hangfire won’t know how to construct it. You end up with a runtime error or a null reference.The standard workaround
The most common approach is to resolve the service from the current `IServiceProvider` inside the job method itself. Hangfire runs inside the same process as your web app, so the provider is available as long as you configure it correctly during startup. Here is what the recurring job registration usually looks like: A few things to keep in mind with this pattern:When to use a hosted service instead
If your recurring job needs to run reliably even when no web requests are hitting the app, or if you want better control over retries and logging, consider moving the logic into a backgroundIHostedService. You can still resolve dependencies there, and Hangfire’s recurring job becomes just a thin trigger that enqueues work onto a queue processed by your hosted service. This decouples execution from the web pipeline entirely.Quick checklist
- Ensure Hangfire server is started and the dashboard is accessible.
- Register your services in the DI container before calling
- Resolve dependencies via
- Avoid scoped services unless you explicitly manage a scope per execution.
That is essentially it. The key is remembering that Hangfire’s job context gives you a back door into the DI container, but it does not automatically inject parameters for you.RecurringJob.AddOrUpdate.ctx.Resolve<T>()inside the job delegate.