Data-access quicky – A micro mapper

Someone (Yes you @lgrave) started taunting me on twitter. He said that he could do data access in only 50 lines using reflection. This gave me an idea. How small can you go with a mapper?

Here’s one, done in only 32 lines of code including some silly comments from me.

public class Mapper<T>
{
    private static Dictionary<Type, Delegate> _typeMappings =
        new Dictionary<Type, Delegate>();

    public Mapper()
    {
        if(!_typeMappings.ContainsKey(typeof(T)))
            InitializeTypeMapping();
    }

    public IEnumerable<T> Load(IDataReader reader)
    {
        while(reader.Read())
            yield return (T)_typeMappings[typeof (T)].DynamicInvoke(reader);
    }

    private void InitializeTypeMapping()
    {
        var recordParameter = Expression.Parameter(typeof(IDataRecord), "record");

        // Trust me, you don't want to know ;-) 
        var lambdaExpression = Expression.Lambda(Expression.MemberInit(Expression.New(typeof(T)),
            typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Select(
                property => Expression.Bind((MemberInfo) property, Expression.Convert(
                    Expression.Call(recordParameter, "GetValue", null,
                    Expression.Call(recordParameter, "GetOrdinal", null,
                    Expression.Constant(property.Name))),
                    property.PropertyType)))), recordParameter);

        _typeMappings[typeof (T)] = lambdaExpression.Compile();
    }
}

To use the mapper, all you need to do is setup a connection with a database and fire away. It takes any POCO. Just make sure you include all the properties of the object in the resultset, otherwise it will break… and pretty bad too.

using(SqlConnection connection = new SqlConnection(
    @"Data Source=.SQLEXPRESS; Initial Catalog=EfSampleApp.AddressBookContext;Integrated Security=True"))
{
    SqlCommand cmd = new SqlCommand("SELECT * FROM People",connection);

    connection.Open();

    using(var reader = cmd.ExecuteReader())
    {
        var people = new Mapper<Person>().Load(reader);
    }
}

The mapper works by generating code that’s nothing more than a type initializer that reads from the reader object that you passed into the mapper earlier. Pretty neat Glimlach. Check out the next code snippet to get an idea of what the gibberish in the first code sample ends up looking like, when you run the application:

new Person() {
    FirstName = (string)record.GetValue(record.GetOrdinal("FirstName")),
    LastName = (string)record.GetValue(record.GetOrdinal("LastName")),
    Id = (int)record.GetValue(record.GetOrdinal("Id")),
};

Cheers!