Fast (dynamic) DataTable load

When you stuff data in a DataTable you need to first build the columns with, then add the rows containing all of the field values. To map properties from strongly typed objects to DataTables involves either tedious error prone mapping code or a more versatile and CPU hungry way using Reflection.

The small code snippet below is a combination of these, using reflection just once then a fast mapping code from then on based on Gerhard Stephan's code emitting method. It is used with a DataTable like this: dt.Columns.AddRange(TableMapper.Columns); : dt.LoadDataRow(TableMapper.GetPropertyValues(item), true);
Now for the actual code. Have fun!

static class TableMapper<T>  
{
    readonly static Func<object, object>[] m_getters = CreateGetters();
    readonly static DataColumn[] m_columns = CreateColumns();

    public static DataColumn[] Columns
    {
        get { return m_columns; }
    }

    public static object[] GetPropertyValues(T obj)
    {
        if (obj == null)
            throw new ArgumentNullException("obj");
        var values = m_getters.Select(getter => getter(obj))
                              .ToArray();
        return values;
    }

    static Func<object, object>[] CreateGetters()
    {
        var getters = typeof(T).GetProperties()
                               .Select(CreateGetMethod)
                               .ToArray();
        return getters;
    }

    static Func<object, object> CreateGetMethod(PropertyInfo propertyInfo)
    {
        MethodInfo getMethod = propertyInfo.GetGetMethod();
        if (getMethod == null)
            return null;
        var arguments = new System.Type[1];
        arguments[0] = typeof(object);
        DynamicMethod getter = new DynamicMethod(
            String.Concat("_Get", propertyInfo.Name, "_"),
            typeof(object), arguments, propertyInfo.DeclaringType);
        ILGenerator generator = getter.GetILGenerator();
        generator.DeclareLocal(typeof(object));
        generator.Emit(OpCodes.Ldarg_0);
        generator.Emit(OpCodes.Castclass, propertyInfo.DeclaringType);
        generator.EmitCall(OpCodes.Callvirt, getMethod, null);
        if (!propertyInfo.PropertyType.IsClass)
            generator.Emit(OpCodes.Box, propertyInfo.PropertyType);
        generator.Emit(OpCodes.Ret);
        return (Func<object, object>)
            getter.CreateDelegate(typeof(Func<object, object>));
    }

    static DataColumn[] CreateColumns()
    {
        var columns = typeof(T)
            .GetProperties()
            .Select(p => new DataColumn(p.Name, p.PropertyType))
            .ToArray();
        return columns;
    }
}
Google
m@kli.dk @klinkby RSS feed  GitHub