Enveloped abstractions

Security Briefs

Syndication

Ever find yourself writing code like this?

// look up connection string
// open sql connection
// execute command
// DO SOMETHING WITH RESULT SET
// close data reader
// close command
// close connection

From time to time I see patterns like this, where there's common code that envelopes something specific that I want to do. I'd like to factor out the enveloping code that is the same (everything except "DO SOMETHING WITH RESULT SET").

One way to approach this using traditional OO languages would be to create a class that represented the operation, and have that class call a virtual function that you could override in a derived class. Or, use a function pointer or delegate. In either case, you'd end up splitting off the "DO SOMETHING" part into some other function, which sometimes obscured the block of code you were writing.

Now that we have anonymous delegates in C# 2.0, it becomes very natural to abstract this enveloping code:

DatabaseHelper.ExecuteSelect("select au_fname, au_lname from authors",
                             delegate(SqlDataReader r) {
    while (r.Read()) {
      Console.WriteLine("{0} {1}", r.GetString(0), r.GetString(1));
    }
  });

Using an anonymous delegate like this allows me to write code that almost looks like it's extended C# by adding an “ExecuteSelect“ statement (kind of like the using statement). Here's some simple code that shows how you might accomplish this. Add whatever error handling makes sense for your application, but this demonstrates the idea.

using System;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;

public delegate void HandleResultSet(SqlDataReader r);

public static class DatabaseHelper {
    public static void ExecuteSelect(string selectStatement,
                                     HandleResultSet fcn) {
        string connStr =
          ConfigurationManager.ConnectionStrings["pubs"].ConnectionString;
        using (SqlConnection conn = new SqlConnection(connStr))
        using (SqlCommand cmd = conn.CreateCommand()) {
            cmd.CommandText = selectStatement;
            conn.Open();
            using (SqlDataReader r = cmd.ExecuteReader()) {
                fcn(r);
            }
        }
    }
}

class Program {
    static void Main(string[] args) {

        Console.WriteLine("*** Authors ***");
        DatabaseHelper.ExecuteSelect("select au_fname, au_lname from authors",
          delegate(SqlDataReader r) {
            while (r.Read()) {
              Console.WriteLine("{0} {1}", r.GetString(0), r.GetString(1));
            }
          });

        Console.WriteLine("*** Titles ***");
        DatabaseHelper.ExecuteSelect("select title from titles",
          delegate(SqlDataReader r) {
            while (r.Read()) {
              Console.WriteLine(r.GetString(0));
            }
          });
    }
}


Posted Apr 11 2007, 07:24 AM by keith-brown
Filed under:

Comments

Israel Aece wrote re: Enveloped abstractions
on 04-11-2007 7:54 AM
Anonymous delegates/methods are very elegant...

Very nice snippet!
Craig (not Andera) wrote re: Enveloped abstractions
on 04-11-2007 8:28 AM
It's funny, I had an extremely similar idea a while back:

http://midwestcoders.net/blogs/craig_vermeer/archive/2005/11/14/186.aspx

Unfortunately I never used it outside of the initial idea and the blog post, so I can't speak to whether it worked all that well in practice.

Once thing that was really nice with anonymous delegates, though, was the fact that the anonymous delegate has access to the local variables of the method in which it is invoked, which helps keep the code cleaner.

It's nice to see that others had similar ideas, and that it wasn't just me trying to get cute with the language :-)
Keith Brown wrote re: Enveloped abstractions
on 04-11-2007 8:41 AM
That's right, CraigNotAndera. The capture of local variables makes this technique almost equivalent to extending the language with new statements like "using". I've been using this technique in my code ever since C# 2.0 was released; I'm teaching in Toronto this week and shared the idea with my students, which got me thinking that it would be good fodder for a blog post :-)

And I agree with Craig that the syntax kinda sucks (the fact that I have to write "delegate" in front of it and enclose it as a parameter to ExecuteSelect makes it a little ugly). Selective use of indentation helps, but I am looking forward to a cleaner syntax.
CraigBlog wrote Caveat Abstractor
on 04-11-2007 9:47 AM