在using块内异步 [英] Async inside Using block

查看:71
本文介绍了在using块内异步的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在C#中具有以下异步功能:

I have the following async function in C#:

private async Task<T> CallDatabaseAsync<T>(Func<SqlConnection, Task<T>> execAsync)
{
    using (var connection = new SqlConnection(_connectionString))
    {
        connection.Open();
        return await execAsync(connection);
    }
}

通过提供连接对象并确保将其正确关闭,它允许执行将SQL连接作为参数并使用它进行数据库调用的任何异步函数 execAsync .

It allows to execute any async function execAsync that takes SQL connection as an argument and uses it to make a database call, by providing the connection object and ensuring it would be properly closed.

然后从WebApi控制器中的操作中调用此函数,如下所示:

This function is then called from an action in a WebApi controller, as follows:

public async Task<HttpResponseMessage> MyAction()
{
    Func<SqlConnection, Task<SomeType>> execAsync = (function definition here);
    await CallDatabaseAsync(execAsync);
    return Request.CreateResponse(HttpStatusCode.OK);
}

在我对WebApi操作进行一次更改之前,所有这些都很好用:我从其中删除了async/await.我不想等待数据库调用,因为我不在乎结果,我只是想开枪而忘记.

This all works great until I make one change to the WebApi action: I remove async/await from it. I do not want to wait for the database call because I do not care about the result, I just want to fire and forget.

这似乎仍然可以正常工作-即,如果我在浏览器中导航到操作的URL,则不会出现任何错误.但是实际上存在一个问题-数据库连接没有关闭.对该操作进行100次调用后,连接池达到其默认限制100,并且该应用程序停止工作.

This still seems to work fine - i.e. if I navigate to the action's URL in the browser I do not get any errors. But actually there is a problem - the database connection does not get closed. After 100 calls to the action, connection pool reaches its default limit of a 100, and the application stops working.

我做错了什么?我需要在 CallDatabaseAsync()中进行什么更改,以确保无论如何都将关闭连接?

What am I doing wrong? What do I need to change in CallDatabaseAsync() so that it absolutely ensures that the connection would be closed, no matter what?

推荐答案

在ASP.NET中,每个请求都有一个特殊的await之后的代码访问当前的 HttpContext ,它将访问属于同一ASP.NET请求的HttpContext.

In ASP.NET, each request has a special 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, it will access the HttpContext that belongs to the same ASP.NET request.

请求终止时,该请求的同步上下文将随之消失.现在,异步数据库访问完成后,它将尝试使用在await之前捕获的SynchronizationContext来运行await之后的代码(其中包括处理SQL连接的代码),但是它不能找不到请求了,因为请求已终止.

When a request terminates, the synchronization context of that request dies with it. Now, when the asynchronous database access completes, it tries to use the SynchronizationContext that it captured before the await to run the code after the await (which includes the code that disposes of the SQL connection), but it cannot find it anymore because the request has terminated.

在这种情况下,您可以做的是使等待后的代码不依赖于当前ASP.NET请求的SynchronizationContext,而是在线程池线程上运行.您可以通过

What you can do in this case is make the code after the await not depend on the current ASP.NET request's SynchronizationContext, but instead run on a Thread-pool thread. You can do this via the ConfigureAwait method like this:

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

这篇关于在using块内异步的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