有什么方法可以在连接关闭后访问 SqlDataReader?
没有。连接关闭后,阅读器将无法从连接中读取数据。
但是,可以通过使用指定CommandBehavior.CloseConnection 的ExecuteReader 或ExecuteReaderAsync 调用来反转DataReader 和Connection 之间的关系寿命。在这种模式下,当 Reader 关闭(或 Disposed)时,连接也会关闭。
当您不一定要一次性检索和具体化查询中的所有数据时,使用 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:打开连接、执行命令并使用长寿阅读器
使用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,因此需要将辅助方法从异步查询中分离出来(不过,我相信辅助方法可以移动到本地函数中)。
- 调用
GenerateFoos 的代码确实需要注意不要将 Enumerable(或其迭代器)保持在所需的时间之外,因为这将使底层的 Reader 和 Connection 保持打开状态。