I want to elaborate on my main ideas from my presentation about Code Templating – abstracting your code via delegates & anonymous methods. During the presentation, I talked about solutions to repetitive, every-day tasks. One of the examples I presented was something *we are all familiar with and that is querying our database. Lets take a look shall we?
public static List<User> GetUsersList()
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
SqlCommand cmd = new SqlCommand(“Select Id, Name From Users”, conn);
conn.Open();
List<User> users = new List<User>();
SqlDataReader r = cmd.ExecuteReader();
while (r.Read())
{
User u = new User();
u.Id = Convert.ToInt32(r[“Id”]);
u.Name = Convert.ToString(r[“Name”]);
users.Add(u);
}
return users;
}
}
Now let’s imagine that we also have Get*List for Products, Orders, Items and Sales. That’s 5 Get*List methods already. Let’s look at the code that is common among those potential methods (in red):
public static List<User> GetUsersList()
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
SqlCommand cmd = new SqlCommand(“Select Id, Name From Users”, conn);
conn.Open();
List<User> users = new List<User>();
SqlDataReader r = cmd.ExecuteReader();
while (r.Read())
{
User u = new User();
u.Id = Convert.ToInt32(r[“Id”]);
u.Name = Convert.ToString(r[“Name”]);
users.Add(u);
}
return users;
}
}
So the first solution to refactor those lines of (repetitive)code out will be, obviously, via OOP.
Here is a quick solution I came up with just to make the point clear:
interface IDataReaderParser<TRet>
{
TRet Parse(IDataReader liveReader);
}
static class DbServices
{
public static TRet ExecuteReader<TRet>(
IDbCommand cmd,
IDataReaderParser<TRet> parser)
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
cmd.Connection = conn;
conn.Open();
IDataReader reader = cmd.ExecuteReader();
return parser.Parse(reader);
}
}
}
So far we have some “parser” interface which will get a liveReader and return some generic type based on the required parsing. I don’t know if you’ve noticed but DbServices.ExecuteReader holds all the lines I marked with red just before.
Let’s look at our GetUsers parser:
class GetUserListParser : IDataReaderParser<List<User>>
{
public List<User> Parse(IDataReader liveReader)
{
List<User> users = new List<User>();
while (liveReader.Read())
{
User u = new User();
u.Id = Convert.ToInt32(r[“Id”]);
u.Name = Convert.ToString(r[“Name”]);
users.Add(u);
}
return users;
}
}
Our parser get the live IDataReader and returns a list of users. Simple.
GetUsersListParser.Parse method contains the rest of the code (all code minus code in red) from our original GetUsersList method.
Finally, our GetUsersList method looks like this:
public static List<User> GetUsersList()
{
SqlCommand cmd = new SqlCommand(“Select Id, Name From Users”);
GetUserListParser parser = new GetUserListParser();
return DbServices.ExecuteReader<List<User>>(cmd, parser);
}
That’s nice, but is it good enough ??
For every Get*List method we will have to build a separate class which will implement IDataReaderParser<TRet>. Let’s pause here.
* breath…. good …. *
Let’s rewind. When I started writing the original GetUsersList method, my main goal were those lines:
// (1) Create command
SqlCommand cmd = new SqlCommand(“Select Id, Name From Users”);
// (2) In some magical way, execute the command and return a live reader so I can parse it into objects.
List<User> users = new List<User>();
while (liveReader.Read())
{
User u = new User();
u.Id = Convert.ToInt32(r[“Id”]);
u.Name = Convert.ToString(r[“Name”]);
users.Add(u);
}
return users;
Open a connection against the database, executing the command as reader and disposing the connection were irrelevant at the time, I knew that I will have to write those lines down but they were just means to get to my real goal(=my real code). So I refactored my code via some sort of OOP solution.
Now I have pieces of code all over the place and even worse – in 1-2 months from now I will have to “Go To Definition” just to remember what the hack is GetUsersListParser class.
My main code was refactored out of my method.
Life shouldn’t be so hard. Like a very wise(and old, they are always old) programmer once said: “If you code it, it will come;”.
Let’s look at a different solution – let’s abstract our code via delegates & anonymous methods.
So our DbServices.ExecuteReader<TRet> will look like:
public delegate TRet ReaderHandler<TRet>(IDataReader liveReader);
static class DbServices
{
public static TRet ExecuteReader<TRet>(
IDbCommand cmd,
ReaderHandler<TRet> handler)
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
cmd.Connection = conn;
conn.Open();
IDataReader reader = cmd.ExecuteReader();
return handler(reader);
}
}
}
DbServices.ExecuteReader receive a (Sql)command to execute and an “handler” – a method with the same signature as ReaderHandler<TRet> delegate. ExecuteReader method will execute the command as a reader and send it(the reader) to the method handler. So who is this “handler” I talk about so much ?! The anonymous method !!
Let’s look at the new version of GetUsersList:
public static List<User> GetUsersList()
{
SqlCommand cmd = new SqlCommand(“Select Id, Name From Users”);
return DbServices.ExecuteReader<List<User>>(cmd,
delegate(IDataReader liveReader) <– our handler, inline
{
List<User> users = new List<User>();
while (liveReader.Read())
{
User u = new User();
u.Id = Convert.ToInt32(liveReader[“Id”]);
u.Name = Convert.ToString(liveReader[“Name”]);
users.Add(u);
}
return users;
});
}
The entire logic is just in front of me now, no need to start smelling around it !
Just like in the first OOP solution, I don’t need to handle the connection, call the ADO.NETs’ ExecuteReader, nothing.
To sum up:
Delegates & anonymous methods 1 : 0 OOP
Think about solutions you’ve implemented via OOP and start thinking about delegates as an alternative abstraction technique. For many straight forward architectural problems, delegates will be by far a better solution than traditional OOP.
My next post on this matter will cover the expected question – when delegates solution is too complex??
* Well, most of us anyway. For the rest of you – be cool and play along.
Oren, you should be aware that a delegate is the OO equivalent of an interface of one method. The fact that C# allows you to inline a delegate’s implementation is the eqivalent of Java’s anonymous inner classes that let you do the exact same thing for larger interfaces as well. This does not contradict traditional OO principles in any significant way. To the contrary, the extensive use of delegates is exactly what OO is all about – programming to an interface and not an implementation. The notion of a single purpose one-method interface is documented under the GoF Command Pattern and you are using it "by the book".
Now, since I dislike Generics I will offer what I think is a better design for this refactoring.
This will be your utility method:
public static class DbServices
{
public delegate object GetRowValue(string byColumnName);
public delegate void RowHandling(GetRowValue getRowValue);
public static void ProcessQuery(IDbCommand cmd, RowHandling rowHandler)
{
using (SqlConnection conn = new SqlConnection(ConnectionString))
{
cmd.Connection = conn;
conn.Open();
IDataReader reader = cmd.ExecuteReader();
while (reader.Read())
{
rowHandler(delegate(string byColumnName)
{
return reader[byColumnName];
});
}
}
}
}
And this will be your usage scenario:
public static List<User> GetUsersList()
{
List<User> users = new List<User>();
DbServices.ProcessQuery(new SqlCommand("Select Id, Name From Users"),
delegate(GetRowValue getRowValue)
{
User u = new User();
u.Id = Convert.ToInt32(getRowValue("Id"));
u.Name = Convert.ToString(getRowValue("Name"));
users.Add(u);
});
return users;
}
I’m still using anonymous implementations and not outside classes to implement my interfaces (delegates), but this is only a wise strategy for simple isolated cases like this one.
You can see I avoided any use of Generics in the abstract utility method, I think Generics introduce unnecessary complexity and should rarly be used.