Earlier this year Jeremy Miller wrote an excellent article for MSDN magazine called Internal Domain Specific Languages. In it he suggests using extension methods as a simple way to create internal DSL's. Currently I'm writing a module to import and export data from a system so I've been mucking around with the ADO.NET API's. The repetitious code was getting to me and so I decided to take Jeremy's advice and go for a more declarative approach by creating a mini DSL.

Here is one of the constructs I was seeing over and over:

string select = string.Format("SELECT TOP {0} * FROM [{1}]", maxResults, tableName);
OleDbCommand command = new OleDbCommand(select, _connection);
DataSet dataSet = new DataSet();
OleDbDataAdapter adapter = new OleDbDataAdapter(command);
adapter.Fill(dataSet);

Now I know we could shorten this up a bit by moving the code around but the mechanics are still there. By taking a more declarative approach we want to do the following:

  1. Create a command
  2. Select a maximum amount of records from a table.
  3. Create an adapter.
  4. Fill a DataSet

Lets create the following extension methods to accomplish this task in a declarative fashion:

public static class OleDbConnectionExtensions
{
    public static OleDbCommand CreateCommand(this OleDbConnection connection)
    {
        return new OleDbCommand { Connection = connection };
    }
}
public static class OleDbCommandExtensions
{
    public static OleDbCommand SelectMaxFromTable(this OleDbCommand command, string tableName, int maxResults)
    {
        command.CommandText = string.Format("SELECT TOP {0} * FROM [{1}]", maxResults, tableName);
        return command;
    }

    public static OleDbDataAdapter CreateAdapter(this OleDbCommand command)
    {
        return new OleDbDataAdapter(command);
    }
}
public static class OleDbDataAdapterExtensions
{
    public static DataSet CreateAndFillDataSet(this OleDbDataAdapter adapter)
    {
        DataSet dataSet = new DataSet();
        adapter.Fill(dataSet);
        return dataSet;
    }
}

And voila!

DataSet result = _connection.
                    CreateCommand().
                    SelectMaxFromTable(columnOptions.TableName, 50).
                    CreateAdapter().
                    CreateAndFillDataSet();

Very easy to follow and very reusable. Doesn't this look eerily similar to pipelining in F#?? :)

Now we can go even farther if we want to make this even more terse by adding the following two extension methods (Which are themselves using our new mini DSL):

public static class OleDbConnectionExtensions
{
    public static OleDbCommand SelectMaxFromTable(this OleDbConnection connection, string tableName, int max)
    {
        return connection.CreateCommand().SelectMaxFromTable(tableName, max);
    }
}
public static class OleDbCommandExtensions
{
    public static DataSet CreateAndFillDataSet(this OleDbCommand command)
    {
        return command.CreateAdapter().CreateAndFillDataSet();
    }
}

There, much better:

DataSet result = _connection.
                    SelectMaxFromTable(columnOptions.TableName, 50).
                    CreateAndFillDataSet();

In our declarative syntax we really didn't care about the minutia of creating the command and adapter so we could eliminate that in our DSL.