How should I manage DbContext Lifetime in MVC Core?
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
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&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 & 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 & 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, 2022Comments
-
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 aServiceA
andServiceB
, which are applying different actions on theDbContext
that they should get a different Instance ofDbContext
.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
andServiceB
, 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 useScoped
?Do I miss some important part of the framework?