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?
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.
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.
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.
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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With