System.ObjectDisposedException: Cannot access a disposed context instance.
— EF Core threw this when a query built inside a using block was enumerated
after the block had ended and the DbContext was gone. Run the query while the
context is alive: call ToList() (or ToListAsync()) before the
method returns.
In the reproduction a method returned IQueryable<Customer> from a
using var db scope. Changing the return type to List<Customer>
and adding .ToList() made the same loop print both customers.
The error
PS> dotnet run -- disposed-bad
...
Unhandled exception. System.ObjectDisposedException: Cannot access a disposed context instance. A common cause of this error is disposing a context instance 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 instance, or wrapping it in a using statement. If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'ShopContext'.
at Microsoft.EntityFrameworkCore.DbContext.CheckDisposed()
at Microsoft.EntityFrameworkCore.DbContext.get_ContextServices()
at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
...
at Program.<Main>$(String[] args) in ...\EfLab\Program.cs:line 19
The first cut hides four NuGet audit warnings printed by dotnet run; the
second hides stack frames inside EF Core. Line 19 is the foreach, not the
method that built the query.
Why it happens
db.Customers.Where(...) does not run any SQL. It returns a query object that
still points at the context it came from. The method below handed that object back to its
caller, and using var db disposed the context at the closing brace. When the
caller's foreach started the enumeration, EF Core found a disposed context and
threw. The stack trace shows the failure at the loop, which is why the bug looks like it is
in the caller.
static IQueryable<Customer> GetCustomersBad()
{
using var db = new ShopContext();
return db.Customers.Where(c => c.City == "Lima");
}
The message itself talks about dependency injection. That is a second route to the same
exception (a context from a scope that has ended), but this reproduction used no DI at all:
a plain using statement was enough.
The fix
static List<Customer> GetCustomersFixed()
{
using var db = new ShopContext();
return db.Customers.Where(c => c.City == "Lima").ToList();
}
PS> dotnet run -- disposed-fix
...
Maria Garcia
David Chen
ToList() runs the SQL and copies the rows into memory while the context is
still open, so the caller receives plain objects. The other way out is to give the context
a longer life than the query, for example by letting the caller own the
DbContext and pass it in. Returning IQueryable out of a method
that disposes its own context does not work.
How it was reproduced
A console project from dotnet new console -n EfLab on .NET SDK 10.0.403 with
Microsoft.EntityFrameworkCore.Sqlite 10.0.14 and a SQLite file shop.db. A
ShopContext with one Customer entity was seeded with two fictional
customers. dotnet run -- disposed-bad enumerated the returned
IQueryable and threw; dotnet run -- disposed-fix printed both
names.
Frequently asked
- Why does Cannot access a disposed context instance point at my foreach?
- Because the query only runs when it is enumerated. In the reproduction the context was disposed when the method returned, and the exception surfaced at the foreach in the caller, line 19.
- Can I return IQueryable from a method that uses a using block for the DbContext?
- Not if the caller enumerates it after the method returns. Call ToList or ToListAsync inside the method, or let the caller own the context.
- Does this error only happen with dependency injection?
- No. The message mentions dependency injection, but this reproduction used a plain using statement and no DI container, and it threw the same ObjectDisposedException.
More decoded errors in the Fixes category.