views:

49

answers:

1

Psuedo code for mapping configuration (as below) is not possible since the lambda only lets us access Type IDataReader, wheras when actually mapping, AutoMapper will reach into each "cell" of each IDataRecord while IDataReader.Read() == true:

var mappingConfig = Mapper.CreateMap<IDataReader, IEnumerable<MyDTO>>();
mappingConfig.ForMember(
    destination => destination.???,
    options => options.MapFrom(source => source.???));

Can anyone think of a way to do this using AutoMapper configuration at runtime or just some other dynamic approach that meets the requirement below.

The requirement is to support any incoming IDataReader which may have column names that don't match the property names of MyDTO and there is no naming convention I can rely on. Instead we'll ask the user at runtime to cross-reference the expected column names with the actual column names found in the IDataReader via IDataReader.GetSchemaTable().

+2  A: 

I don't know about automapper but I'm mapping datareader to objects using the ValueInjecter like this:

 while (dr.Read())
 {
    var o = new User();
    o.InjectFrom<DataReaderInjection>(dr);
    return o;
 }

and the DataReaderInjection (something like ValueResolver for Automapper)

public class DataReaderInjection : KnownSourceValueInjection<IDataReader>
    {
        protected override void Inject(IDataReader source, object target, PropertyDescriptorCollection targetProps)
        {
            for (var i = 0; i < source.FieldCount; i++)
            {
                var activeTarget = targetProps.GetByName(source.GetName(i), true);
                if (activeTarget == null) continue;

                var value = source.GetValue(i);
                if (value == DBNull.Value) continue;

                activeTarget.SetValue(target, value);
            }
        }
    }

you can use this to inject values from an IDataReader to any type of object


ok, so according to your requirements, I guess it should be like this:

public class DataReaderInjection : KnownSourceValueInjection<IDataReader>
        {
            protected override void Inject(IDataReader source, object target, PropertyDescriptorCollection targetProps)
            {
                var columns = source.GetSchemaTable().Columns;
                for (var i = 0; i < columns.Count; i++)
                {
                    var c = columns[i];

                    var targetPropName = c.ColumnName; //default is the same as columnName

                    if (c.ColumnName == "Foo") targetPropName = "TheTargetPropForFoo";
                    if (c.ColumnName == "Bar") targetPropName = "TheTargetPropForBar";
                    //you could also create a dictionary and use it here

                    var targetProp = targetProps.GetByName(targetPropName);
                    //go to next column if there is no such property in the target object
                    if (targetProp == null) continue;

                    targetProp.SetValue(target, columns[c.ColumnName]);
                }
            }
        }

here I used GetSchemaTable, just like you wanted :)


ok, if you want to pass some stuff to the injection, you can do it in many ways, here's how:

o.InjectFrom(new DataReaderInjection(stuff), dr);
//you need a constructor with parameters for the DataReaderInjection in this case

var ri = new DataReaderInjection();
ri.Stuff = stuff;
o.InjectFrom(ri, dr);
//you need to add a property in this case

here's a hint (for the constructor with parameters way)

  public class DataReaderInjection : KnownSourceValueInjection<IDataReader>
     {
        private IDictionary<string, string> stuff;
        public DataReaderInjection(IDictionary<string,string> stuff)
        {
          this.stuff = stuff;
        }
                    protected override void Inject(
...
Omu
+1 for a very interesting answer. However it doesn't (yet) meet the requirement as it still expects the 'column' name to match the property name. Thanks for the heads-up on ValueInjecter anyway.
rohancragg
@rohancragg you have all the property names from the object (targetProps) and you can get all the names from the datareader (source.GetName(theName)), so you can modify it to suit your needs
Omu
@rohancragg I've edited my question, I think this way should work (I didn't tried it, but I hope so)
Omu
Great, thanks. However, one last step is needed I think... How to pass a Dictionary as a parameter to DataReaderInjection.Inject()?
rohancragg
@rohancragg I've edited the question again ;), basically you just add a constructor with parameters to the injection, call it in a slightly different way
Omu