c# - DbContext 在 Async 方法中处理得太早

标签 c# entity-framework asynchronous asp.net-core async-await

在将现有的同步方法转换为异步方法时,我不小心在其中一个方法上使用了“async void”,这导致了一些意外行为。

下面是我实际执行的那种更改的简化示例,

public IActionResult Index()
{
    var vm = new ViewModel();
    try
    {
        var max = 0;

        if (_dbContext.OnlyTable.Any())
        {
            max = _dbContext.OnlyTable.Max(x => x.SomeColumn);
        }

        _dbContext.Add(new TestTable() { SomeColumn = max + 1 });
        _dbContext.SaveChanges();

        MakePostCallAsync("http:\\google.com", vm);

        if (!string.IsNullOrEmpty(vm.TextToDisplay))
        {
            vm.TextToDisplay = "I have inserted the value " + newmax + " into table (-1 means error)";
        }
        else
        {
            vm.TextToDisplay = "Errored!";
        }


    }
    catch (Exception ex)
    {
        vm.TextToDisplay = "I encountered error message - \"" + ex.Message + "\"";
    }
    return View("Index", vm);
}

private async void MakePostCallAsync(string url, ViewModel vm)
{
    var httpClient = new HttpClient();

    var httpResponse = await httpClient.PostAsync("http://google.com", null).ConfigureAwait(true);

    newmax = _dbContext.OnlyTable.Max(x => x.SomeColumn);
}

问题是,MakePostCallAsync() 方法在尝试使用 DbContext 查询数据库时抛出一个异常,表明 DbContext 已被释放。

示例中的

_dbContext 是使用 ASP .Net Core 的 DI(通过 AddDbContext() 扩展)及其默认范围 (Scoped) 注入(inject)的。

我未能理解以下内容,需要帮助,

  • 为什么在服务请求之前就处理了数据库上下文,而 Scoped 对象的生命周期应该是当前请求的整个持续时间
  • 即使我使用了 ConfigureAwait(true)(这明确意味着一旦等待的方法返回 MakePostCallAsync() 应该在请求上下文中继续?)- 这似乎没有发生
  • 在实际代码中,一旦我使所有方法异步(一直到 Controller ),这个问题就得到了修复 - 在我共享的重现中,即使那样也无助于防止异常 - 为什么会这样,如何我如何解决这个问题?

Repro 在 https://github.com/jjkcharles/SampleAsync 中可用

最佳答案

你不应该使用 async void除非您正在编写事件处理程序。如果你想使用 async/await,你需要一直沿着调用堆栈向上移动,直到你返回一个 Task<IActionResult>

public async Task<IActionResult> Index()
{
    var vm = new ViewModel();
    try
    {
        var max = 0;

        if (_dbContext.OnlyTable.Any())
        {
            max = _dbContext.OnlyTable.Max(x => x.SomeColumn);
        }

        _dbContext.Add(new TestTable() { SomeColumn = max + 1 });
        _dbContext.SaveChanges();

        await MakePostCallAsync("http:\\google.com", vm);

        if (!string.IsNullOrEmpty(vm.TextToDisplay))
        {
            vm.TextToDisplay = "I have inserted the value " + newmax + " into table (-1 means error)";
        }
        else
        {
            vm.TextToDisplay = "Errored!";
        }


    }
    catch (Exception ex)
    {
        vm.TextToDisplay = "I encountered error message - \"" + ex.Message + "\"";
    }
    return View("Index", vm);
}

private async Task MakePostCallAsync(string url, ViewModel vm)
{
    var httpClient = new HttpClient();

    var httpResponse = await httpClient.PostAsync("http://google.com", null).ConfigureAwait(true);

    newmax = _dbContext.OnlyTable.Max(x => x.SomeColumn);
}

关于c# - DbContext 在 Async 方法中处理得太早,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40955450/

相关文章:

c# - 从 IOrderedEnumerable 中删除一个对象

c# - 如何使用用于 DocumentDb 单元测试的过滤器来模拟 DocumentClient CreateDocumentQuery?

c# - 检测具有相同 child 的实体

c# - 如何检测当前用户的审计日志?

javascript - 使用来自 ajax 调用的数据而不使用异步 : false

java - Spring @Async 每秒的速率

c# - RegionInfo.CurrentRegion 基于设备语言 - c#

c# - 为什么这个循环再做一次迭代?

c# - 我对处理器、内核、线程和并行性的理解是否正确?

c# - WPF DataGrid 列标题排序箭头在更改其 View 集合源的源后消失