1

I have a DataSet populated during an ADO.NET call which I want to then use AutoMapper to convert to a DTO.

I have defined a generic-use mapper for many of our DTO types:

IMapper DataReaderMapper = new MapperConfiguration(cfg => {
    cfg.AddDataReaderMapping();
    cfg.CreateMap<IDataReader, MyApp>();
    cfg.CreateMap<IDataReader, MyRole>();
    cfg.CreateMap<IDataReader, MyBill>();
}).CreateMapper();

This works great when mapping for all of these DTO types:

var apps = DataReaderMapper.Map<IList<AppDTO>>(dataSet.Tables[0].CreateDataReader());

However, I have now added this mapping:

    cfg.CreateMap<IDataReader, Money>();

However, this Money type contains two float properties which appear to be giving AutoMapper some issues, with this exception:

Error mapping types.

Mapping types: IDataReader -> Money System.Data.IDataReader -> Common.Models.Money

Type Map configuration: IDataReader -> Money System.Data.IDataReader -> Common.Models.Money

Destination Member:

Amount

Which contains this InnerException:

Specified cast is not valid.

I have tried specifying a custom value mapping:

cfg.CreateMap<IDataReader, Money>().ForMember(x => x.Amount, opt => opt.MapFrom(rdr => Convert.ToDouble(rdr["Amount"])));

But this is not even changing the exception.

How can I tell AutoMapper that this Money type should have its float Amount property populated by converting from the SQL field float Amount?

Matt W
  • 11,753
  • 25
  • 118
  • 215

1 Answers1

0

Thanks to @lucian-bargaoanu for the link. This prompted me to take another look at the documentation which resulted in providing a resolver.

I have now fleshed out the mappings with some custom resolvers:

IMapper DataReaderMapper = new MapperConfiguration(cfg => {
    cfg.AddDataReaderMapping();
    cfg.CreateMap<IDataReader, MyApp>();
    cfg.CreateMap<IDataReader, MyRole>();
    cfg.CreateMap<IDataReader, MyBill>();
    cfg.CreateMap<IDataReader, Money>()
        .ForMember(dest => dest.Amount, opt => opt.MapFrom<MoneyAmountResolver>())
        .ForMember(dest => dest.SubTotal, opt => opt.MapFrom<MoneySubTotalResolver>());
}).CreateMapper();

The resolvers are just small classes:

public class MoneyAmountResolver: IValueResolver<IDataReader, Money, float>
{
    public float Resolve(IDataReader source, Moneydestination, float member, ResolutionContext context)
    {
        return (float)Convert.ToDouble(source["Amount"]);
    }
}

public class MoneySubTotalResolver: IValueResolver<IDataReader, Money, float>
{
    public float Resolve(IDataReader source, Moneydestination, float member, ResolutionContext context)
    {
        return (float)Convert.ToDouble(source["SubTotal"]);
    }
}
Matt W
  • 11,753
  • 25
  • 118
  • 215