Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Getting DbContext when resolving a singleton

Within ConfigureServices I have

services.AddDbContext<MyContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

as well as

services.AddSingleton<IMyModel>(s =>
{
    var dbContext = s.GetService<MyContext>();
    var lastItem= dbContext.Items.LastOrDefault();
    return new MyModel(lastItem);
});

But s.GetService<MyContext>() throws an error:

Cannot resolve scoped service 'MyContext' from root provider.

How can I achieve that? I don't want to inject MyDbContext in MyModel contructor as it is in a library which should have no reason to know about Entity Framework.

like image 390
François Avatar asked Sep 20 '17 16:09

François


People also ask

Can DbContext be Singleton?

1 Answer. 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.

Should DbContext be in a using?

EF and EF Core DbContext types implement IDisposable . As such, best practice programming suggests that you should wrap them in a using() block (or new C# 8 using statement). Unfortunately, doing this, at least in web apps, is generally a bad idea.

Is DbContext scoped or transient?

This example registers a DbContext subclass called ApplicationDbContext as a scoped service in the ASP.NET Core application service provider (a.k.a. the dependency injection container). The context is configured to use the SQL Server database provider and will read the connection string from ASP.NET Core configuration.

Can not consume scoped service from Singleton?

You should almost never consume scoped service or transient service from a singleton. You should also avoid consuming transient service from a scoped service. What is happening when you consume scoped service from a singleton service is known as a captive dependency.


1 Answers

AddDbContext defaults to using a scoped lifestyle:

Scoped lifetime services (AddScoped) are created once per client request (connection).

The reason an error is being thrown is that you're attempting to obtain an instance of MyContext from outside of a request. As the error message suggests, it is not possible to obtain a scoped service from the root IServiceProvider.

For your purposes, you can create a scope explicitly and use that for your dependency resolution, like so:

services.AddSingleton<IMyModel>(sp =>
{
    using (var scope = sp.CreateScope())
    {
        var dbContext = scope.ServiceProvider.GetService<MyContext>();
        var lastItem = dbContext.Items.LastOrDefault();
        return new MyModel(lastItem);
    }
});    

This code above creates a scoped IServiceProvider that can be used for obtaining scoped services.

like image 196
Kirk Larkin Avatar answered Nov 01 '22 07:11

Kirk Larkin