【问题标题】:Cannot access a disposed object. A common cause of this error is disposing a context无法访问已处置的对象。此错误的常见原因是处理上下文
【发布时间】:2019-04-05 21:27:37
【问题描述】:

我编写了一个简单的应用程序,当我导航到我的编辑页面时,会弹出以下错误。

Microsoft.EntityFrameworkCore.Query[10100]

迭代上下文类型“app.Models.ApplicationDbContext”的查询结果时发生异常。

System.ObjectDisposedException:无法访问已处置的对象。此错误的一个常见原因是释放从依赖注入中解析的上下文,然后尝试在应用程序的其他地方使用相同的上下文实例。如果您在上下文上调用 Dispose() 或将上下文包装在 using 语句中,则可能会发生这种情况。如果你使用依赖注入,你应该让依赖注入容器负责处理上下文实例。

似乎 EF 提供了我无法理解的有用信息。这个错误的棘手部分是它在我导航到编辑页面时随机发生。有时它可以工作,有时它无法在Edit.cshtml 上加载某些属性,但仍然可以工作,有时应用程序会在我的控制台中因提供的错误而崩溃。另一个奇怪的事情是它不会产生任何5005xx 错误。它只是简单地崩溃并停止应用程序。

这是我的Edit.cshtml 内容:

@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>

这是我的Edit.cshtm.cs OnGet 方法:

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

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

我正在使用.Net Core 2.2.104

另外,当我运行命令dotnet ef --version 时,它会生成Entity Framework Core .NET Command-line Tools 2.2.2-servicing-10034

【问题讨论】:

标签: .net entity-framework .net-core entity-framework-core


【解决方案1】:

这是因为您的方法返回类型为async void。一般来说,当您在代码中使用async void 时,这是个坏消息,因为:

  • 你不能等待它完成
  • 任何未处理的异常都会终止您的进程(哎哟!)

所以从您的方法中返回async Task 而不是async void,如下所示:

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

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

更多详情:

【讨论】:

  • 我也遇到了这个问题,但是我没有使用异步 void,总是使用异步任务
  • @brando 那么问题出在代码中的任何地方。请用您的代码提出一个单独的问题。希望我或其他人会帮助你。
  • 我发现了我的错误。在您意识到简单的修复之前非常令人沮丧
  • 非常感谢。我正在抓狂,开始认为 Azure 的免费套餐很糟糕,而实际上,问题又出现在 PC 面前。
  • 提前谢谢,但是我如何从线程调用中调用“公共异步任务 OnGet()”方法??
【解决方案2】:

我要发布的内容不是这个特定问题的答案。但它是相关的,所以我发布它只是为了让一些人头疼。我遇到了同样的错误

System.ObjectDisposedException:无法访问已处置的对象。等等

以下是有 bug 的代码(你能看到吗?):

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

    return StatusCode(200);
}

以下更改修复了它:

[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);
}

是的,我忘记在使用 EF Core dbcontext 的函数之前添加“等待”。添加“等待”修复它。很容易错过它,尤其是当你累了并且在最后期限之前。

【讨论】:

  • 谢谢老兄,我有一个控制器命中了一个非异步方法,然后将它所命中的服务方法更改为异步,并且从未在控制器端添加aysnc方法或等待,duhhh...... ..
  • 我也没有在我的控制器方法中使用异步,我觉得自己像个彻头彻尾的白痴,为此挣扎了两天。有时你只需要站起来走一走。感谢您发布此内容。
  • 只是补充一下,简而言之,返回一个任务将解决问题并确保对象实例没有被释放
  • 在任何编辑器中都不会出现这种情况...?
  • @fullStackChris 我想现在是
【解决方案3】:

当问题不是 VOID 类型的任务时更新答案 如果您使用无效的方法,则接受的答案很好。如果您的方法不是无效的,那么简单的解决方案还有另一个潜在问题。

  1. 您是否实现了某种模式或某种类型的关注点分离?控制器 => 服务 => 存储库

如果接下来的方法也是异步任务,请验证您的控制器方法是否为异步任务。就我而言,问题在于我没有在控制器方法上放置异步,但下线方法是异步任务。

干杯!

