Using 块中的异步
Async inside Using block
我在 C# 中有以下异步函数:
private async Task<T> CallDatabaseAsync<T>(Func<SqlConnection, Task<T>> execAsync)
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
return await execAsync(connection);
}
}
它允许执行任何异步函数 execAsync 以 SQL 连接作为参数并使用它进行数据库调用,方法是提供连接对象并确保它会被正确关闭。
然后从 WebApi 控制器中的操作调用此函数,如下所示:
public async Task<HttpResponseMessage> MyAction()
{
Func<SqlConnection, Task<SomeType>> execAsync = (function definition here);
await CallDatabaseAsync(execAsync);
return Request.CreateResponse(HttpStatusCode.OK);
}
这一切都很好,直到我对 WebApi 操作进行了一项更改:我从中删除了 async/await。我不想等待数据库调用,因为我不关心结果,我只想开火就忘了。
这似乎仍然可以正常工作 - 即,如果我在浏览器中导航到操作的 URL,我不会收到任何错误。但实际上有一个问题——数据库连接没有关闭。调用操作 100 次后,连接池达到其默认限制 100,应用程序停止工作。
我做错了什么?我需要在 CallDatabaseAsync() 中更改什么,以便它绝对确保无论如何都会关闭连接?
在ASP.NET中,每个请求都有一个特殊的SynchronizationContext. This synchronization context makes the code that runs after the await
use the same "context" of the original request. For example, if the code after the await
accesses the current HttpContext,它会访问属于同一个ASP.NET请求的HttpContext
。
当请求终止时,该请求的同步上下文也随之终止。现在,当异步数据库访问完成时,它会尝试使用在 await
之前捕获的 SynchronizationContext
到 运行 await
之后的代码(其中包括处理 SQL 连接),但它无法再找到它,因为请求已终止。
在这种情况下,您可以做的是让 await 之后的代码不依赖于当前 ASP.NET 请求的 SynchronizationContext
,而是 运行 线程池线程。您可以通过 ConfigureAwait 方法执行此操作,如下所示:
private async Task<T> CallDatabaseAsync<T>(Func<SqlConnection, Task<T>> execAsync)
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
return await execAsync(connection).ConfigureAwait(false);
}
}
我在 C# 中有以下异步函数:
private async Task<T> CallDatabaseAsync<T>(Func<SqlConnection, Task<T>> execAsync)
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
return await execAsync(connection);
}
}
它允许执行任何异步函数 execAsync 以 SQL 连接作为参数并使用它进行数据库调用,方法是提供连接对象并确保它会被正确关闭。
然后从 WebApi 控制器中的操作调用此函数,如下所示:
public async Task<HttpResponseMessage> MyAction()
{
Func<SqlConnection, Task<SomeType>> execAsync = (function definition here);
await CallDatabaseAsync(execAsync);
return Request.CreateResponse(HttpStatusCode.OK);
}
这一切都很好,直到我对 WebApi 操作进行了一项更改:我从中删除了 async/await。我不想等待数据库调用,因为我不关心结果,我只想开火就忘了。
这似乎仍然可以正常工作 - 即,如果我在浏览器中导航到操作的 URL,我不会收到任何错误。但实际上有一个问题——数据库连接没有关闭。调用操作 100 次后,连接池达到其默认限制 100,应用程序停止工作。
我做错了什么?我需要在 CallDatabaseAsync() 中更改什么,以便它绝对确保无论如何都会关闭连接?
在ASP.NET中,每个请求都有一个特殊的SynchronizationContext. This synchronization context makes the code that runs after the await
use the same "context" of the original request. For example, if the code after the await
accesses the current HttpContext,它会访问属于同一个ASP.NET请求的HttpContext
。
当请求终止时,该请求的同步上下文也随之终止。现在,当异步数据库访问完成时,它会尝试使用在 await
之前捕获的 SynchronizationContext
到 运行 await
之后的代码(其中包括处理 SQL 连接),但它无法再找到它,因为请求已终止。
在这种情况下,您可以做的是让 await 之后的代码不依赖于当前 ASP.NET 请求的 SynchronizationContext
,而是 运行 线程池线程。您可以通过 ConfigureAwait 方法执行此操作,如下所示:
private async Task<T> CallDatabaseAsync<T>(Func<SqlConnection, Task<T>> execAsync)
{
using (var connection = new SqlConnection(_connectionString))
{
connection.Open();
return await execAsync(connection).ConfigureAwait(false);
}
}