ASP.NET Core - Repository dependency injection fails on Singleton injection

asp.net-core asp.net-core-mvc c# dependency-injection entity-framework-core

Question

I am using SoapCore to create a web service for my ASP.NET Core MVC application.

I am using Entity Framework Core and a simple Repository pattern to get my DB data.

I am injecting my repository classes via .AddSingleton() in my Startup.cs:

services.AddSingleton<IImportRepository, ImportRepository>();
services.AddSingleton<IWebService, WebService>();

Since the EF DbContext is scoped I get an error when calling my web service:

Cannot consume scoped service 'App.Data.ApplicationDbContext' from singleton 'App._Repository.IImportRepository'.

When I use .AddScoped() instead, it works fine.

I've read injecting scoped dependencies via a controllers/classes constructor is bad practice, since it "falls back" to be a singleton or behaves like one.

I wonder if there is another way to make it work with singletons or if this has some major draw backs in the long term (about 100-200 users will use the site) when using scoped injections in my controllers via the ctor?

1
2
8/14/2019 1:04:57 PM

Accepted Answer

Simply put, your go-to lifetime should be "scoped". You should only use a singleton or transient lifetime if you have a good reason to do so. For a singleton, that's stuff like managing locks or holding data that needs to persist for the lifetime of the application, neither of which applies to the concept of a repository. Repositories should be entirely disposable. The point is to persist to the database or to some other store, so they should not contain any data in their own right that needs to be persisted.

Long and short, your best bet here is to simply make your repo(s) scoped, so you can directly inject the context. As far as constructor injection goes, I'm not sure where you got the idea that that's a bad practice. It's in fact how dependency injection works in most cases, so you can't really have one without the other.

If you absolutely need to have a singleton, then your only option is the service locator antipattern. For that, you will inject IServiceProvider:

public class MyRepo
{
    private readonly IServiceProvider _serviceProvider;

    public MyRepo(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    ...
}

Then, each time you need the context (that's important), you'll need to do:

using (var scope = _serviceProvider.CreateScope())
{
    var context = scope.ServiceProvider.GetRequiredService<MyContext>();
    // do something with context
}
6
8/14/2019 3:04:18 PM

Popular Answer

Scoped objects cannot be injected in to singleton objects. Its simple as this, singletons are created only once when the app is starting and used by all the subsequent requests. Scoped objected are created during each request and disposed at the end of the request. So there is no way for the previously created single object to know about the scoped objects creating during each request. So using scoped objects is not possible in singletons. But the the other way around is possible.

injecting scoped dependencies via a controllers/classes constructor

I don't think its a bad practice at all. If not how are you planing to do unit testing?

Also its not right to make DB context singleton. You will face cashing issues/data anomalies in parallel and subsequent requests. In my opinion DB context has to be scoped and all the objects that uses the db-context has to be scoped all the way up.

So in you case, make all the objects ImportRepository, WebService and DB Context all scoped.

Cheers,



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow