OracleDataReader GetOrDefault() extension method

The various GetXXX methods of the OracleDataReader class take the zero-based column index instead of the column name. Referring columns by index instead of name leads to hard to understand code. Additionally, the code may be susceptible to errors if the column order is modified.

We might write something like this if the columns returned from a database procedure are emp_name, hire_date and salary in that order,

DateTime hireDate;
double salary;

var empName = dr.GetString(0);

// check for IsDBNull before getting the column data
if (!dr.IsDBNull(1))
{
    hireDate = dr.GetDateTime(1);
}

if (!dr.IsDBNull(2))
{
    salary = dr.GetDouble(2);
}

With the OracleDataReader extension method, the code is significantly reduced.

// default to "Unknown" if database value is NULL
var empName = dr.GetOrDefault<string>("emp_name", string.Empty);

// default to DateTime.MinValue if database value is NULL
var hireDate = dr.GetOrDefault<DateTime>("hire_date");

// default to 1000 if database value is NULL
var salary = dr.GetOrDefault<double>("salary", 1000);

The second parameter is returned if the first parameter is null or DBNull.value. I created the GetOrDefault as a static method of the Helper class for reusability.

namespace App.Helpers
{
    public class Helper
    {
        public static T GetOrDefault<T>(
            object value,
            T defaultValue = default(T))
        {
            // do not use GetType()
            // IsNullableType requires typeof to work correctly
            Type type = typeof(T);

            if (value == DBNull.Value 
               || value == null
               || string.IsNullOrEmpty(Convert.ToString(value)))
            {
                return defaultValue;
            }

            // if type is nullable then get the underlying type
            if (IsNullableType(type))
            {
                type = Nullable.GetUnderlyingType(typeof(T));
            }

            return (T)Convert.ChangeType(value, type);
        }

        /// <summary>
        /// Refer http://msdn.microsoft.com/en-us/library/ms366789.aspx
        /// Use the following code to determine whether a Type object
        /// represents a Nullable type. 
        /// Remember that this code always returns false if the Type object 
        /// was returned from a call to GetType.
        /// </summary>
        public static bool IsNullableType(Type type)
        {
            return type.IsGenericType 
                   && type.GetGenericTypeDefinition()
                          .Equals(typeof(Nullable<>));
        }
    }
}

Here is the extension method on the OracleDataReader class that calls the helper method.

namespace App.Helpers
{
    public static class Extensions
    {
        public static T GetOrDefault<T>(
             this OracleDataReader dr,
             string fieldName,
             T defaultValue = default(T))
        {
            try
            {
                return Helper.GetValue<T>(dr[fieldName], defaultValue);
            }
            catch (IndexOutOfRangeException ex)
            {
                throw new IndexOutOfRangeException(string.Format("Unable to find column '{0}' in the result set", fieldName), ex);
            }
        }
    }
}