【讨论】:

  • 非常感谢
  • 黄金答案!!!!!! :)
  • 节省了一晚!
  • 我遇到了同样的问题,非常感谢。我进行了全局搜索,发现了更多的 async void,我将其替换为 async Task 以避免潜在的未来问题。
  • 我确实挖掘了我所有的服务功能和回购。我的控制器不是异步的。添加它对我有帮助....谢谢伙计!从 Repo 调用 savechanges 方法时,我收到“System.ObjectDisposedException:'无法访问已处理的上下文实例......”。
【解决方案4】:

async Task 转化的替代解决方案


你试过了……

  • 在调用堆栈中创建所有方法async Task 而不是async void

仔细查看我的调用堆栈后,我重写了DbContext 中的SaveChangeAsync 方法,并进行了调用以更改async 默认运行方式的行为。这导致我的上下文被处理并仍在尝试访问它。


为了简洁省略了一些代码

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);
        }
    }
}

问题方式

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

解决办法

省略ConfigureAwait(false)

await dispatcher.Dispatch(domainEvent);

说明

ASP.NET Core 不使用 [SynchronizationContext].(https://devblogs.microsoft.com/dotnet/configureawait-faq/)。

当在async Task 上使用ConfigureAwait(false) options 时,执行任务将不会在此上下文中恢复,而是在线程池线程上恢复。基本上通过配置await 调用,当thread finished dispatching the events 时,上下文已经被释放。

建议

继续查看每个方法/函数调用,从您的请求开始一直到它失败 strong> 看看您是否可能通过ConfigureAwait(false) 或类似的更改更改async/await 的默认行为。

如需了解更多信息,我强烈推荐 Stephen Cleary 在他的博客上写的 following this linkasync 更多信息。

【讨论】:

  • 你能解释一下这会造成什么样的问题吗?继续在线程池线程上恢复的事实不应允许在其完成之前释放上下文(除非您在释放上下文之前没有等待顶级 SaveChangesAsync() 调用,在这种情况下它将是功能上等同于 async void)。我觉得这个故事必须比你的代码 sn-ps 中反映的更多,比如使用 [ThreadStatic] 字段或 Task.Run() 或其他东西。
  • @JeremyTodd 如果我没记错的话,在我用来构造这个答案的整个请求管道中,没有ThreadStaticTask.Runs等或任何东西除了标准await/async。由于我试图从我在答案中链接的文档中理解的原因,它似乎是对SaveChangesAsyncasync 调用,它对调度程序有一个ConfigureAwait 异步调用导致此问题,因为它是唯一的更改这导致错误完全消失。它始终在调试/发布模式下发生。
【解决方案5】:

在尝试访问 Startup.cs 中的数据以在服务器负载上运行一些启动数据构建功能时遇到与上述相同的错误,这实际上对我有用,而不是特定的控制器操作:

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

在 Startup.cs 的 Configure 方法中,包含 IServiceProvider 作为参数:

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

只包括那些想要在 Startup.cs 中做同样事情的人。

【讨论】:

    【解决方案6】:

    这是阅读此主题的另一个答案。

    我尝试使用 javascript 触发 asnyc 方法。因为它不能触发,所以结果不是通过异步方法计算的。我正在返回 json 对象以在 dotnet core mvc 中像这样查看。

    return Json(_service.AsyncMethod(parameter));
    

    我的 IActionResult 不是异步的,所以我不能添加“等待”。我只是添加“.Result”并计算结果。

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

    【讨论】:

      【解决方案7】:

      创建 IServiceScopeFactory 对象并在 IServiceScopeFactory 对象释放后使用它的依赖项。

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

      我的对象在执行以下代码后已被处置,我想在执行此代码后访问对象。我已经从 IServiceScopeFactory 创建了对象并使用了。对我来说工作得很好。

      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;
                  }
      

      【讨论】:

      • 这段(缩进不佳的)代码如何回答这个问题?正确答案已经给出,而且非常简单。即使在创建范围 async void 时也会出现问题,所以这并不能解决任何问题。
      猜你喜欢
      • 1970-01-01
      • 2020-04-25
      • 2019-03-04
      • 1970-01-01
      • 2020-09-20
      • 1970-01-01
      • 2011-10-23
      • 2011-03-25
      相关资源
      最近更新 更多