How many times have you complained about how much repetitive code is required to properly dispose of objects in your code? How many times have you found yourself in a situation where you’re unsure whether or not you’re even supposed to dispose of an object in the first place?
I want to show you a technique which completely (or, at least, temporarily) avoids the need to bother yourself with those very important concerns. This is an extremely powerful technique which can be used for much more than dealing with IDisposable related issues, but that seems to be the scenario I use it on most often.
What not to do (AKA, what we’re used to doing)
Let’s take some scenarios we’re probably all very familiar with – ADO.NET’s vast array of IDisposable bad boys.
The first example below illustrates a typical usage of OleDbConnection, OleDbCommand, and OleDbDataReader objects.
Things to notice:
- There are ~15 lines of code in this method.
- Only ~5 lines of that code is specific to the act of returning a list of first names from the method.
- The surrounding ADO.NET related code could definitely be made more robust.
In case you haven’t pointed them out on your own yet, I’ve highlighted the important lines of code below:
ere’s a similar example. You can see we’re doing something different, but the same problems exist here that exist in the example above. The thing to notice here is that most of the code in this method, unfortunately, is related to using ADO.NET, and most of the code is exactly the same as the previous example.
Now think about that for a minute. Every single time we go to the database, there are ~10 extra lines of code we’re writing just to ensure that our application works smoothly. How do we know that we’ve written those ~10 lines correctly? And how do we ensure that we’ve written those ~10 lines in the same way throughout our entire application where a connection to the database is involved?
The solution is to completely delegate those responsibilities to something else. In other words, we’re not (yet) going to worry about those things.
How do we fix this?
If you look at the two examples above, you’ll probably be able to detect a common theme. We’re basically just going through the process of creating an OleDbDataReader (the reader object) so we can get values from each row in the table. All of the other code related to the connection, the command, the try/finally, disposing, null checking, etc…. is practically boilerplate code that we don’t want to have to deal with inside our GetFirstNames and NotifyPeople methods. Again, in these examples, it’s all about the OleDbDataReader and that’s very important to keep in mind.
Here’s the trick: What we want is a new method that allows us to instantiate a new OleDbDataReader with whatever information is required, and which will provide us with the OleDbDataReader only at the point where we need it. In this case, we need to pass a SQL query to instantiate the OleDbDataReader, and we want to use the OleDbDataReader every time we successfully call its Read() method.
Let’s look at some examples of what we’re trying to achieve. The examples below do the exact same things as their respective counterparts, yet the important work is immediately visible and easily readable.
Here’s how we can retrieve the first names….
And here’s how we can notify our people…
As you can see, pretty much all of the ADO.NET related code has been removed from GetFirstNames2, and NotifyPeople2. Pretty snazzy, eh?
It’s may be a little confusing to you, but the body of the method (highlighted below) actually gets called every time we successfully call the reader.Read() method on the while loop.
But what happened to all the other code, you ask? It’s in that method you see above called ForEachRowInReader. ForEachRowInReader fully handles the responsibility of creating the connection, wrapping everything in a try/catch, calling reader.Read(), disposing of any IDisposable objects, etc…. We’ve moved this complicated, error prone code in ForEachRowInReader so that it only exists in one place within our application.
Here’s what that method looks like:
As you can see, ForEachRowInReader pretty much only contains ADO.NET related code. There’s nothing in this method even remotely related to retrieving names from the database.
The heart and soul of ForEachRowInReader is the Action<OleDbDataReader> parameter:
What is Action<OleDbDataReader>? Well, it’s called a “delegate”, ironically. I know delegates are confusing if you’re not used to working with them, but hopefully this post will inspire you to take a closer look. What we’ve essentially done is wrap a method in another method, and even if you don’t completely understand what’s happening here, surely you will admit it’s pretty awesome.
While I fully intended on going into greater detail in this post, I think its length has reached a point where adding much more might confuse the reader. And since this is my first real post on my site, I think I’m going to save the rest for part two.
8/2/2010 1:14:39 PM Update by AD: To learn how to use this technique, be sure to check out my post on Using Delegates To Eliminate Duplicate Code.
So, happy coding, interwebz.