Mapping From IDataReader/IDataRecord with AutoMapper

October 16th, 2009

A while ago, I submitted a patch to AutoMapper that added basic support for mapping data from an IDataReader/IDataRecord to an object. For those of us who don’t have the luxury to use NHibernate in their projects, this feature can save you from writing lots of repetitive and tedious code.

Its usage is pretty much the same as with regular object-to-object mapping using AutoMapper. Lets show a very simple example.

Suppose we have a view object like the one shown below:

public class SomeView
{
    public Int32 SomeNumber { get; private set; }
    public Guid AnId { get; private set; }
    public Double OrNothing { get; private set; }
}

Now when we can execute a query like this,

SELECT ColumnA AS SomeNumber,
       ColumnB AS AnId,
       ColumnC AS OrNothing
FROM SomeTable
WHERE ...

and read the results using a data reader. Now we can use AutoMapper to map the results to instances of our view class:

var dataReader = ... // Execute a data reader
var views = Mapper.Map<IDataReader, IEnumerable<SomeView>>(_dataReader);

This results in a collection of one of or more view objects. When our query is guaranteed to always return one record, we can use the following syntax:

var dataRecord = ... // Execute data reader and read first record
var = Mapper.Map<IDataRecord, SomeView>(_dataRecord);

This approach expects that a convention is followed whereby the name of a field returned by the query matches the name of a property on the target class. Its also possible to use projection as already provided for regular object-to-object mapping.

Suppose we add a new property to our view,

public class SomeView
{
    ...
    public DateTime SomeDate { get; private set; }
}

and we modify the query so that we retrieve the corresponding date value from the database:

SELECT ...
       ColumnD AS BirthDay
FROM SomeTable

Notice that we’ve broken the convention here and we need to use projection to ensure that the retrieved date value is mapped to the correct property.

Mapper.CreateMap<IDataRecord, SomeView>()
    .ForMember(dest => dest.SomeDateAndTime, 
               options => options.MapFrom(
               src => src.GetDateTime(src.GetOrdinal("BirthDay"))));

var dataRecord = ...    // Execute data reader and read first record
var = Mapper.Map<IDataRecord, SomeView>(_dataRecord);

Using projection we’re able to manually map from a data reader or data record. In some sense,  we’re back to square one if we have to do this for all fields. Trying to follow the convention is of course the most useful. 

I know it’s not much, but I think it can be helpful for those cases where you actually need to map from a data reader or a data record to an object.

  • http://peter.worksontheweb.net Peter Eysermans

    Allright, great contribution. This will save me a bunch of mapping classes. It’s small things like these that make your life so much better if you can’t use NHibernate.

  • http://www.silvercrux.com/blog/ PM-SilverCrux

    Excellent Addition. I have a question, perhaps this a wrong place to ask but let me try anyway : Can it be tweaked to run under Compact Framework 3.5 ?

  • http://elegantcode.com Jan Van Ryswyck

    @PM-SilverCrux If AutoMapper can run with the Compact Framework, then yes. I guess you can ask on the user group .

  • Pingback: Reflective Perspective - Chris Alcock » The Morning Brew #458

  • ae

    Please, any helper for Ent.Library Data Block 4.1, for reduce code lines and write elegant code ?? any suggestions ??

    Database db = DatabaseFactory.CreateDatabase(“ConnectionStrings.Oracle.D04PES01″);
    using (DbCommand cm = db.GetStoredProcCommand(“TBL_POC_TEST_TIPOS.TBL_POC_TEST_TIPOS_FBY_PK”))
    {
    db.AddInParameter(cm, “P_ID_TEST_TIPOS”, DbType.String, id);
    cm.Parameters.Add(CreateCursorParameter(“P_REFCURSOR”));

    // Using “using” will cause both the DataReader and connection to be
    // closed. (ExecuteReader will close the connection when the
    // DataReader is closed.)
    using (IDataReader dataReader = db.ExecuteReader(cm))
    {
    if (dataReader.Read())
    {
    factory.Add(dataReader);
    }
    //while (dataReader.Read())
    //{
    // factory.Add(dataReader);
    // break;
    //}
    return factory.Entity;
    }
    }
    thanks in advance…

  • andrex

    Many years I use a great bltoolkit library for mapping and dataaccess:

    public class SomeView
    {
    public Int32 SomeNumber { get; private set; }
    public Guid AnId { get; private set; }
    public Double OrNothing { get; private set; }
    }

    // for getting list entities
    using(var db = new DbManager())
    var list = db.SetCommand(SqlQueryHere).ExecuteList();

  • David

    Could you do it with List instead of IEnumerable like so?

    var views = Mapper.Map<IDataReader, List>(_dataReader);

  • David

    When I map IDataReader to IEnumerable, it fails with null reference exception in DataReaderMapper.cs at line 106 inside the following methid:

    private static void MapPropertyValues(ResolutionContext context, IMappingEngineRunner mapper, object result)
    {
    // this is where it is failing… right at the first line
    foreach (var propertyMap in context.TypeMap.GetPropertyMaps())
    {
    MapPropertyValue(context, mapper, result, propertyMap);
    }
    }

  • Emdadgar2

    Great!!! Very good class for using in small projects!!!
    Thanks