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

.NetEntity Framework.Net CoreEntity Framework-Core

.Net Problem Overview


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

.Net Solutions


Solution 1 - .Net

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 - .Net

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 - .Net

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 - .Net

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 - .Net

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.

Solution 6 - .Net

This is another anwser for who gonna read this topic.

I tried to trigger asnyc method with javascript. Because of that it can't triggered and so result didn't calculated by async method. I was returning json object to view like this in dotnet core mvc.

return Json(_service.AsyncMethod(parameter));

My IActionResult is not async so I can't add "await". I just add ".Result" and it calculate result.

return Json(_service.AsyncMethod(parameter).Result);

Solution 7 - .Net

Create IServiceScopeFactory object and used dependency from IServiceScopeFactory object once it dispose.

private readonly IServiceScopeFactory moserviceScopeFactory;
private readonly InvoicesDataContext moInvoicesDataContext;
public ABCController(InvoicesDataContext diInvoicesDataContext, IServiceScopeFactory serviceScopeFactory)
		{
			moInvoicesDataContext = diInvoicesDataContext;
			moserviceScopeFactory = serviceScopeFactory;
		}

My Object has been dispose after execute below code and I want to access object after execute this code. I have created object from IServiceScopeFactory and used. and working fine for me.

public async Task<int> generateInvoice()
    		{
    			List<Guid> loSentId = new List<Guid>();
    			Invoices loDataContext = new Invoices(moInvoicesDataContext);
    			List<Invoices> loInvoices = loInvoiceDataContext.get("", null, "", null, null, 1, "1", "asc", 1, 5);
    
    			bool lblIsSuccess = await loSystemEmails.sendInvoice(loInvoice.stInvoiceNumber, loInvoice.stCompanyEmail, string.Empty, loInvoice.stCompanyName, GetFolderPath.loRootPath + lsSystemFilePath, "1");
    								if (lblIsSuccess)
    								{
    									loSentInvoiceId.Add(loInvoice.unCompanyInvoiceId);
    
    									

using (var scope = moserviceScopeFactory.CreateScope())
    									{
    										var context = scope.ServiceProvider.GetRequiredService<InvoicesDataContext>();

    
    										int liSuccess = new Invoices(context).SaveData(Convert.ToString(loInvoice.unCompanyInvoiceId), 2, Guid.Empty, "New Zealand Standard Time", "+13:00"); //2 = Sent
    									}
    								}
    			return loSentInvoiceId != null && loSentInvoiceId.Count > 0 ? loSentInvoiceId.Count : 0;
    		}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSoheilView Question on Stackoverflow
Solution 1 - .NetTanvirArjelView Answer on Stackoverflow
Solution 2 - .NetbrandoView Answer on Stackoverflow
Solution 3 - .NetMichael HowardView Answer on Stackoverflow
Solution 4 - .NetReapView Answer on Stackoverflow
Solution 5 - .NetJohn ErnestView Answer on Stackoverflow
Solution 6 - .NetHakkıView Answer on Stackoverflow
Solution 7 - .NetSujit PatelView Answer on Stackoverflow