Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How should I manage DbContext Lifetime in MVC Core?

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?

like image 240
Christian Gollhardt Avatar asked Sep 25 '17 19:09

Christian Gollhardt


People also ask

What is DbContext lifetime?

The lifetime of a DbContext begins when the instance is created and ends when the instance is disposed. A DbContext instance is designed to be used for a single unit-of-work. This means that the lifetime of a DbContext instance is usually very short.

Does DbContext need to be disposed?

Don't dispose DbContext objects. Although the DbContext implements IDisposable , you shouldn't manually dispose it, nor should you wrap it in a using statement. DbContext manages its own lifetime; when your data access request is completed, DbContext will automatically close the database connection for you.

Should DbContext be singleton or transient?

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.

Is DbContext managed?

The problem is there's no clear-cut "yes" or "no" answer to "Is DbContext unmanaged?". It's a CLR class, so it's definitely a managed object.


1 Answers

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.

like image 191
poke Avatar answered Sep 21 '22 13:09

poke