.net 无法访问已释放的对象,此错误的常见原因是释放上下文

ifsvaxew  于 2022-11-19  发布在  .NET
关注(0)|答案(8)|浏览(347)

我写了一个简单的应用程序,当我导航到我的编辑页面,下面的错误弹出。
微软.实体框架核心.查询[10100]
对上下文类型“app.Models. ApplicationDbContext”的查询结果进行迭代时出现异常。
系统.对象处理异常:无法访问已释放的对象。此错误的常见原因是释放了通过依赖项注入解析的上下文,然后又尝试在应用程序中的其他位置使用同一上下文示例。如果调用Dispose(),或者将上下文 Package 在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.csOnGet方法:

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

643ylb08

643ylb081#

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

如需详细信息:

4nkexdtk

4nkexdtk2#

我将要发布的不是这个问题的答案。但它是相关的,所以只是为了保存某人头痛,我张贴它。我遇到了这个相同的错误
系统.对象处理异常:无法访问已释放的对象等
下面是出现错误的代码(您能看到吗?):

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

是的,没错,我忘了把“await”放在一个使用EF核心dbcontext的函数前面。添加“await”修复了它。所以很容易错过它,特别是如果你很累,在最后期限之前。

qrjkbowd

qrjkbowd3#

当问题不是无效类型的任务时更新的答案如果您使用无效的方法,则接受的答案是正确的。如果您的方法不是无效的,则简单的解决方案可能会出现另一个问题。

1.您是否实现了某种模式或某种类型的关注点分离?
验证你的控制器方法是async Task,如果下行的方法也是async Task。在我的例子中,问题是我没有在控制器方法上放置async,但是下行的方法是async Task。
干杯!干杯!

2hh7jdfx

2hh7jdfx4#

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核心不使用[SynchronizationContext].(https://devblogs.microsoft.com/dotnet/configureawait-faq/)。
asyncTask上使用ConfigureAwait(false) * 选项 * 时,执行中的工作将不会在此内容上继续,而是在执行绪集区执行绪上继续。实际上,透过设定await呼叫,当thread finished dispatching the events时,内容已经处置。

建议

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

要了解更多信息,我强烈推荐Stephen Cleary在他的博客中关于asyncfollowing this link

gzjq41n4

gzjq41n45#

以下是我在尝试访问Startup.cs中的数据以在服务器负载上运行一些启动数据构建函数(而不是特定的Controller操作)时遇到的与上述相同的错误的情况下实际上起作用的方法:

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中做同样事情的人。

0yg35tkg

0yg35tkg6#

这是另一个回答谁会读这个主题。
我试图用javascript触发asnyc方法。因为它不能被触发,所以结果没有被async方法计算。我正在返回json对象到dotnet核心mvc中这样的视图。

return Json(_service.AsyncMethod(parameter));

我的IActionResult不是异步的,所以我不能添加“await”。我只是添加了“.Result”,它就会计算结果。

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

yhived7q7#

创建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;
            }
pgx2nnw8

pgx2nnw88#

主要的问题是你的数据仍然在服务器端,当你杀死上下文对象时,你的控制器或者你用来获取数据的任何东西都不能读取数据。
你必须使用.ToList()或类似的方法。这有助于将数据从服务器传递到客户端。所以我的意思是,当你使用.ToList()时,你的数据保存到你的内存中,你的依赖关系以上下文对象结束。

相关问题