Есть ли способ получить доступ к SqlDataReader после закрытия соединения?
Нет. После того, как соединение закрыто, считыватель не может прочитать данные из соединения.
Однако можно инвертировать продолжительность жизни отношения между DataReader
и Connection
, используя вызовы ExecuteReader
или ExecuteReaderAsync
с указанным CommandBehavior.CloseConnection
. В этом режиме соединение закрывается, когда считыватель закрыт (или ликвидирован).
Долгоживущие считыватели данных с CommandBehavior.CloseConnection
могут быть полезны, когда вы не обязательно хотите извлекать и материализовывать все данные в запросе сразу, с такими вариантами использования, как подкачка данных или отложенное вычисление типа take-while. Это Вариант 2, ниже.
Вариант 1. Открыть соединение, получить и материализовать все данные и закрыть все
Как и в случае с другими ответами, во многих случаях для выборки небольших определенных данных предпочтительнее открывать Connection, Create Command, Execute Reader, а также извлекать и материализовывать все данные за один раз, например:
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. Откройте соединение, выполните команду и используйте долгоживущий ридер
Используя CommandBehavior.CloseConnection
, мы можем создать долгоживущий ридер и отложить закрытие Connection до тех пор, пока 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, поэтому необходимо отделить вспомогательный метод от асинхронного запроса (однако, вспомогательный метод, я думаю, можно переместить в локальную функцию).
- Код, вызывающий
GenerateFoos
, должен помнить о том, что не следует удерживать Enumerable (или его итератор) дольше, чем необходимо, так как это будет держать открытыми лежащие в основе Reader и Connection.
person
StuartLC
schedule
19.11.2017
DataSet
илиDataTable
с помощью методаLoad
, после чего вы можете закрыть соединение. - person Ben   schedule 07.11.2014DataTable
/DataSet
, если он уже используетSqlDataReader
. Ему просто нужно соответствующим образом инициализировать класс из полей считывателя. - person Tim Schmelter   schedule 07.11.2014Linq-To-Sql
, и использовать его отложенное выполнение возможностей. - person Tim Schmelter   schedule 07.11.2014DataTable
, вы также можете загрузить типизированныйList<OneToNinetyNine>
(или что-то еще). - person Tim Schmelter   schedule 07.11.2014DataReader
, даже если вы не знаете столбцы. Таким образом, вы можете загрузить, например, свойствоList<string>
as в нашем классе для динамической части. - person Tim Schmelter   schedule 07.11.2014