简体   繁体   中英

Where to close SqlDataReader object and SqlConnection object?

I call a function which returns a SqlDataReader object to calling statement. I am confused where should I close the SqlDataReader object and SqlConnection object? In function or after calling it?

This is the function call:

SqlDataReader dr2= RetrieveSearcher();
pid = dr2[0].ToString();

This is the function:

protected SqlDataReader RetrieveSearcher()
{
    String Q = "select price from tb3 where pid='12';
    cn = new SqlConnection("data source=.\\sqlexpress; integrated security=true; initial catalog=singh");
    cn.Open();

    cmd = new SqlCommand(Q,cn);
    dr1 = cmd.ExecuteReader();
    dr1.Read();

    return dr1;
}
  1. Always use parameterized queries to avoid sql injection attacks and increase performance (most db servers can reuse execution plans with proper queries)
  2. Never leave a connection open any longer than necessary!
  3. Do not share db connections! Create it, use it, destroy it.
  4. Wrap everything that implements IDisposable in a using block like Connections, Commands, DataReaders, etc. This ensures no resources remain open even in the event of an exception.
  5. Use correct types in your db schema and read those types, do not blanket-convert everything to/from string! Example price seems like it should really be a decimal or numeric value and not a string so do not store it as a string and do not read it back as a string.
  6. Retrieve the connection strings by name from the app.config or web.config (depending on the application type), do not hard code the strings into your connections or anywhere else.

About your logic

Change your method to return a custom type like a piece of data. This ensures proper SoS (Separation of Concerns). Do not return a DataReader! This will abstract the whole database call from the caller which is what you should strive for.

protected SomeType RetrieveSearcherData(string pid)
{
    const string Q = "SELECT price FROM tb3 WHERE pid = @pid";
    using(var cn=new SqlConnection())
    using(var cmd=new SqlCommand(Q,cn))
    {
        // I do not know what pid is but use tho correct type here as well and specify that type using SqlDbType
        cmd.Parameters.Add(new SqlParameter("@pid", SqlDbType.VarChar, 100) { Value = pid});
        cn.Open();
        using(var dr1= cmd.ExecuteReader())
        {
            if(dr1.Read())
            {
               var result = dr1.GetDecimal(0);
               // read something and return it either in raw format or in some object (use a custom type)
            }
            else
              return null; // return something else that indicates nothing was found
        }
    }
}
  1. Do you really want to open a connection each time you call into this function? Having one thread deal with multiple connections is a sure fire way to get deadlocks.

  2. If you still want to do #1, I'd recommend having your RetrieveSearcher return the data it needs in a List<T> or heck, just return a DataTable and deal with that. That way the function can close the connection that it opened.

  3. If you still REALLY want to return a SqlDataReader then you need to make sure that you can close the connection that you opened. SqlDataReader doesn't expose a SqlConnection directly, so you can't directly close the connection after you leave the RetrieveSearcher method. However, you can do this:

     dr1 = cmd.ExecuteReader(CommandBehavior.CloseConnection); 

    That will close the connection when the reader is closed. So, then you can do:

     using (SqlDataReader dr2 = RetrieveSearcher()) { pid=dr2[0].ToString(); } 

I'm assuming of course that you REALLY need more than just one string. :) If you REALLY only need one string you just be returning the string and calling cmd.ExecuteScalar();

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM