How should I inject (using the standard dependency injection) a DbContext
instance into a IHostedService
?
I currently have my IHostedService
class take a MainContext
(deriving from DbContext
) instance in the constructor.
When I run the application I get:
Cannot consume scoped service 'Microsoft.EntityFrameworkCore.DbContextOptions' from singleton 'Microsoft.Extensions.Hosting.IHostedService'.
So I tried to make the DbContextOptions
transient by specifying:
services.AddDbContext<MainContext>(options => options.UseSqlite("Data Source=development.db"), ServiceLifetime.Transient);
in my Startup
class.
But the error remains the same, even though, according to this solved Github issue the DbContextOptions
passed should have the same lifetime specified in the AddDbContext
call.
I can't make the database context a singleton otherwise concurrent calls to it would yield concurrency exceptions (due to the fact that the database context is not guaranteed to be thread safe).
First, DbContext is a lightweight object; it is designed to be used once per business transaction. Making your DbContext a Singleton and reusing it throughout the application can cause other problems, like concurrency and memory leak issues. And the DbContext class is not thread safe.
In order to avoid that you should always use "scoped" on the DBContext. Correct. Maybe there are cases in which you need a transient EF-Context - but usually you should stick to scoped.
The AddDbContext extension method registers DbContext types with a scoped lifetime by default.
A good way to use services inside of hosted services is to create a scope when needed. This allows to use services / db contexts etc. with the lifetime configuration they are set up with. Not creating a scope could in theory lead to creating singleton DbContexts and improper context reusing (EF Core 2.0 with DbContext pools).
To do this, inject an IServiceScopeFactory
and use it to create a scope when needed. Then resolve any dependencies you need from this scope. This also allows you to register custom services as scoped dependencies should you want to move logic out of the hosted service and use the hosted service only to trigger some work (e.g. regularly trigger a task - this would regularly create scopes, create the task service in this scope which also gets a db context injected).
public class MyHostedService : IHostedService { private readonly IServiceScopeFactory scopeFactory; public MyHostedService(IServiceScopeFactory scopeFactory) { this.scopeFactory = scopeFactory; } public void DoWork() { using (var scope = scopeFactory.CreateScope()) { var dbContext = scope.ServiceProvider.GetRequiredService<MyDbContext>(); … } } … }
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