Jose R. Guay Paz

Most of the time on
ASP.NET, C# & SQL Server

Sponsors

News

INETA Community Speakers Program

My latest tweets

this.blog.owner=

  • CSW Solutions
  • Follow Me
  • Works On My Machine

.NET Communities

  • INETA Latam

Blogs I Read

Websites I like

Ensure Proper Closure & Disposal Of A DataReader

Most of the time, we create programs that use DataReaders (weather they are on ASP.NET or Winforms). They're fast, easy to use but sometimes, they fill out our connections pool and withour proper care, they can cause problems to our users.

There are some blog posts around this issue and many questions in the forums, so I'll try to make it as simple as possible so you'll find it attractive to use, because in the end, there is no magic here, just a nice tip.

The solution: Use the DataReader within the scope of a using clause. How? Take a look at this sample:

Regular Programming
In general, programmers tend to use DataReaders like this:

SqlConnection connection = new SqlConnection("connection string");
SqlCommand cmd = new SqlCommand("SELECT * FROM SomeTable", connection);
SqlDataReader reader = cmd.ExecuteReader();
if (reader != null)
{
      while (reader.Read())
      {
             
//do something
      
}
}
reader.Close();
reader.Dispose();

While this would work and do excatly what you need it to do, it does not ensure that the reader will be actualy closed and disposed. Why? it's by design. DataReaders will be kept open until they got all the data from the source and even then, they depend on the command object that should be opened with the CommandBehavior.CloseConnection parameter.

So, anyway. Here is my proposed solution to overcome this problems.

using (SqlDataReader reader = cmd.ExecuteReader())
{
       if (reader != null)
       {
              while (reader.Read())
              {
                        //do something
              }
              reader.Close();
              reader.Dispose();
       }
}

Why this works better? Because the DataReader lives only within the scope of the using clause. Once the flow leaves the using clause the datareader is forced to deallocate from memory thus closing/disposing it properly.

Hope this will help you get the most of DataReaders because, yes, they are the FASTEST way to get data from any datasource.

Enjoy!!

 

Comments

AndrewSeven said:

Dispose() will call Close()

# July 22, 2008 4:47 PM

Mike said:

Isn't the call to Dispose no longer necessary once it is in the using statement.

It was my understanding that using was syntatic sugar for a try/finally block that calls dispose on the object.

# July 22, 2008 4:50 PM

Sully said:

Yeah, the lone fact of the "using" statement is that at the end of that statement, whatever object you were using is disposed, gone, caput.

# July 22, 2008 4:55 PM

Bivvo said:

If you need to return a reader from a method make sure to give the CommandBehavior.CloseConnection parameter to ensure the underlying connection is closed.

public static SqlDataReader GetReader()

{

  SqlConnection connection = new SqlConnection("connection string");

  SqlCommand cmd = new SqlCommand("SELECT * FROM SomeTable", connection);

  SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection);

  if (reader != null)

  {

        return reader;

  } else { return null ; }

}

using(SqlDataReader myReader = GetReader())

{

  if(myReader != null && myReader.HasRows())

  {

     // read in the data

  }

  myReader.Close();

}

From MSDN on using and calling Dispose. The using statement calls the Dispose method on the object in the correct way, and (when you use it as shown earlier) it also causes the object itself to go out of scope as soon as Dispose is called.

So calling dispose is not necessary.

# July 22, 2008 6:21 PM

pbz said:

@Bivvo

Returning a reader is calling for trouble. You have no control over the code that calls that and you could end up with full connection pools followed by errors and you won't have a clue why it's so "random."

Here's a way around it, but avoid if possible:

public static void Read(string query, Action<IDataReader> predicate)

   {

     if (predicate != null)

     {

       using (SqlConnection connection = new SqlConnection("connection string"))

       {

         using (SqlCommand command = new SqlCommand(query, connection))

         {

           using (SqlDataReader reader = command.ExecuteReader())

           {

             predicate(reader);

           }

         }

       }

     }

   }

   public static void SampleOfUsingRead()

   {

     int sum = 0;

     Read("SELECT Amount FROM SomeTable", p =>

       {

         while (p.Read()) sum += Convert.ToInt32(p["Amount"]);

       });

   }

This way you're guaranteed that the connection, command, and reader are always closed.

# July 22, 2008 11:32 PM

Joe said:

> Returning a reader is calling for trouble...

It's OK as long as you do it properly, i.e.

- catch / rethrow exceptions, ensuring the connection is closed in the case of an exception

- use CommandBehavior.CloseConnection to ensure the connection is closed when the reader is closed.

Bivvo's sample would then become:

public static SqlDataReader GetReader()

{

 SqlConnection connection = new SqlConnection("connection string");

 try

 {

   SqlCommand cmd = new SqlCommand("SELECT * FROM SomeTable", connection);

   return cmd.ExecuteReader(CommandBehavior.CloseConnection);

 }

 catch

 {

    connection.Close();

    throw;

 }

}

using(SqlDataReader myReader = GetReader())

{

 while (reader.Read())

 {

    ...

 }

}

# July 23, 2008 6:52 AM

mustafa ülkü said:

thanks

# December 29, 2008 2:55 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)