Reputation: 3739
I have the following in Startup.cs:
services.AddDbContext<MyDbContext>(options => options.UseSqlite(Configuration.GetConnectionString("Sqlite")));
Now, I'd like to fire up an instance of MyDbContext
that has a transient lifetime. The reason for doing so is because I'm populating my cache at startup. So how can I get an instance of MyDbContext
that I'm responsible for disposing of myself? I have an IServiceProvider
ready at hand.
serviceProvider.GetRequiredService<MyDbContext>();
throws an exception which says it's out of scope.
I understand why the exception is getting thrown, but I'm not sure what the best way of getting around it is.
Upvotes: 2
Views: 926
Reputation: 192417
joe-audette's answer from 2018 was not clear to me. It relies on serviceProvider
and I am not sure where that comes from. Also after creating a scope, it does this:
var scopedServices = scope.ServiceProvider;
...which looks like it is retrieving... a ServiceProvider object with a confusing name (scopedServices
). But the ServiceProvider... is the thing it started with.? And then it shows GetRequiredService()
but doesn't show how to use the result of that call, and doesn't even show storing the thing it gets. None of that is clear.
If I am writing a Minimal API app using aspnet core, there is no ambient serviceProvider
object. There is a WebAppBuilder, and out of the builder I can get an app, of type WebApplication.
Given that, here's what worked for me. (.NET core 8.0)
using Microsoft.EntityFrameworkCore;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDbContext<TodoDb>(opt => opt.UseInMemoryDatabase("TodoList"));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
...
var app = builder.Build();
...add all my methods here...
// initialize the in-memory DB just to start out
using (var scope = app.Services.CreateScope())
{
var serviceProvider = scope.ServiceProvider;
using (var db = new TodoDb(serviceProvider.GetRequiredService<DbContextOptions<TodoDb>>())) {
var todo = new Todo("go grocery shopping");
db.Todos.Add(todo);
db.SaveChanges();
}
}
// start listening
var port = Environment.GetEnvironmentVariable("PORT") ?? "8080";
var url = $"http://0.0.0.0:{port}";
app.Run(url);
This article gave me the crucial tip, to create a scope and get the service provider from the scope.
I've got to say, this topic is sort of impenetrable. Most hints I found are partial, obtuse, partly wrong, or out of date.
Upvotes: 0
Reputation: 36696
You need to create a scope manually something like this:
using (var scope = serviceProvider.CreateScope())
{
var scopedServices = scope.ServiceProvider;
scopedServices.GetRequiredService<MyDbContext>();
...
}
this will give a scoped dbcontext that will get automatically disposed by the scope closure when you are finished using it. During web requests there is a scope created automatically per request so it gets disposed at the end of the request.
Upvotes: 6