Cannot access a disposed object. A common cause of this error is disposing a context

84,675

Solution 1

This is because of your method return type async void. In general, when you are using async void in your code it’s bad news, because:

  • You can’t wait for its completion
  • Any unhandled exceptions will terminate your process (ouch!)

So return async Task instead of async void from your method as follows:

public async Task OnGet(int id)
{
    Book = await _db.Books.SingleOrDefaultAsync(x => x.Id == id);

    if(Book == null)
    {
       RedirectToPage("Index");
    }
}

For more details:

Solution 2

What I am about to post is NOT the answer to this particular question. But it is related so just to save somebody headache I am posting it. I was encountering this same error

System.ObjectDisposedException: Cannot access a disposed object. etc

The following was the code with the bug (can you see it?):

[HttpGet("processs/oxxo-spei/ticket-email/{paymentIdx}")]
public StatusCodeResult ProcessOxxoSpeiTicketEmailAsync(string paymentIdx)
{
    var paymentId = paymentIdx.DecodeRef();
            
    var response = _orderEngine.ProcessOxxoSpeiTicketEmailAsync(paymentId);

    return StatusCode(200);
}

The following change fixed it:

[HttpGet("processs/oxxo-spei/ticket-email/{paymentIdx}")]
public async Task<StatusCodeResult> ProcessOxxoSpeiTicketEmailAsync(string paymentIdx)
{
    var paymentId = paymentIdx.DecodeRef();
            
    var response = await _orderEngine.ProcessOxxoSpeiTicketEmailAsync(paymentId);
                // ^^^^I HAD FORGOTTEN TO PUT AWAIT
    return StatusCode(200);
}

Yes that's right I had forgotten to put "await" before a function that used an EF Core dbcontext. Adding 'await' fixed it. So easy to miss it, especially if you're tired and under deadline.

Solution 3

UPDATED ANSWER WHEN THE PROBLEM IS NOT A TASK OF TYPE VOID The accepted answer is fine if you are using a voided method. There is another potential problem with a simple solution if your method is not a void.

  1. Did you implement a pattern or some type of separation of concerns? Controller => Service => Repository

Verify that your controller method is async Task if the methods down the line are also async Task. In my case the problem was the fact that I did not put async on the controller method, but the downline methods were async Task.

Cheers!

Solution 4

Alternative Solution to async Task conversions


You've tried...

  • Making all methods in the callstack async Task instead of async void

After a careful look at my call stack, I had overridden the SaveChangeAsync method in my DbContext, and made a call to change the behavior of how async operates by default. This which resulted in my context being disposed and still trying to access it.


Some code omitted for brevity

public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
  int result = await base.SaveChangesAsync(cancellationToken);

  // ignore events if no dispatcher provided
  if (dispatcher is null) return result;

  // dispatch events only if save was successful
  await DispatchEventsIfSaveSuccessful();
  return result;
}

private async Task DispatchEventsIfSaveSuccessful()
{
    var entitiesWithEvents = ChangeTracker
        .Entries<Entity>()
        .Select(e => e.Entity)
        .Where(e => e.Events.Any())
        .ToArray();
...
    foreach (var entity in entitiesWithEvents)
    {
        var events = entity.Events.ToArray();
        entity.Events.Clear();
        foreach (var domainEvent in events)
        {
            await dispatcher.Dispatch(domainEvent).ConfigureAwait(false);
        }
    }
}

Problem way

await dispatcher.Dispatch(domainEvent).ConfigureAwait(continueOnCapturedContext: false);

The solution

omit the ConfigureAwait(false)

await dispatcher.Dispatch(domainEvent);

Explanation

