How should I manage DbContext Lifetime in MVC Core?

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

Question

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?

1
20
6/21/2018 1:46:02 AM

Accepted Answer

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.

26
9/25/2017 9:55:43 PM

Popular Answer

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



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