2016-09-23 21 views
5

Tôi đang cố thực thi nhiều SqlDataReaders bằng Task.WhenAll. Nhưng khi nhiệm vụ được chờ đợi, tôi nhận đượcNhiệm vụ khi tất cả, kết nối đang đóng

"System.InvalidOperationException: Hoạt động không hợp lệ. Kết nối bị đóng".

Creation nhiệm vụ:

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

của tôi "SqlDataReader" phương pháp:

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

nên không kết nối cơ sở dữ liệu được mở ra khi Task.WhenAll được thực hiện hay tôi thiếu một cái gì đó ?

+0

Bạn có hai kết nối. Bạn đang gặp lỗi nào khi kết nối? Tôi sẽ đặt điểm ngắt trên sqlCon.Mở() và xem liệu mã của bạn có đang chạy [hai lần] hay không và các kết nối trong thực tế đang được mở. –

+4

Ngay khi cuộc gọi 'ExecuteReaderAsync' của bạn trả về, kết nối của bạn sẽ bị đóng (như được khai báo bên trong câu lệnh' using'). Cuộc gọi 'ExecuteReaderAsync' của bạn trả về * trước * dữ liệu đã được đọc (tất cả đều là trình đọc không đồng bộ!). –

+2

Kết nối bị đóng vì bạn bước ra khỏi khối sử dụng vì câu lệnh trả về. Bạn nên xem http://codereview.stackexchange.com/a/22916/54013 – AntonR

Trả lời

2

UPDATE: Tôi sẽ để lại điều này ở đây, nhưng tôi vừa nhớ ra rằng bạn không được phép để kết hợp yieldawait ... ít nhất, không được nêu ra.


Hãy nhớ rằng gọi command.ExecuteReaderAsync(), ngay cả với từ khóa return, không dừng thực hiện phương thức. Đó là toàn bộ điểm của phương pháp _Async(). Vì vậy, ngay sau cuộc gọi hàm đó, mã sẽ thoát khỏi khối using. Điều này có tác dụng xử lý đối tượng kết nối của bạn trước khi bạn có cơ hội sử dụng nó để đọc từ DataReader của bạn.

Cố gắng trả lại một Task<IEnumerable<IDataRecord>> thay vì:

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

Lưu ý rằng có một "Gotcha" với mô hình này. Mỗi yield return sử dụng cùng một đối tượng và do đó một số điều kỳ lạ có thể xảy ra nếu bạn không cẩn thận. Tôi khuyên bạn nên tiếp tục thay đổi này bao gồm mã mà đặt dữ liệu từ mỗi bản ghi trong đối tượng rdr vào riêng dụ (mạnh mẽ, đánh máy) đối tượng của nó:

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}; 
     } 
    } 
} 
+0

Bây giờ đã viết lại các phương thức để thu thập dữ liệu từ trình đọc và trả lại một tác vụ để thay thế. – AsusT9

5

Có thể vượt qua một CommandBehavior.CloseConnection đến ExecuteReaderAsync. Sau đó, kết nối sẽ vẫn mở cho đến khi đối tượng datareader trả về bị đóng: xem MSDN herehere. Trong trường hợp đó, SqlConnection không cần phải có trong câu lệnh using.

Như thế này:

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

tôi thiếu cái gì?

Bạn đang cố gắng để có được một SqlDataReader không có kết nối cơ bản? Tôi không nghĩ rằng nó sẽ hoạt động tốt. Điều gì xảy ra khi bạn đọc từ người đọc? Kết nối đã được đóng.

Vì vậy, bạn có thể chỉ cần đọc các dữ liệu thực tế trước khi chốt kết nối:

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; 
     } 
    } 
} 
+0

Cảm ơn bạn đã phản hồi. Bây giờ tôi hiểu tại sao lỗi này xảy ra và đã điều chỉnh các phương pháp đọc dữ liệu. – AsusT9

Các vấn đề liên quan