Note that disposing a SqlDataReader instantiated using SqlCommand.ExecuteReader() will not close/dispose the underlying connection.
There are two common patterns. In the first, the reader is opened and closed within the scope of the connection:
using(SqlConnection connection = ...)
{
connection.Open();
...
using(SqlCommand command = ...)
{
using(SqlDataReader reader = command.ExecuteReader())
{
... do your stuff ...
} // reader is closed/disposed here
} // command is closed/disposed here
} // connection is closed/disposed here
Sometimes it's convenient to have a data access method open a connection and return a reader. In this case it's important that the returned reader is opened using CommandBehavior.CloseConnection, so that closing/disposing the reader will close the underlying connection. The pattern looks something like this:
public SqlDataReader ExecuteReader(string commandText)
{
SqlConnection connection = null;
SqlCommand command = null;
try
{
connection = new SqlConnection(...);
connection.Open();
command = new SqlCommand(commandText, connection);
return command.ExecuteReader(CommandBehavior.CloseConnection);
}
catch
{
// Close connection before rethrowing
command.Close();
connection.Close();
throw;
}
}
and the calling code just needs to dispose the reader thus:
using(SqlDataReader reader = ExecuteReader(...))
{
... do your stuff ...
} // reader and connection are closed here.