Anonymous Methods as Parameters
If you are like me you hate writing the same mundane code over and over and over...puke!! You see this a lot when it comes to querying data from a database. There are a lot of nifty frameworks that consolidate this type of functionality and allows you, as a developer, to focus on the needs of the business.
If you are ever in a situation, however, wherein the powers that be do not condone the use of frameworks (as odd as that might be), here's a nifty little way to harness the power of Anonymous Methods as parameters in an attempt to isolate the mundane code in one single implementation (buckle up).
To retrieve a result set from a database, for example, all you need is a helper class that has one simple method with a signature like the following:
DbHelper.ExecuteReader(DataReaderHandler dataReaderHandler,
IEnumerable<IDbDataParameter> parameters,
string procedureName)
Notice the DataReaderHandler parameter. This is reference to a delegate that is declared inside the DbHelper class:
public delegate void DataReaderHandler(IDataReader reader);
If you wanted retrieve a list of user ids from a database, for example, the calling code that exercises the "ExecuteReader" method would look like this:
public List<int> GetUserIds()
{
List<int> ids = new List<int>();
List<IDbDataParameter> parameters = new List<IDbDataParameter>();
// populate parameters (you can use a helper class to do this as well -- but I'm not going to cover that in this posting)
DbHelper.ExecuteReader(
delegate(IDataReader reader) { ids = PopulateUserIds(reader); },
parameters,
"SelectUserIds" ) ;return ids;
}
The magic happens when the executing code declares an anonymous method in-line:
delegate(IDataReader reader) { ids = PopulateUserIds(reader); }
When the code is executed the runtime passes the anonymous method as the first parameter. This delegate reference is used by the "DbHelper.ExecuteReader" method as follows:
public static void ExecuteReader(DataReaderHandler dataReaderHandler,
IEnumerable<IDbDataParameter> parameters,
string procedureName)
{using (IDbConnection connection = new SqlConnection(connectionString))
{
using (IDbCommand command = new SqlCommand(procedureName, connection))
{
command.CommandType = CommandType.StoredProcedure;foreach (IDbDataParameter parameter in parameters)
{
command.Parameters.Add(parameter);
}connection.Open();
using (IDataReader reader = command.ExecuteReader())
{
dataReaderHandler(reader);
}
}
}}
Things to note:
- The "connectionString" will ideally be read from a configuration file.
- This example uses both SqlCommand and SqlConnection objects. You can implement a different database provider depending upon your applications requirements.
- The ExecuteReader method has a "void" return type.
- The use of "using" statements ensures that connections are closed and that all objects are properly disposed of by the .Net garbage collector.
To cap off this little example, when the "dataReaderHandler(reader)" delegate is invoked inside the "ExecuteReader" method, the runtime routes the call to the "PopulateUserIds" method that was specified when the "DbHelper.ExectureReader" method was called. The "PopulateUserIds" method signature calls for one input parameter of type IDataReader. It uses this reader object to parse the result set and return the desired collection of user ids:
private static List<int> PopulateUserIds(IDataReader reader)
{
List<int> ids = new List<int>();while (reader.Read())
{
ids.Add(int.Parse(reader["USER_ID"].ToString()));
}return ids;
}
Now, as you can imagine, there is more database interaction then simply retrieving a result set. You can build out the DbHelper class to account for the return of scalar values, execute non-query procedures, inserting, updating, even interact with different database providers, etc...
The nice part about leveraging Anonymous Methods as parameters to framework level components is that it allows the framework to do all of the heavy lifting, while allowing you to have control over how the operation needs to be handled from a business perspective.
Applying this same technique to non-database centric activities helps you write less code and ultimately be more productive (IMHO).
Comments