You got the "Cannot access a disposed object using Entity Framework Core". What should you do?
Photo by Caspar Camille Rubin on Unsplash |
You tried to access Entity Framework Core's db context on a background thread in .NET Core and got this error:
Cannot access a disposed object. A common cause of this error is disposing a context that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application. This may occur if you are calling Dispose() on the context, or wrapping the context in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.This exception happens because EF Core disposes the connection just after the request to the controller is closed. Indeed that's the right approach and is the default behaviour as we don't want those resources hanging open for much longer. But before going forward, I'd like you to know that most times you don't need or want to access EF Core on a background context. A few good explanations are described here. And, if you need an introduction, I'd also recommend reading Scott Hanselman's introduction on the topic.
However, such an approach sometimes may be necessary. For example, I came across that issue writing a MVP, a proof of concept where a Vue.JS chat room using EF Core communicated with a SignalR Core backend running on Linux. In my opinion MVPs and proofs of concept are the only acceptable use cases for this solution. As always, the default approach should be accessing the service via the injected dependency.
Enough talking. Let's take a look at how to address the problem.
IServiceScopeFactory
The key to doing this is using IServiceScopeFactory. Available on the Microsoft.Extensions.DependencyInjection Nuget package, IServiceScopeFactory provides us a singleton from which we can resolve services trough DI the same way the .NET Core framework does for us.Microsoft describes it as:
A factory for creating instances of IServiceScope, which is used to create services within a scope. Create an IServiceScope which contains an IServiceProvider used to resolve dependencies from a newly created scope.
The Implementation
The implementation is divided in 3 (three) steps:- Inject the IServiceScopeFactory singleton on your controller
- Pass the instance of IServiceScopeFactory to your background task or thread
- Resolve the service from the background task
Step 1 - Inject IServiceScopeFactory in your controller
First, you need to inject IServiceScopeFactory in your controller.Step 2 - Pass it to your background thread
Then, you have some code that supposedly invokes the bg thread/task. For example:Step 3 - Resolve the service from the background task
And finally, when your background thread is run, access the scope and have the framework initialize the EF context for you with:And because it's a singleton, IServiceScopeFactory won't throw an exception when you try to access it.
at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
at Microsoft.EntityFrameworkCore.DbContext.Add[TEntity](TEntity entity)
at Microsoft.EntityFrameworkCore.Internal.InternalDbSet`1.Add(TEntity entity)
Conclusion
While you shouldn't use this as a pattern to process background tasks, there are situations where this is necessary. Since the there isn't much documentation around IServiceScopeFactory I thought it was good to document it. Hope it helps!References
- IServiceScopeFactory Interface
- StackOverflow - DbContext for background tasks via Dependency Injection
- StackOverflow - .NET Core IServiceScopeFactory.CreateScope() vs IServiceProvider.CreateScope() extension
- GitHub - CreateScope from IServiceProvider
See Also
- Microservices in ASP.NET
- My journey to 1 million articles read
- Adding Application Insights to your ASP.NET Core website
- Creating ASP.NET Core websites with Docker
- Distributed caching in ASP.NET Core using Redis, MongoDB and Docker
- Send emails from ASP.NET Core websites using SendGrid and Azure
- Hosting NuGet packages on GitHub
- Configuration in .NET Core console applications
- Building and Running ASP.NET Core apps on Linux
- 5 tools for Azure Development on Linux
- Simplifying Razor logic with C# Local Functions in ASP.NET Core
- Package Management in .NET Core
- Why I use Fedora Linux