如何在SqlConnection和SqlDataReader上正确使用异步方法?

Pet*_*ter 2 .net asp.net-mvc-4

我正在将一些旧的ASP.NET代码移植到.NET 4.5.我习惯用老式的方式使用SqlConnection/SqlCommand/SqlDataReader.现在我正在寻找使用异步原语以期获得更好的性能.

这是我打开连接的旧代码:

    static DBWrapper Open(string connKey) {
        string connStr = WebConfigurationManager.ConnectionStrings[connKey].ConnectionString;
        SqlConnection c = new SqlConnection(connStr);
        c.Open();
        DBWrapper retVal = new DBWrapper();
        retVal._c = c;
        return retVal;
    }
Run Code Online (Sandbox Code Playgroud)

类DBWrapper只是SqlConnection的一个瘦包装器,它有一些我需要的额外方法.

以下是我如何转换为新的异步样式:

    static async Task<DBWrapper> Open(string connKey) {
        string connStr = WebConfigurationManager.ConnectionStrings[connKey].ConnectionString;
        SqlConnection c = new SqlConnection(connStr);
        var v = c.OpenAsync();
        DBWrapper retVal = new DBWrapper();
        await v;
        retVal._c = c;
        return retVal;
    }
Run Code Online (Sandbox Code Playgroud)

基本上,在打开连接时,我可以同时创建一个新的DBWrapper对象.

我感到困惑的是,这个功能真的表现得更好还是更差.创建任务和等待它的开销可能远远超过创建新DBWrapper对象所需的时间.无论我是否使用异步,我最终都会返回一个完全构建的连接对象.

这是一个从数据库返回用户列表的方法:

 List<MyUsers> getUsers() {
     using(SqlCommand cmd ...) {
       using(SqlDataReader reader ...) {
          while(reader.Read() {
             ... Fill the list
          }
       }
 }
Run Code Online (Sandbox Code Playgroud)

我也可以将此方法转换为异步样式:

while (await reader.ReadAsync())
  ... Fill the list
}
Run Code Online (Sandbox Code Playgroud)

我再一次看不到任何性能提升.事实上,在第二个例子中,我在等待ReadAsync()时甚至没有同时做任何其他事情.

这是正确的方法还是有更好的方法来使用异步范例与数据库连接和查询?

Ste*_*ary 13

请记住,async这并不意味着"更快"; 它意味着"并发".也就是说,重点async在于您可以在操作进行时执行其他操作.在UI应用程序中,这个"别的东西"通常是"响应用户输入"; asyncUI应用程序的主要好处是响应能力.对于ASP.NET应用程序,它的工作方式有点不同......

如果您使用async"一直向上",那么asyncASP.NET应用程序的主要优点是可伸缩性.通过"一直向上",我的意思是如果你的List<MyUsers> getUsers()方法变成了Task<List<MyUsers>> getUsersAsync(),并且调用它的方法变成了async等等,一直到你的MVC动作/ WebPage方法/无论什么(也是async),那么你"一路"异步".

在那种情况下,您将获得可伸缩性优势.请注意,每个单独的操作都不会运行得更快,但整个应用程序的扩展性会更好.当await产生ASP.NET运行时,请求线程被释放以处理其他请求,而该数据库连接正在接收下一行数据.使用同步代码,只要您的应用在与数据库通信时等待I/O,它就会阻塞一个线程(它什么都不做); async在等待I/O时释放该线程.

因此,整个应用程序可以更好地扩展,因为它更好地利用了线程池线程.

确实有时你可以通过请求中利用并发来获得收益; 例如,如果一个请求必须访问数据库并且还调用一些Web API,那么您可以启动它们并使用await Task.WhenAll(...)(异步)等待两个结果.在您的第一个代码示例中,您正在同时进行数据库连接和对象创建,但我怀疑在这种情况下您不会真正看到任何好处,除非您的构造函数的计算成本非常高.如果您有两个I/O操作或一个真正的CPU操作,那么async可以帮助您请求中进行并发,但主要用例是提高整个应用程序的可伸缩性.

  • 异步的开销几乎可以忽略不计.如果您有可扩展的后端(例如,Azure SQL),那么我将对任何I/O使用async.我不会使用异步的唯一地方是1)后端不可扩展(例如,单个SQL Server实例)**和**2)大多数请求命中后端**和**3)你已经有一个同步解决方案.在这种情况下,*转换*到异步不会获得任何好处.但是,如果你的后端可以扩展,或者有很多请求做其他事情(没有命中后端),或者你正在开始一个新的解决方案,那么使用异步. (2认同)