DataRow adaptation to IDataRecord

I’ve been wanting to write generic functions for a while to handle reads from data readers and tables.  The problem is that DbDataReader and DataTable don’t have much commonality.  Then I ran across Chris McKenzie’s post, where he creates an adapter for a DataRow to the IDataRecord interface.  I’ve converted Chris’ class to C# and created a full implementation:

public class DataRowAdapter : IDataRecord
{
    #region Members
    private DataRow _Row;
    #endregion

    #region Properties
    public DataRow Row
    {
        get { return _Row; }
    }
    #endregion

    #region Constructors
    public DataRowAdapter(DataRow row)
    {
        _Row = row;
    }
    #endregion

    #region IDataRecord Implementation
    public object this[string name]
    {
        get { return _Row[name]; }
    }

    public object this[int i]
    {
        get { return _Row[i]; }
    }

    public int FieldCount
    {
        get { return _Row.Table.Columns.Count; }
    }

    public bool GetBoolean(int i)
    {
        return Convert.ToBoolean(_Row[i]);
    }

    public byte GetByte(int i)
    {
        return Convert.ToByte(_Row[i]);
    }

    public long GetBytes(int i, long fieldOffset, byte[] buffer, int bufferoffset, int length)
    {
        throw new NotSupportedException("GetBytes is not supported.");
    }

    public char GetChar(int i)
    {
        return Convert.ToChar(_Row[i]);
    }

    public long GetChars(int i, long fieldoffset, char[] buffer, int bufferoffset, int length)
    {
        throw new NotSupportedException("GetChars is not supported.");
    }

    public IDataReader GetData(int i)
    {
        throw new NotSupportedException("GetData is not supported.");
    }

    public string GetDataTypeName(int i)
    {
        return _Row[i].GetType().Name;
    }

    public DateTime GetDateTime(int i)
    {
        return Convert.ToDateTime(_Row[i]);
    }

    public decimal GetDecimal(int i)
    {
        return Convert.ToDecimal(_Row[i]);
    }

    public double GetDouble(int i)
    {
        return Convert.ToDouble(_Row[i]);
    }

    public Type GetFieldType(int i)
    {
        return _Row[i].GetType();
    }

    public float GetFloat(int i)
    {
        return Convert.ToSingle(_Row[i]);
    }

    public Guid GetGuid(int i)
    {
        return (Guid)_Row[i];
    }

    public short GetInt16(int i)
    {
        return Convert.ToInt16(_Row[i]);
    }

    public int GetInt32(int i)
    {
        return Convert.ToInt32(_Row[i]);
    }

    public long GetInt64(int i)
    {
        return Convert.ToInt64(_Row[i]);
    }

    public string GetName(int i)
    {
        return _Row.Table.Columns[i].ColumnName;
    }

    public int GetOrdinal(string name)
    {
        return _Row.Table.Columns.IndexOf(name);
    }

    public string GetString(int i)
    {
        return _Row[i].ToString();
    }

    public object GetValue(int i)
    {
        return _Row[i];
    }

    public int GetValues(object[] values)
    {
        values = _Row.ItemArray;
        return _Row.ItemArray.GetLength(0);
    }

    public bool IsDBNull(int i)
    {
        return Convert.IsDBNull(_Row[i]);
    }
    #endregion
}

Implementing GetChars() and GetBytes() seems to be a little overboard, so I haven’t done those.  Now I have a generic GetDataRecordBool function:

public static bool GetDataBool(IDataRecord data, string columnName, bool defaultValue)
{
    bool ReturnValue = defaultValue;
    int FieldValueAsInteger;
    int ColumnOrdinal;
    ColumnOrdinal = data.GetOrdinal(columnName);
    if (typeof(bool) == data.GetFieldType(ColumnOrdinal))
    {
        ReturnValue = data.GetBoolean(ColumnOrdinal);
    }
    else
    {
        FieldValueAsInteger = GetDataInt(data, columnName, defaultValue ? 1 : 0);
        ReturnValue = IntToBool(FieldValueAsInteger);
    }
    return ReturnValue;
}

where I can control how it handles different data types, that is easily implementable for both a DbDataReader:

public static bool GetReaderBool(DbDataReader reader, string columnName, bool defaultValue)
{
    return GetDataBool(reader, columnName, defaultValue);
}

and a DataTable:

public static bool GetTableBool(DataTable table, int row, string columnName)
{
    return GetDataBool(new DataRowAdapter(table.Rows[row]), columnName);
}

That’s nice because several of my data tool libraries have custom handling for the type.  For example, my GetDataString class formats the return value as currency for money types, short date for datetime types without a time, longdate for datetime types without time, etc.  Thanks Chris!

Advertisements

5 thoughts on “DataRow adaptation to IDataRecord

  1. There’s a bug in this code.

    In GetValues you have to copy the values into the array that is passed in, assigning it to another array is not going to work. The values parameter would need to be passed in using ref.

  2. I haven’t tested it.

    But this should do it.

    ///
    /// Gets all the attribute fields in the collection for the current record.
    ///
    /// An array of Object to copy the attribute fields into.
    /// The number of instances of Object in the array.
    public int GetValues(object[] values)
    {
    int fieldCount = FieldCount;
    int i;

    for(i=0; i < values.Length && i < fieldCount; ++i)
    values[0] = mRow[0];

    return i – 1;
    }

  3. Hakeem, DataTable (and therefore DataRow) is independent of where the data came from–you could use the database of your choice or even construct the table in memory. The IDataReader interface generalizes functionality across SqlDataReader, OleDbDataReader, etc. so you should be able to use whatever datasource you need!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s