Scott Van Vliet

Less Talk, More Rock

IDataReader Extension Methods

First of all -- I'm a total liar.  I was supposed to offer up some great insight from Tech•Ed.  But alas, I had to leave early and fly to Seattle for a meeting.  So, I wasn't able to share any fun stuff :(

We'll, I actually have a tiny bit of time to write, and I thought I'd share the result of recent code review.

I'm currently teaching .NET (Framework fundamentals, C#, et al.) to some of my clients who are preparing to take over the support of a very large application (we're going on 2 years of development and deployment!) As we were looking at some of the code, I came across some stuff from our offshore team that slipped by my team's radar. In this particular project, we found hundreds of lines that looked like this:

theatre.ID = (reader["theatre_id"] is DBNull) ?
   
0 : Convert.ToInt32(reader["theatre_id"]);

theatre.Code = (reader["theatre_code"] is DBNull) ?
   
String.Empty : reader["theatre_code"].ToString();

Ick!  I was very bummed to come across this while teaching my clients about best practice -- was a bit 'the fool'.  Not wanting to let this pass, I quickly redirected the discussion to talk about refactoring and reuse (which was kind of a nice segue to some OO topics.)  In this discussion, we wrote an Extension Method to the IDataReader class to clean-up this code.  The new code looked pretty:

theatre.ID = reader.GetValueOrDefault<int>("theatre_id");

theatre.Code = reader.GetValueOrDefault<string>("theatre_code");

The details of this Extension Method are as follows:

/// <SUMMARY>
/// Contains extension methods for the IDataReader interface.
/// </SUMMARY>
public static class DataReaderExtender
{
  /// <SUMMARY>
  /// This method will return the value of the specified columnName, cast to
  /// the type specified in T. However, if the value found in the reader is
  /// DBNull, this method will return the default value of the type T.
  /// </SUMMARY>
  /// <TYPEPARAM name="T">The type to which the value found in the reader should be cast.</TYPEPARAM>
  /// <PARAM name="reader">The reader in which columnName is found.</PARAM>
  /// <PARAM name="columnName">The columnName to retrieve.</PARAM>
  /// <RETURNS>The column value within the reader typed as T.</RETURNS>
 
public static T GetValueOrDefault<T>(this IDataReader reader, string columnName)
  {
   
object columnValue = reader[columnName];
    T returnValue = default(T);
    if (!(columnValue is DBNull))
    {
     
returnValue = (T)Convert.ChangeType(columnValue, typeof(T));
    }
    return returnValue; 
  
}
}

Enjoy! (I've posted the code here: DataReaderExtender.zip)

Comments

Rob said:

I do something very similar but I use Lambda's instead of the Convert stuff:

public static T GetValue<T>(this DbDataReader reader, string field, Func<int, T> code)

{

   T value = default(T);

   int ordinal = reader.GetOrdinal(field);

   if (!reader.IsDBNull(ordinal))

       value = code(ordinal);

   return (value);

}

and then you can do tasty stuff like this without the convert.

address.ID = dr.GetValue("ID", i => dr.GetInt32(i));

Now in my case I use DbDataReader and ordinal values instead of strings but the cool part is the lambda.

I also have one that supports nullable types on structs:

public static T? GetValueOrNull<T>(this DbDataReader reader, string field, Func<int, T> code) where T : struct

{

   T? value = null;

   int ordinal = reader.GetOrdinal(field);

   if (!reader.IsDBNull(ordinal))

       value = code(ordinal);

   return (value);

}

I use ordinals because my data mapping layer get generated from CodeSmith and I can 100% count on the ordinals being in the correct order.

I don't see this kind of thing done enough so good on ya for punting and refactoring.

Cheers

Rob

# June 18, 2008 9:18 PM

Rob said:

Doh, this version does use the string and gets the ordinal.

Another version uses the ordinal for code gen'd stuff, sorry for the confusion.  You get the idea though.

# June 18, 2008 9:24 PM

DotNetKicks.com said:

You've been kicked (a good thing) - Trackback from DotNetKicks.com

# June 18, 2008 10:32 PM

T. Ferguson said:

There's someone still writing code like this?

Why?

How many years ago was NHibernate released?

# June 19, 2008 1:04 AM

skillet said:

T. Ferg --

Yeah, there are still applications that have been written without NHibernate -- and many good ones.  The question I'd ask is rather -- how about LLBLGen or ADO.NET Entity Framework vs. NHibernate...

Furthermore, when you think of the size/scale of the application in question -- development started in 2005 and is still underway (with ~1.5MM lines of code, between C# and PL/SQL, and not to say that metric is good or valid for anything, it just speaks to the shear volume of this bloody app.)

Rob --

Bravo on the Lambda, looks pretty sexy.

If you download the attached file, there is an overloaded method that includes the ordinal (by using GetName(int ordinal) and passing that to the other method.)

Cheers,

Scott

# June 19, 2008 1:50 AM

Paul said:

Second line of original code assumes that String.Empty will be used if reader returns DBNull type. Refactored code will return (string)null. Be careful :)

# June 19, 2008 6:36 AM

dmaq said:

Rob, can you put the full class code up on this comments page?

# June 19, 2008 10:30 AM

skillet said:

Paul --

Good catch.  My team actually has several more overloads on this method, including one that has an additional parameter of type T that allows you to specify the desired "default value".  In this case, you could call the code as follows:

theatre_code = reader.GetValueOrDefault<string>("theatre_code", String.Empty);

Cheers,

Scott

# June 19, 2008 10:34 AM

ryan sorensen » Blog Archive » IDataReader Extension Method said:

Pingback from  ryan sorensen &raquo; Blog Archive   &raquo;  IDataReader Extension Method

# July 25, 2008 4:27 PM

Lance said:

Great code, but doesn't like nullable types.  Looks like its Convert.ChangeType.

blog.pumacode.org/.../using-convert-changetype-on-nullable-types

# September 8, 2008 11:55 PM

chi flat iron said:

I was just searching for about this when I discovered your blog post. I’m only visiting to say that I truly enjoyed reading this post, it’s really well written. Are you going to blog more on this? It looks like there’s more material here for more posts.

# September 4, 2009 5:42 AM

1@qq.com said:

 Better to light one candle than to curse the darkness.

# September 16, 2009 10:19 PM

1@qq.com said:

 At twenty years of age, the will reigns; at thirty, the wit; and at forty, the judgment.

# September 26, 2009 5:23 AM

ed hardy clothes said:

At twenty years of age, the will reigns; at thirty, the wit; and at forty, the judgment.

# October 8, 2009 3:51 AM

wholesale lingerie said:

I have really enjoyed browsing your posts. Thanks for sharing this information. Someone on Yahoo Answers referred me here and I love it.

# October 13, 2009 6:25 AM

ugg sale said:

The Bailey Button UGG Boots from UGG Australia are a new addition this year (2009) to the lineup of classics for women. They have an interesting, unique look while still maintaining the basic traditional "classic" design that has become so well known in the fashion world.

# October 18, 2009 9:52 AM
Leave a Comment

(required) 

(required) 

(optional)

(required)