ASP.NET Core does not use a [SynchronizationContext].(https://devblogs.microsoft.com/dotnet/configureawait-faq/).

When using ConfigureAwait(false) options on an async Task, the executing task will not resume on this context, but instead on a thread pool thread. Essentially by configuring that await call, when the thread finished dispatching the events, the context was already disposed.

Advice

Continue looking through each method/function invocation from the beginning of your request all the way to when it fails and see if you may be altering the default behavior of async/await via ConfigureAwait(false) or a similar altering change.

For more information, I highly recommend following this link by Stephen Cleary on his blog about async more.

Solution 5

Here is what actually worked for me in a situation where I was getting the same error as above when trying to access the data in Startup.cs to run some startup data building functions on server load, rather than a specific Controller action:

        IServiceScope scope = provider.CreateScope();
        YourDbContext context = scope.ServiceProvider.GetRequiredService<YourDbContext>();

In the Configure method of Startup.cs, with IServiceProvider included as a parameter:

public async void Configure(IApplicationBuilder app, IWebHostEnvironment env, IServiceProvider provider)

Just including that for anyone who wants to do the same in Startup.cs.

Share:
84,675
Soheil
Author by

Soheil

Abandon the Lay-by even if the pain comes by. That's my motto!

Updated on July 09, 2022

Comments

  • Soheil
    Soheil almost 2 years

    I have written a simple application and when I navigate to my edit page the below error pops up.

    Microsoft.EntityFrameworkCore.Query[10100]

    An exception occurred while iterating over the results of a query for context type 'app.Models.ApplicationDbContext'.

    System.ObjectDisposedException: 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.

    It seems EF is providing a useful information which I can not understand. The tricky part of this error is that it happens randomly when I navigate to the edit page. Sometime it works, Sometimes it fails to load some properties on Edit.cshtml but still works and sometimes the application crashes with provided error just in my console. Another strange happen is that it dose not generate any 500 or 5xx error. It just simply crashes and stop the application.

    Here is my Edit.cshtml content:

    @page
    @model EditModel
    @{
        ViewData["Title"] = "Edit Book";
    }
    
    <h2>Edit Book</h2>
    
    <div class="row justify-content-center">
        <div class="col-md-6">
            <form method="post" class="form-border">
                <div asp-validation-summary="All" class="validation-container alert alert-danger"></div>
                <div class="form-group">
                    <label asp-for="Book.Name"></label>
                    <input asp-for="Book.Name" class="form-control" />
                    <span class="form-text text-danger" asp-validation-for="Book.Name"></span>
                </div>
                <div class="form-group">
                    <label asp-for="Book.Description"></label>
                    <input asp-for="Book.Description" class="form-control" />
                </div>
                <div class="form-group">
                    <label asp-for="Book.Author"></label>
                    <input asp-for="Book.Author" class="form-control" />
                </div>
                <input asp-for="Book.Id" type="hidden">
                <button type="submit" class="btn btn-primary">Update</button>
                <a asp-page="Index" class="btn btn-success">Back To List</a>
            </form>
        </div>
    </div>
    

    Here is My Edit.cshtm.cs OnGet method:

    public async void OnGet(int id)
    {
        Book = await _db.Books.SingleOrDefaultAsync(x => x.Id == id);
    
        if(Book == null)
        {
            RedirectToPage("Index");
        }
    }
    

    I am using .Net Core 2.2.104

    Also when I run command dotnet ef --version it generates Entity Framework Core .NET Command-line Tools 2.2.2-servicing-10034

    • NaDeR Star
      NaDeR Star about 5 years
      read this article
  • brando
    brando almost 5 years
    I'm having this problem too, but nowhere do I use async void, always use async Task
  • TanvirArjel
    TanvirArjel almost 5 years
    @brando Then the problem lies in anywhere in your code. Please ask a separate question with your code. Hope I or someone else will help you.
  • brando
    brando almost 5 years
    I discovered my error. Very frustrating until you realize the simple fix
  • LeonidasFett
    LeonidasFett over 4 years
    Thank you so much. I was tearing my hair out and was beginning to think that Azure's Free Tier sucks big time when in reality, the problem again sat in front of the PC.
  • Tim Maxey
    Tim Maxey over 3 years
    Thanks dude, I had a controller hitting a non async method and then changed the service method it was hitting to async and never added aysnc method or await on the controller side, duhhh........
  • snejame
    snejame about 3 years
    I was also not using async in my controller method and I feel like a complete moron, wrestled with this for two days. Sometimes you just need to get up and take a walk. Thanks for posting this.
  • Gert Arnold
    Gert Arnold about 3 years
    How does this (poorly-indented) code answer the question? The correct answer has already been given and is very simple. Even when creating a scope async void will be an issue so this doesn't solve anything.
  • Selim Reza
    Selim Reza about 3 years
    Thanks in advance, but how I can call "public async Task OnGet()" method from a Thread calling??
  • TanvirArjel
    TanvirArjel about 3 years
    @SelimReza await OnGet()
  • Selim Reza
    Selim Reza about 3 years
    @TanvirArjel , you have shown normal calling process, but I want to know the process by Thread calling.
  • TanvirArjel
    TanvirArjel about 3 years
    @SelimReza Please a look here: Asynchronous programming with async and await
  • user1890098
    user1890098 over 2 years
    Thanks a bunch for this
  • Jitendra Sawant
    Jitendra Sawant over 2 years
    GOLDEN ANSWER!!!!! :)
  • Waleed
    Waleed over 2 years
    Just to add, in short returning a Task will fix the issue and make sure object instance is not disposed
  • Roshan
    Roshan over 2 years
    Saved one night!
  • fullStackChris
    fullStackChris over 2 years
    Wouldn't this be picked up in literally any editor...?
  • brando
    brando over 2 years
    @fullStackChris I think now it is
  • fullStackChris
    fullStackChris over 2 years
    I'm running into this problem today, with DbContext, I think it has much more to do with how classes are injected and when.
  • Jeremy Todd
    Jeremy Todd over 2 years
    Can you clarify what kind of problem this created? The fact that the continuation is resuming on a thread pool thread shouldn't allow the context to be disposed before it finishes (unless you're not awaiting your top-level SaveChangesAsync() call before disposing the context, in which case it would be functionally equivalent to async void).I feel like there must be more to the story than is reflected in your code snippets, like use of [ThreadStatic] fields or Task.Run() or something.
  • Reap
    Reap over 2 years
    @JeremyTodd If I recall correctly, in the entire request pipeline that I used to construct this answer, there was no ThreadStatic or Task.Runs, etc. or anything other than standard await/async. For reasons I've tried to understand from the documentation I linked in my answer, it appears an async call to the SaveChangesAsync that has a ConfigureAwait async call to the dispatcher caused this issue because it was the only change made that caused the error to completely disappear. It happened consistently on debug/release mode.
  • GerardF
    GerardF over 2 years
    I had the same issue, thanks a lot for this. I did a global search and found a couple more async void that I replaced with async Task to avoid potential future issues.
  • SimpleUser
    SimpleUser about 2 years
    Thanks so much for this. Hit this problem today. And it's nice to know why it happened as well.
  • Aakash
    Aakash about 2 years
    I literally dug up all my services functions and Repo as well. My Controller was not async. Adding it helped me.... Thanks man! I was getting an "System.ObjectDisposedException: 'Cannot access a disposed context instance. ..... " upon Calling savechanges method from Repo.
  • srzsanti
    srzsanti about 2 years
    Bro, the odds of finding this answer. THANK YOU!