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
?
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"]);
}
}