dotNetChris @ Marisic.Net

January 16, 2009

Working with DataTables/Datarows

Filed under: Programming — Tags: , , , , , , , — dotnetchris @ 6:21 pm

Sadly the project I am on at work is all circa 2005 standards where everything is DataTables and Data Adapters etc. These classes are an incredibly annoying “feature” to work with and are just brutal since it’s impossible to ever have a good domain design with them. But I wrote this class to make life easier to deal with them. It uses .net 3.5 extension methods but with a few minor changes it will run on .net 2.0. Some of the basis of this code for the ChangeType method came from QueryString Candy – Could Rot Your Teeth however I’ve added my own touches to it.

Here’s my extension method class:

///<summary>
/// Extension methods for manipulating DataRows
///</summary>
public static class DataRowUserExtensions
{
    /// <summary>
    /// Determines whether [is empty string] [the specified data row].
    /// </summary>
    /// <param name="dataRow">The data row.</param>
    /// <param name="key">The key.</param>
    /// <returns>
    ///   <c>true</c> if [is empty string] [the specified data row]; otherwise, including DBNull, <c>false</c>.
    /// </returns>
    public static bool IsEmptyString(this DataRow dataRow, string key)
    {
        if (dataRow.Table.Columns.Contains(key))
            return !dataRow.IsNull(key) && dataRow[key].ToString() == string.Empty;

        throw new ArgumentOutOfRangeException(key, dataRow, "does not contain column");
    }

    /// <summary>
    /// Determines whether the specified data row is null.
    /// </summary>
    /// <param name="dataRow">The data row.</param>
    /// <param name="key">The key.</param>
    /// <returns>
    ///   <c>true</c> if the specified data row is null; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsNull(this DataRow dataRow, string key)
    {
        if (dataRow.Table.Columns.Contains(key))
            return dataRow[key] == null || dataRow[key] == DBNull.Value;

        throw new ArgumentOutOfRangeException(key, dataRow, "does not contain column");
    }

    /// <summary>
    /// Determines whether [is null or empty string] [the specified data row].
    /// </summary>
    /// <param name="dataRow">The data row.</param>
    /// <param name="key">The key.</param>
    /// <returns>
    ///   <c>true</c> if [is null or empty string] [the specified data row]; otherwise, <c>false</c>.
    /// </returns>
    public static bool IsNullOrEmptyString(this DataRow dataRow, string key)
    {
        if (dataRow.Table.Columns.Contains(key))
            return dataRow.IsNull(key) && dataRow.IsEmptyString(key);

        throw new ArgumentOutOfRangeException(key, dataRow, "does not contain column");
    }

    /// <summary>
    /// Gets the specified data row.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="dataRow">The data row.</param>
    /// <param name="key">The key.</param>
    /// <returns></returns>
    public static T Get<T>(this DataRow dataRow, string key)
    {
        if (dataRow.Table.Columns.Contains(key))
        {
            // If we're trying to convert an empty string to string return the string instead
            // default(string) which is null, otherwise return null if dataRow IsNullOrEmptyString
            // else convert
            return typeof (T) == typeof (string) && dataRow.IsEmptyString(key)
                ? (T) dataRow[key] : (dataRow.IsNullOrEmptyString(key)
                ? default(T) : (T) ChangeTypeTo<T>(dataRow[key]));
        }

        throw new ArgumentOutOfRangeException(key, dataRow, "does not contain column");
    }

    /// <summary>
    /// Changes the type to.
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="value">The value.</param>
    /// <returns></returns>
    private static object ChangeTypeTo<T>(this object value)
    {
        if (value == null)
            return null;

        Type underlyingType = typeof (T);
        if (underlyingType == null)
            throw new ArgumentNullException("value");

        if (underlyingType.IsGenericType && underlyingType.GetGenericTypeDefinition().Equals(typeof (Nullable<>)))
        {
            var converter = new NullableConverter(underlyingType);
            underlyingType = converter.UnderlyingType;
        }

        // Guid convert
        if (underlyingType == typeof (Guid))
        {
            return new Guid(value.ToString());
        }

        // Check for straight conversion or value.ToString conversion
        var objType = value.GetType();

        // If this is false, lets hope value.ToString can convert otherwise exception
        bool objTypeAssignable2typeT = underlyingType.IsAssignableFrom(objType);

        // Do conversion
        return objTypeAssignable2typeT ? Convert.ChangeType(value, underlyingType)
                                                        : Convert.ChangeType(value.ToString(), underlyingType);
    }
}

Usage example:

ds.Tables[0].Rows[0].Get<DateTime?>("SomeDate")

Basically what is going first it checks to make sure that “SomeDate” is an actual column inside the DataRow, then it checks to make sure dr["SomeDate"] isn’t null, DBNull or an empty string.

Inside of the ChangeType method it will then resolve whether <T> is an object or a nullable object. If it’s a nullable object it will find the actual object type.

Next we do an outside check to see if it’s a Guid (since Convert.ChangeType can’t handle guids) and if it is to return the guid or it will bubble up an exception of underlyingType == typeof(Guid) and value is not a valid guid.

After that the actual conversion occurs first it checks if the type of value is Assignable To underlyingtype, if it is then it will do the Convert.ChangeType. If it’s not assignable we’re going to hope the string representation of value can be converted to type T otherwise it will fail. This second case is useful if you want to get the string representation of a number or date. As with the previous example:

ds.Tables[0].Rows[0].Get<string>("SomeDate")

Will return the string value of the SomeDate value instead of raising an exception if we didn’t have the second condition of the return statement there.

Hope this helps some of you out as much as it does me!

BloggingContext.ApplicationInstance.CompleteRequest();

The Shocking Blue Green Theme Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

Join 242 other followers

%d bloggers like this: