【问题标题】:Task when all, connection is closing任务全部完成,连接正在关闭
【发布时间】:2016-09-23 13:11:12
【问题描述】:

我正在尝试使用 Task.WhenAll 执行多个 SqlDataReader。但是当等待任务时,我得到了

"System.InvalidOperationException: 无效操作。连接 已关闭”。

创建任务:

        List<Task<SqlDataReader>> _listTasksDataReader = new List<Task<SqlDataReader>>();
        _listTasksDataReader.Add(GetSqlDataReader1(10));
        _listTasksDataReader.Add(GetSqlDataReader2(10));
        SqlDataReader[] _dataReaders = await Task.WhenAll(_listTasksDataReader);

我的“SqlDataReader”方法:

    public Task<SqlDataReader> GetSqlDataReader1(int recordCount)
    {
        using (var sqlCon = new SqlConnection(ConnectionString))
        {
            sqlCon.Open();
            using (var command = new SqlCommand("sp_GetData", sqlCon))
            {
                command.Parameters.Clear();
                command.Parameters.Add(new SqlParameter("@recordCount", recordCount));
                command.CommandType = System.Data.CommandType.StoredProcedure;
                return command.ExecuteReaderAsync();
            }
        }
    }

Task.WhenAll 执行时不应该打开数据库连接还是我遗漏了什么?

【问题讨论】:

  • 您有两个连接。您在哪个连接上遇到错误?我会在 sqlCon.Open() 上设置一个断点,看看您的代码是否正在运行 [两次] 并且连接实际上正在打开。
  • 一旦您的ExecuteReaderAsync 调用返回,您的连接将被关闭(正如它在using 语句中声明的那样)。你的ExecuteReaderAsync 调用返回before数据被读取(毕竟它是一个异步读取器!)。
  • 连接被关闭是因为你因为return语句而退出了using块。你应该看看codereview.stackexchange.com/a/22916/54013
  • 罗伯特在第二个(“GetSqlDataReader2”)
  • @AsusT9 你必须同时改变这个方法调用者。调用者应该提供和控制连接。关闭连接后,您无法使用阅读器

标签: c# async-await sqldatareader


【解决方案1】:

可以将 CommandBehavior.CloseConnection 传递给 ExecuteReaderAsync。然后连接将保持打开状态,直到返回的 datareader 对象关闭:请参阅 MSDN herehere。在这种情况下,SqlConnection 不需要在using 语句中。

像这样:

public Task<SqlDataReader> GetSqlDataReader1(int recordCount)
{
    var sqlCon = new SqlConnection(ConnectionString);
    sqlCon.Open();

    using (var command = new SqlCommand("sp_GetData", sqlCon))
    {
        command.Parameters.Clear();
        command.Parameters.Add(new SqlParameter("@recordCount", recordCount));
        command.CommandType = System.Data.CommandType.StoredProcedure;
        return command.ExecuteReaderAsync(CommandBehavior.CloseConnection);
    }
}

【讨论】:

    【解决方案2】:

    我错过了什么吗?

    您想获得一个没有底层连接的SqlDataReader?我认为这不会很好。当您从读者那里阅读时会发生什么?连接已关闭。

    所以,您可能只需要在关闭连接之前读取实际数据:

    public async Task<List<T>> GetData1(int recordCount)
    {
        using (var sqlCon = new SqlConnection(ConnectionString))
        {
            sqlCon.Open();
            using (var command = new SqlCommand("sp_GetData", sqlCon))
            {
                command.Parameters.Clear();
                command.Parameters.Add(new SqlParameter("@recordCount", recordCount));
                command.CommandType = System.Data.CommandType.StoredProcedure;
    
                var result = new List<T>();
                var reader = await command.ExecuteReaderAsync();
                // TODO: use `reader` to populate `result`
                return result;
            }
        }
    }
    

    【讨论】:

      【解决方案3】:

      更新:我将把它留在这里,但我刚刚记得你不能组合 yieldawait... 至少,现在还不行.


      请记住,调用command.ExecuteReaderAsync(),即使使用return 关键字,也不会停止方法的执行。这就是_Async() 方法的全部意义所在。因此,在该函数调用之后,代码立即退出 using 块。这具有在您有机会使用连接对象从 DataReader 读取数据之前处置连接对象的效果。

      尝试返回Task&lt;IEnumerable&lt;IDataRecord&gt;&gt;

      public async Task<IEnumerable<IDataRecord>> GetSqlDataReader1(int recordCount)
      {
          using (var sqlCon = new SqlConnection(ConnectionString))
          using (var command = new SqlCommand("sp_GetData", sqlCon))
          {
              command.Parameters.Add("@recordCount", SqlDbType.Int).Value = recordCount;
              command.CommandType = System.Data.CommandType.StoredProcedure;
      
              sqlCon.Open();               
              var rdr = await command.ExecuteReaderAsync();
              while (rdr.Read())
              {
                   yield return rdr;
              }
          }
      }
      

      请注意,这种模式有一个“陷阱”。每个yield return 使用相同的对象,因此如果你不小心,可能会发生一些奇怪的事情。我建议进一步更改此包含代码,将 rdr 对象中每条记录的数据放入它自己的(强类型)对象实例中:

      public async Task<IEnumerable<SomeObject>> GetSqlDataReader1(int recordCount)
      {
          using (var sqlCon = new SqlConnection(ConnectionString))
          using (var command = new SqlCommand("sp_GetData", sqlCon))
          {
              command.Parameters.Add(new SqlParameter("@recordCount", recordCount));
              command.CommandType = System.Data.CommandType.StoredProcedure;
      
              sqlCon.Open();                
              var rdr = await command.ExecuteReaderAsync();
              while (rdr.Read())
              {
                   yield return new SomeObject() {Field1 = rdr[1], Field2 = rdr[2], etc};
              }
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-07-13
        • 2013-07-16
        • 1970-01-01
        • 2021-02-03
        • 2012-02-07
        • 1970-01-01
        • 2013-04-22
        • 1970-01-01
        相关资源
        最近更新 更多