Overview
The full value of the C# using statement is often underrated. This article will describe what this statement does and why you should use it often. Note that this article is discussing the usingstatement in the context of defining object scope and disposal, not of namespace inclusion (i.e. not "using System.Data;").
The Common Problem
Although a managed language like C# typically handles managing memory and other resources on your behalf there are still certain objects you need to be careful with. One of these that almost any web programmer is aware of is a database connection object. Since these objects are often pooled and therefore not immediately destroyed if you're not careful to clean up the resources they reference you'll have problems. Forget to call the Close method after calling the Open method and you're just asking for trouble.
This is where the using statement comes in. In this article I'm going to make the bold suggestion that you never call the Close method of a database connection method. Suicidal? We'll see...
Typical Safety
Before I back up my suggestion let's first take a look at how we usually ensure that our resource critical objects are properly cleaned up. It's normal practice to first write code that achieves it's primary purpose. For instance if we were coding a data fetch we'd probably code something like:
- SqlConnection connection = new SqlConnection(connectionString);
- SqlCommand command = connection.CreateCommand();
- command.CommandText = "mysp_GetValue";
- command.CommandType = CommandType.StoredProcedure;
- connection.Open();
- object ret = command.ExecuteScalar();
- connection.Close();
In this case we've correctly released the connection when we're done with it. However what if something goes wrong while executing the query? A big fat exception will be thrown and the Closemethod will never be called. If this code is in a web page every time someone hits the page we're going to open another connection to the database. If you've never had the joy of this happen to you on a busy production server you're really missing out my friend.
Due to the plethora of database connection code samples out there almost everyone knows the "proper" way to do this:
- SqlConnection connection = new SqlConnection(connectionString);
- SqlCommand command = connection.CreateCommand();
- command.CommandText = "mysp_GetValue";
- command.CommandType = CommandType.StoredProcedure;
- object ret = null;
- try
- {
- connection.Open();
- ret = command.ExecuteScalar();
- }
- finally
- {
- if (connection != null)
- connection.Close();
- }
There, we've now done our duty and the code is now safe. Regardless of any pesky exceptions the SqlConnection will get closed after it's been opened. We couldn't do any better, right?
Well, I'm afraid I only give the above code a "B" grade. One of the reasons for this is because it's not fool-proof. A good coder codes defensively, not only for how the code currently runs but also for what could happen when the code is later adjusted (the code is always adjusted). Suppose some clever coder comes along and does this:
- SqlConnection connection = new SqlConnection(connectionString);
- SqlCommand command = connection.CreateCommand();
- command.CommandText = "mysp_GetValue";
- command.CommandType = CommandType.StoredProcedure;
- object ret = null;
- try
- {
- connection.Open();
- ret = command.ExecuteScalar();
- }
- finally
- {
- EmailerClass.EmailMe("ret = " + ret.ToString() + "!!!");
- if (connection != null)
- connection.Close();
- }
The poor coder has done his best to add some functionality he told his boss would be very easy to do - "it will only take 5 seconds". However what happens when ret is null or something goes wrong in the EmailerClass.EmailMe method which of course doesn't handle it's own exceptions? An exception is thrown that prevents the connection from being closed.
Using Using
The answer to these problems is the using statement. This statement guarantees that resources are properly cleaned up and that they're cleaned up in the most timely manner. Recoded with theusing statement our code block would look like:
- using (SqlConnection connection = new SqlConnection(connectionString))
- {
- SqlCommand command = connection.CreateCommand();
- command.CommandText = "mysp_GetValue";
- command.CommandType = CommandType.StoredProcedure;
- connection.Open();
- object ret = command.ExecuteScalar();
- }
The big change here is that we're wrapping the instantiation of our resource critical object in the "resource-acquisition" part of a using statement. The curly braces after this define the scope of the object. When the object goes out of scope it is automatically cleaned up. In the case of our SqlConnection object, it's connection will be closed. This is true regardless of any exception that gets thrown. So you don't have to worry about other programmers not being careful about what code they inject and where they inject it. The code that you wrote is safe.
There you have it - a database connection object being used with no call being made to it's Closemethod. If you're interested in how this "magic" works and what the performance implications are, read on. Otherwise just start coding with using statements.
IDisposable
So how does our using statement, as coded above, know that for the particular object involved (the database connection object) the connection should be closed when the object goes out of scope? The answer is that this is done in the usual object-oriented way - via the implementation of an interface. The interface for this is called IDisposable and it is important enough to be found in the System namespace.
When a class implements the IDisposable interface it must implement a method called Dispose. It is this method that gets called when an object goes out of the scope of a using statement. In effect the above code is functionally equivalent to:
- IDisposable connection = null;
- try
- {
- connection = new SqlConnection(connectionString);
- SqlCommand command = connection.CreateCommand();
- command.CommandText = "mysp_GetValue";
- command.CommandType = CommandType.StoredProcedure;
- ((SqlConnection)connection).Open();
- object ret = command.ExecuteScalar();
- }
- finally
- {
- connection.Dispose();
- }
This implies that only objects that implement IDisposable can be used in a using statement, which is important to note. It is also important to note that calling Dispose explicitly (or implicitly via a using statement) can have performance benefits. As MSDN states: "Code that is using a resource can call Dispose to indicate that the resource is no longer needed. If Dispose is not called, then automatic disposal eventually occurs as a consequence of garbage collection." Yet another reason to use using statements.
But "wait a minute" you say! "What if I need my object to be a class member and therefore can't wrap it's scope in a using statement?" Good question! The answer to is to have your class implement IDisposable and in the Dispose method call the Dispose method of your member object. Then whenever you instantiate your class, do so in a using statement.
No comments:
Post a Comment