How should I manage DbContext Lifetime in MVC Core?

15,054

Solution 1

As others already explained, you should use a scoped dependency for database contexts to make sure it will be properly reused. For concurrency, remember that you can query the database asynchronously too, so you might not need actual threads.

If you do need threads, i.e. background workers, then it’s likely that those will have a different lifetime than the request. As such, those threads should not use dependencies retrieved from the request scope. When the request ends and its dependency scope is being closed, disposable dependencies will be properly disposed. For other threads, this would mean that their dependencies might end up getting disposed although they still need them: Bad idea.

Instead, you should explicitly open a new dependency scope for every thread you create. You can do that by injecting the IServiceScopeFactory and creating a scope using CreateScope. The resulting object will then contain a service provider which you can retrieve your dependencies from. Since this is a seperate scope, scoped dependencies like database contexts will be recreated for the lifetime of this scope.

In order to avoid getting into the service locator pattern, you should consider having one central service your thread executes that brings together all the necessary dependencies. The thread could then do this:

using (var scope = _scopeFactory.CreateScope())
{
    var service = scope.ServiceProvider.GetService<BackgroundThreadService>();
    service.Run();
}

The BackgroundThreadService and all its dependency can then follow the common dependency injection way of receiving dependencies.

Solution 2

I believe you wouldn't faced with concurrency issues in most cases when you use scoped lifetime. Even in example you posted there is no concurrency problems as services in current request will be called subsequently. I cant even imagine case when you will run 2 or more services in parallel (its possible but not usual) in context of one HTTP request(scope).

Lifetimes its just a way to store your data(to be simple here). Just look on some lifetime managers in popular DI frameworks, all of them works pretty match the same - this is just dictionary like objects that implementing disposable pattern. Using Transient I believe your get object method will always return null so DI will create new instance each time it requested. SingleInstance will store objects in something like static concurrent dictionary so container will create instance only once and then receive existing one.

Scoped is usually mean scope object is used to store created objects. In asp net pipeline it usually mean the same as per request (as scope can be passed through the pipeline)

To be short - don't worry just use scoped it is safe and you can call this as per request.

I've tried to be very simple in my explanation, you can always look to the source code to find as match details as you need here https://github.com/aspnet/DependencyInjection

Share:
15,054
Christian Gollhardt
Author by

Christian Gollhardt

Stack Overflow Inc. doesn't care anymore about it's core user base. So I don't care about Stack Overflow anymore, and mostly stopped contributing. Currently there is a new project which is under development by veteran Stack Overflow users, called Codidact. Im realy looking forward to this Open Source community-oriented Q&amp;A site. I have developed my first application when I was 13 years old. So yes, I have made my hobby a profession. I love writing frameworks more than using it, because with every solved problem you have learned something more. If you don't understand the basics, you don't understand the framework. In my early days I have written my own forum with php, css &amp; xhtml. That was early 2000. The time where every developer said: Don't use javascript! Nowadays we all know it better. Hell yeah, we have technologies like ajax &amp; json. I have earned some experience in wpf and mvvm. I realy love how the databinding works. Sadly today everything needs to be mobilefirst. It's rare to have the opportunity to do a project based on this. In current days I am fallen in love with c#, asp.net-mvc, asp.net-core entity-framework, and twitter-bootstrap. I am excited about every new technology I am able to meet. Next things I want to learn about: uwp angular Gamedevelopment in general If you want to provoke me, simple write Yoda-Code:

Updated on June 03, 2022

Comments

  • Christian Gollhardt
    Christian Gollhardt almost 2 years

    From the Documentation

    Entity Framework contexts should be added to the services container using the Scoped lifetime. This is taken care of automatically if you use the helper methods as shown above. Repositories that will make use of Entity Framework should use the same lifetime.

    I always thought, that I should create a new Context for every single unit of work I have to process. This let me think, if I have a ServiceA and ServiceB, which are applying different actions on the DbContext that they should get a different Instance of DbContext.

    The documentation reads as following:

    • Transient objects are always different; a new instance is provided to every controller and every service.

    • Scoped objects are the same within a request, but different across different request

    Going back to ServiceA and ServiceB, it sounds to me, Transient is more suitable.

    I have researched, that the Context should only saved once per HttpRequest, but I really do not understand how this does work.

    Especially if we take a look at one Service:

    using (var transaction = dbContext.Database.BeginTransaction())
    {
        //Create some entity
        var someEntity = new SomeEntity();
        dbContext.SomeEntity.Add(someEntity);
    
        //Save in order to get the the id of the entity
        dbContext.SaveChanges();
    
        //Create related entity
        var relatedEntity = new RelatedEntity
        {
            SomeEntityId = someEntity.Id
        };
        dbContext.RelatedEntity.Add(relatedEntity)
        dbContext.SaveChanges();
        transaction.Commit();
    }
    

    Here we need to Save the context in order to get the ID of an Entity which is related to another one we just have created.

    At the same time another service could update the same context. From what I have read, DbContext is not thread safe.

    Should I use Transient in this case? Why does the documentation suggest, I should use Scoped?

    Do I miss some important part of the framework?