I can't simply inject the DB context so I have to rely on injecting IServiceScopeFactory
and obtain it from the registered services like this.
AppDbContext Context()
{
using IServiceScope scope = Services.CreateScope();
return scope.ServiceProvider.GetRequiredService<AppDbContext>();
}
It doesn't work when I invoke it elsewhere like this.
async Task Seed(int count)
{
AppDbContext context = Context();
...
await context.AddAsync(new Thing { ... });
await context.SaveChangesAsync();
}
According to the error message, the service is disposed. And, indeed, since I'm setting it in the scope of using
statement, as the method ends, it should be expected. I see two ways to deal with it.
I dislike both of them. The first one leads to code redundancy. The other one may block the otherwise scoped DB access.
What would be a better option to deal with it?
Initially, I tried to create options and create the DB access internally in my class. However, that stopped working when I started to create migrations, as dotnet ef migrations add Init
requires the service to be registered in Program
, hence forcing me to obtain it using the method above.
DbContextOptions<AppDbContext> ContextOptions()
{
DbContextOptionsBuilder<AppDbContext> builder = new();
builder.UseInMemoryDatabase("memento");
//builder.UseSqlServer(a => { });
return builder.Options;
}
The normal solution is to create a scope around the work that needs it. Since your service is periodic, I recommend a scope for each time-based invocation. That's what I do for my scheduled services.
After injecting an IServiceProvider
, you can use that to create a scope and then create whatever "processor" type you need (which, in turn, would inject a DbContext
):
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
using IServiceScope scope = _serviceProvider.CreateScope();
AppDbContext context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
}
}
Scopes are quite fast to create; the normal pattern for ASP.NET is to create a new scope for every request.