What's hot ? (and I mean really ...) - scroll down for more
1).  Code Templating - advanced usage of delegates & generics: my slides & demos are available for download! CodeProject article is also available.

2).  My series "TDD in the eyes of a simpleminded" is in progress(including code!): preface, part1, part2, Q&A 1, Manual Stub .vs. Mock Stub

3).  TDD Workshop: SeeCompass v0.1 and v0.2 are out.
 Monday, May 22, 2006

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.

 |