I am just looking at the using statement, I have always known what it does but until now not tried using it, I have come up with the below code:
using (SqlComma
First, your code example should be:
using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
cmd.Connection.Open();
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
With the code in your question, an exception creating the command will result in the just-created connection not being disposed. With the above, the connection is properly disposed.
If you need to handle exceptions in construction of the connection and command (as well as when using them), yes, you have to wrap the entire thing in a try/catch:
try
{
using (SqlConnection conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString))
using (SqlCommand cmd = new SqlCommand(reportDataSource, conn))
{
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
cmd.Connection.Open();
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
}
catch (RelevantException ex)
{
// ...handling...
}
But you don't need to handle cleaning up conn
or cmd
; it's already been done for you.
Contrast with the same thing without using
:
SqlConnection conn = null;
SqlCommand cmd = null;
try
{
conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
cmd = new SqlCommand(reportDataSource, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
cmd.Connection.Open();
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
catch (RelevantException ex)
{
// ...handling...
}
finally
{
if (cmd != null)
{
try
{
cmd.Dispose();
}
catch { }
cmd = null;
}
if (conn != null)
{
try
{
conn.Dispose();
}
catch { }
conn = null;
}
}
// And note that `cmd` and `conn` are still in scope here, even though they're useless
I know which I'd rather write. :-)
using isn't about catching exceptions. It's about properly disposing of resources that are outside the view of the garbage collector.
I would make my decision on when to and when not to use the using statement dependant on the resource I am dealing with. In the case of a limited resource, such as an ODBC connection I would prefer to use T/C/F so I can log meaningful errors at the point they occurred. Letting database driver errors bubble back to the client and potentially be lost in the higher level exception wrapping is sub optimal.
T/C/F gives you peace of mind that the resource is being handled the way you want it to. As some have already mentioned, the using statement does not provide exception handling it just ensures the resource is destructed. Exception handling is an underuitilised and underestimated language structure that is often the difference between the success and failure of a solution.
When doing IO work I code to expect an exception.
SqlConnection conn = null;
SqlCommand cmd = null;
try
{
conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString)
cmd = new SqlCommand(reportDataSource, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
conn.Open(); //opens connection
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
Logger.Log(ex);
throw;
}
finally
{
if(conn != null)
conn.Dispose();
if(cmd != null)
cmd.Dispose();
}
Edit: To be explicit, I avoid the using block here because I believe it to be important to log in situations like this. Experience has taught me that you never know what kind of weird exception might pop up. Logging in this situation might help you detect a deadlock, or find where a schema change is impacting a little used and little tested part of you code base, or any number of other problems.
Edit 2: One can argue that a using block could wrap a try/catch in this situation, and this is completely valid and functionally equivalent. This really boils down to preference. Do you want to avoid the extra nesting at the cost of handling your own disposal? Or do you incur the extra nesting to have auto-disposal. I feel that the former is cleaner so I do it that way. However, I don't rewrite the latter if I find it in the code base in which I am working.
Edit 3: I really, really wish MS had created a more explicit version of using() that made it more intuitive what was really happening and given more flexibility in this case. Consider the following, imaginary code:
SqlConnection conn = null;
SqlCommand cmd = null;
using(conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString),
cmd = new SqlCommand(reportDataSource, conn)
{
conn = new SqlConnection(Settings.Default.qlsdat_extensionsConnectionString);
cmd = new SqlCommand(reportDataSource, conn);
cmd.CommandType = CommandType.StoredProcedure;
cmd.Parameters.Add("@Year", SqlDbType.Char, 4).Value = year;
cmd.Parameters.Add("@startDate", SqlDbType.DateTime).Value = start;
cmd.Parameters.Add("@endDate", SqlDbType.DateTime).Value = end;
cmd.Open();
DataSet dset = new DataSet();
new SqlDataAdapter(cmd).Fill(dset);
this.gridDataSource.DataSource = dset.Tables[0];
}
catch(Exception ex)
{
Logger.Log(ex);
throw;
}
A using statement just creates a try/finally with Dispose() calls in the finally. Why not give the developer a unified way of doing disposal and exception handling?
The using statement is actually changed into a try/finally block by the compiler in which the parameter of the using block is disposed of so long as it implements the IDisposable interface. Aside from ensuring the specified objects are properly disposed when they fall out of scope, there is really no error capturing gained by using this construct.
As is mentioned by TheSoftwareJedi above, you will want to make sure both the SqlConnection and SqlCommand objects are disposed of properly. Stacking both into a single using block is a bit messy, and might not do what you think it does.
Also, be mindful of using the try/catch block as logic. It's a code smell that my nose has a particular dislike for, and often used by newbies or those of us in a big hurry to meet a deadline.
So, basically, "using" is the exact same as "Try/catch/finally" only much more flexible for error handling.