将现有的同步方法转换为异步时,我意外地使用了" 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)。
我没有帮助理解以下内容,
答案 0 :(得分:0)
在您的示例中,您没有等待拨打MakePostCallAsync("http:\\google.com", vm)
的电话。这意味着请求会立即继续执行,并最终执行处理_dbContext
的代码,可能是MakePostCallAsync
仍在等待HTTP客户端返回响应。一旦HTTP客户端确实返回响应,您的MakePostCallAsync
会尝试调用newmax = _dbContext.OnlyTable.Max(x => x.SomeColumn)
,但您的请求已经处理完毕,并且您的数据库上下文已被处理掉。
答案 1 :(得分:0)
在我看来,您在理解如何使用async-await时遇到一些麻烦,因为我在您的程序中看到了几个主要错误。
由非常有帮助的Stephen Cleary写的{p> This article,帮助我理解了如何正确使用它。每个想要使用async-await的函数都必须返回Task
而不是void
和Task<TResult>
而不是TResult
。此规则的唯一例外是事件处理程序,它们对操作的结果不感兴趣。
首先更改Index
,使其返回Task<IActionResult>
。完成此操作后,编译器可能会警告您忘记等待Index函数内的某个位置。
如果你调用异步函数,你不必等待它完成,你可以做其他有用的东西,只要异步函数必须等待某些事情就会执行。但是在你可以使用异步函数的任何结果之前,你必须等待它准备好:
var taskMakePostCall = MakePostCallAsync(...)
// if you have something useful to do, don't await
DoSomethingUseful(...);
// now you need the result of MakePostCallAsync, await until it is finished
await TaskMakePostCall;
// now you can use the results.
如果你的MakePostCallAsync会返回一些东西,例如一个int,那么返回值就是Task<int>
而你的代码就是:
Task<int> taskMakePostCall = MakePostCallAsync(...)
DoSomethingUseful(...);
int result = await TaskMakePostCall;
如果你没有什么有用的东西,请立即等待:
int result = await MakePostCallAsync(...);
您的异常的原因是您的MakePostCallAsync在某处之前没有完全完成处理您的dbContext,可能是通过using
语句。在返回之前添加此等待之后,您确定MakePostCallAsync在返回Index()之前已完全完成