关闭连接后,我可以保持SqlDataReader“活着”吗?

时间:2014-11-07 09:17:39

标签: c# asp.net-mvc dynamic sqldatareader

连接关闭后有没有办法访问SqlDataReader

或者是否有任何等同于SqlDataReader的对象我可以将阅读器存储到它们中并在以后对这些对象进行处理?

我从服务器接收了一个数据透视数据集,因此我无法使用普通类来处理这类数据,我的模型如下所示:

public class OneToNinetyNine
{
    public List<Cities> listCities;
    public string CityID;
    public DateTime DateFrom;
    public DateTime DateTo;
    // this is the reader that I attempt to pass to the views 
    public SqlDataReader SqlReader; 
}

3 个答案:

答案 0 :(得分:4)

关闭连接后,您无法使用DataReader,因为它需要使用连接从数据源检索数据。

您需要使用DataSet方法将数据读入DataTableLoad,然后才能关闭连接。

答案 1 :(得分:4)

您可以将SqlDataAdapter中的数据存储到DataSet中以备将来使用:

DataSet ds = new DataSet();
SqlCommand mycommand = new SqlCommand("sql statement");
using (SqlDataAdapter adapter = new SqlDataAdapter(mycommand))
{
    adapter.Fill(ds);
}

答案 2 :(得分:1)

  

连接关闭后有没有办法访问SqlDataReader?

没有。关闭连接后,阅读器无法从连接中读取数据。

但是,通过DataReaderConnection来电ExecuteReader,可以ExecuteReaderAsyncCommandBehavior.CloseConnection 之间的关系生命周期反转1}}指定。在此模式下,当Reader关闭(或处置)时,Connection将关闭。

如果您不一定想要一次性检索和实现查询中的所有数据,那么使用CommandBehavior.CloseConnection的长寿数据读者会非常有用,例如数据分页或接收类型惰性等用例评价。这是下面的选项2。

选项1:打开连接,检索并实现所有数据,并关闭所有内容

与其他答案一样,在许多情况下,对于小的,确定的数据提取,最好打开连接,创建命令,执行读取器,并一次性获取和具体化所有数据:

public async Task<Foo> GetOneFoo(int idToFetch)
{
   using (var myConn = new SqlConnection(_connectionString))
   using (var cmd = new SqlCommand("SELECT Id, Col2, ... FROM Foo WHERE Id = @Id"))
   {
      await myConn.OpenAsync();
      cmd.Parameters.AddWithValue("@Id", idToFetch);
      using (var reader = await cmd.ExecuteReaderAsync())
      {
         var myFoo = new Foo
         {
            Id = Convert.ToInt32(reader["Id"]),
            ... etc
         }
         return myFoo; // We're done with our Sql data access here
      } // Reader Disposed here
   } // Command and Connection Disposed here
}

选项2:打开连接,执行命令,并使用Long Lived Reader

使用CommandBehavior.CloseConnection,我们可以创建一个长期存在的读者,并推迟关闭连接,直到不再需要Reader

为了防止像DataReaders这样的数据访问对象流入更高层代码,可以使用yield return生成器来管理读者生命周期 - 同时确保读者(以及连接)一旦不再需要发电机,它就会关闭。

public async Task<IEnumerable<Foo>> LazyQueryAllFoos()
{
   // NB : No `using` ... the reader controls the lifetime of the connection.
   var sqlConn = new SqlConnection(_connectionString);
   // Yes, it is OK to dispose of the Command https://stackoverflow.com/a/744307/314291
   using (var cmd = new SqlCommand(
        $"SELECT Col1, Col2, ... FROM LargeFoos", mySqlConn))
   {
      await mySqlConn.OpenAsync();
      var reader = await cmd.ExecuteReaderAsync(CommandBehavior.CloseConnection);
      // Return the IEnumerable, without actually materializing Foos yet.
      // Reader and Connection remain open until caller is done with the enumerable
      return GenerateFoos(reader);
   }
}

// Helper method to manage lifespan of foos
private static IEnumerable<Foo> GenerateFoos(IDataReader reader)
{
    using(reader)
    {
       while (reader.Read())
       {
          yield return new Foo
          {
              Id = Convert.ToInt32(reader["Id"]),
              ...
          };
       }
    } // Reader is Closed + Disposed here => Connection also Closed.
}

备注

  • 在C#6,async代码当前也不能使用yield return,因此需要将helper方法从异步查询中分离出来(但是我可以相信将帮助器移动到Local Function中)
  • 调用GenerateFoos的代码确实需要注意不要将Enumerable(或它的Iterator)保留的时间超过需要的时间,因为这会使底层的Reader和Connection保持打开状态。