I have an ASP.NET Core 8 app with a long-lived service that refreshes an in-memory cache on a timer. I registered the service as a singleton (it’s a hosted background worker), and it needs to query the database periodically.
When I inject AppDbContext directly into the service constructor I get:
InvalidOperationException: Cannot consume scoped service 'AppDbContext' from singleton 'MyCacheService'.
Minimal Example:
// Program.cs
builder.Services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(builder.Configuration.GetConnectionString("Default")));
builder.Services.AddHostedService<MyCacheService>(); // MyCacheService is long-lived/singleton
public class MyCacheService : BackgroundService
{
private readonly AppDbContext _db; // <-- injecting scoped DbContext into singleton
public MyCacheService(AppDbContext db)
{
_db = db; // causes InvalidOperationException at startup
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var items = await _db.Items.ToListAsync(stoppingToken);
// refresh cache...
await Task.Delay(TimeSpan.FromMinutes(1), stoppingToken);
}
}
}
I know the exception comes from the lifetime mismatch (singleton depends on scoped), but I want to do this properly:
I’ve read hints about creating a scope manually with IServiceScopeFactory.
I’ve also seen AddDbContextFactory / IDbContextFactory recommended for background work.
Changing the service to scoped doesn’t fit my scenario because it’s a long-lived background worker -I’m using BackgroundService / IHostedService.
Here is my question: for a long-running background/hosted service that periodically queries the DB, what is the recommended, production-safe pattern to obtain a DbContext? Please show a short example of the recommended approach and mention any gotchas - concurrency, disposal, thread-safety, etc.
SaveChangesusingblocks to ensure both connections and transactions are closed as soon as possible. Connection pooling at the ADO.NET level eliminates the cost of opening new connections by effectively reseting and reusing older connections.DbContexton demand, which is itself scoped as a Unit of Work viausing().DbContextinstances are intended to be alive only as long as absolutely necessary so even a long-running background service should only construct when needed, use, and dispose when completed.