Generic Power: Retrieving Data with Nullable Fields

I’m working on a quick project here at work where I build a report from a third party’s SQL database. Every field in this database, except for the primary keys, can be null. This makes my job slightly annoying because I must check every field in a result set to determine if it contains null or an actual value. I could use an ORM tool, but this is a small project with a HUGE database. I only need a few tables, and I can write my DAL quickly (there’s also no relations defined in the database). My code to read the result set used to look like the following:

DateTime? date = null;

if (!_Reader.IsDBNull(dateOrdinal))
{
    date = _Reader.GetDateTime(dateOrdinal);
}

There's technically nothing wrong with this code. It works, but it consists of at least five lines of code. I could shorten this by using a ternary operator:

DateTime? date = (!_Reader.IsDBNull(dateOrdinal)) ? _Reader.GetDateTime(dateOrdinal) : null;

This approach is ugly. I have no qualms using ternary operators in JavaScript. In JavaScript, anything that cuts down file size without limiting performance is a-ok in my book, but I’m dealing with the compiled language of C#. Readability is more important than file size. As I’ve recounted many times, I’m lazy; I want to write less code, and in this case without sacrificing readability and performance. Then I thought of the solution: generics.

MSDN defines generics as “the concept of type parameters, which make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code.” I use generics all the time, but I rarely use them outside the realm of collections. I saw generics as the perfect solution to this particular problem. So I wrote two methods to check if the field has a value and return either the value as a specified data type or the type’s default value.

private T GetValue<T>(int ordinal)
{
    if (!_Reader.IsDBNull(ordinal))
    {
        return (T)_Reader[ordinal];
    }
    else
    {
        return default(T);
    }
}

private T GetValue<T>(string columnName)
{
    try
    {
        int ordinal = _Reader.GetOrdinal(columnName);
        return GetValue<T>(ordinal);
    }
    catch (IndexOutOfRangeException ex)
    {
        throw new Exception("Column name does not exist");
    }
}

These methods make retrieving data easy and readable:

DateTime? date = GetValue<DateTime?>(dateOrdinal);
string grantor = GetValue<string>("grantor");
decimal? amount = GetValue<decimal?>(4);

Of course, this solution’s benefits extend only to retrieving database fields that can be null; use the “Get” methods (GetDateTime(), GetString(), GetInt32(), ect) on fields that cannot be null.

Update (April 6, 2009): There are some performance issues with this approach. Check this blog post for more info.

4/1/2009 10:14:11 AM | Tags: .NET, C#
© 2008 Jeremy McPeak