无法访问已处置的对象。导致此错误的常见原因是处理上下文

Soh*_*eil 4 .net entity-framework entity-framework-core .net-core

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

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>
Run Code Online (Sandbox Code Playgroud)

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

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

    if(Book == null)
    {
        RedirectToPage("Index");
    }
}
Run Code Online (Sandbox Code Playgroud)

我在用 .Net Core 2.2.104

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

小智 61

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

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

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

干杯!

  • 我真的挖出了我所有的服务功能和回购协议。我的控制器不是异步的。添加它帮助了我......谢谢伙计!从 Repo 调用 savechanges 方法时,我收到“System.ObjectDisposeException:'无法访问已处置的上下文实例......”。 (2认同)

bra*_*ndo 21

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

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

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

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

    return StatusCode(200);
}
Run Code Online (Sandbox Code Playgroud)

以下更改修复了它:

[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);
}
Run Code Online (Sandbox Code Playgroud)

是的,我忘记在使用 EF Core dbcontext 的函数之前放置“await”。添加“等待”修复它。很容易错过它,特别是如果你很累并且在最后期限内。

  • 谢谢老兄,我有一个控制器点击了非异步方法,然后将其点击的服务方法更改为异步,并且从未在控制器端添加 aysnc 方法或等待,duhhhh........ (3认同)
  • 我也没有在我的控制器方法中使用异步,我感觉自己像个十足的白痴,为此苦苦挣扎了两天。有时你只需要站起来散散步。感谢您发布此内容。 (2认同)

Tan*_*jel 10

这是因为您的方法返回类型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");
    }
}
Run Code Online (Sandbox Code Playgroud)

更多细节:

  • 我也有这个问题,但我无处使用 async void,总是使用 async Task (4认同)

Rea*_*eap 7

async Task转换的替代解决方案


你已经尝试过...

  • 使调用堆栈中的所有方法async Task而不是async void

仔细查看我的调用堆栈后,我重写了SaveChangeAsync我的方法DbContext,并进行了调用以更改默认情况下操作的行为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);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

问题出路

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

解决方案

省略ConfigureAwait(false)

await dispatcher.Dispatch(domainEvent);

解释

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

当在 上使用ConfigureAwait(false) 选项async Task时,执行任务将不会在此上下文上恢复,而是在线程池线程上恢复。本质上,通过配置该await调用,当线程完成事件分派时,上下文已经被释放

建议

继续查看从请求开始失败的每个方法/函数调用,看看您是否可能正在更改via的默认行为或类似的更改。async/awaitConfigureAwait(false)

欲了解更多信息,我强烈建议您关注Stephen Cleary 在他的博客上提供的链接,async了解更多信息。