在将现有的同步方法转换为异步方法时,我不小心在其中一个方法上使用了“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/