Will putting a "using" statement around a DataReader close it?
I usually write my DataReader
code like this:
try
{
dr = cmd.ExecuteReader(CommandBehavior.SingleResult);
while (dr.Read())
{
// Do stuff
}
}
finally
{
if (dr != null) { dr.Close(); }
}
Is it safe to replace the try
and finally
with just a using
block around the DataReader
's creation? The reason I wonder is because in all the Microsoft examples I've seen they use a using for the connection but always explicitly call Close()
on the DataReader
.
Heres's an example from Retrieving Data Using a DataReader (ADO.NET):
static void HasRows(SqlConnection connection)
{
using (connection)
{
SqlCommand command = new SqlCommand(
"SELECT CategoryID, CategoryName FROM Categories;",
connection);
connection.Open();
SqlDataReader reader = command.ExecuteReader();
if (reader.HasRows)
{
while (reader.Read())
{
Console.WriteLine("{0}\t{1}", reader.GetInt32(0),
开发者_C百科 reader.GetString(1));
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
}
}
Yes. using
calls Dispose. Calling Dispose on SqlDataReader closes it.
This is psuedo-code of SqlDataReader gleaned from Reflector:
public void Dispose()
{
this.Close();
}
public override void Close()
{
if( !IsClosed )
CloseInternal(true);
}
private void CloseInternal(bool closeReader)
{
try
{
// Do some stuff to close the reader itself
}
catch(Exception ex)
{
this.Connection.Abort();
throw;
}
if( this.Connection != null && CommandBehavior.CloseConnection == true )
{
this.Connection.Close();
}
}
Typically, using()
calls Dispose()
and that calls close()
in turn.
In case of a DataReader, the Close is called only when CommandBehavior.CloseConnection
is set (see comments of this article http://weblogs.asp.net/joseguay/archive/2008/07/22/ensure-proper-closure-amp-disposal-of-a-datareader.aspx).
EDIT: This article says something interesting:
The Close() method on the SqlDataReader calls an InternalClose() method, which does not call Dispose. Note that previously we stated the correct way to do this was to have your close call dispose. To make it even more confusing the Dispose() method actually calls the Close() method so for this object the order is reversed.
Unlike the example here, my practice has been to employ a using block for the connection, the command and the reader. Note that you can stack nested using blocks to lower the indentation cost.
static void HasRows(SqlConnection connection)
{
using (connection)
using (SqlCommand command = new SqlCommand(
"SELECT CategoryID, CategoryName FROM Categories;",
connection))
{
connection.Open();
using (SqlDataReader reader = command.ExecuteReader())
{
if (reader.HasRows)
{
while (reader.Read())
{
Console.WriteLine("{0}\t{1}", reader.GetInt32(0),
reader.GetString(1));
}
}
else
{
Console.WriteLine("No rows found.");
}
reader.Close();
}
}
}
From what I can recall, if an exception occurs in a Using block, then the Dispose method is still called on the object. I usually have a Using statement for all disposable objects, without a Try..Catch.
EDIT: Forgot to say that for some objects, calling Dispose will in turn call Close for that object.
精彩评论