1

I am getting the following exception when I try to call Mapper.Map in my application:

AutoMapper.AutoMapperMappingException. 

Mapping types:
Double -> Double
System.Double -> System.Double

Destination path:
Address.Latitude

Source value:
42.250287

   System.InvalidCastException. Unable to cast object of type 'Acme.Order' to type 'Acme.Address'.

Aside from the obvious frustration with an error indicating that it can't map objects of the same type (and primitive types at that), the fact is that I NEVER map from type "Acme.Order" to type "Acme.Address"!

Here is the actual call being used:

var order = Mapper.Map<IDataRecord, Order>(theDataReader);

My object model looks like this:

public class Order
{
    public Address Address { get; set; }
    public Int32 Number { get; set; }
    public PhoneNumber PhoneNumber { get; set; }
}

public class Address
{
    public String City { get; set; }
    public Double Latitude { get; set; }
    public Double Longitude { get; set; }
    public Int32 Number { get; set; }
    public String PostalCode { get; set; }
    public String State { get; set; }
    public String Street { get; set; }
}

public class PhoneNumber
{
    public String Extension { get; set; }
    public String Number { get; set; }
}

And I have the following configuration defined in my application:

CreateMap<IDataRecord, Address>()
.ForMember(target => target.Latitude, opt => opt.MapFrom(record => GetDoubleFrom(record, "Latitude", 0)))
.ForMember(target => target.Longitude, opt => opt.MapFrom(record => GetDoubleFrom(record, "Longitude", 0)))
;
CreateMap<IDataRecord, PhoneNumber>()
.ForMember(target => target.Extension, opt => opt.MapFrom(record => GetStringFrom(record, "Extension", String.Empty)))
.ForMember(target => target.Number, opt => opt.MapFrom(record => GetStringFrom(record, "PhoneNumber", String.Empty)))
;
CreateMap<IDataRecord, Order>()
.ForMember(target => target.Address, opt => opt.ResolveUsing(record => Mapper.Map<IDataRecord, Address>(record)))
.ForMember(target => target.PhoneNumber, opt => opt.ResolveUsing(record => Mapper.Map<IDataRecord, PhoneNumber>(record)))
;

With these helper methods:

private Double GetDoubleFrom(IDataRecord record, String name, Double defaultValue)
{
    return (record.Contains(name) && !record.IsDbNull(name)) ? record.GetDouble(name) : defaultValue;
}

private String GetStringFrom(IDataRecord record, String name, String defaultValue)
{
    return (record.Contains(name) && !record.IsDbNull(name)) ? record.GetString(name) : defaultValue;
}

(I have extension methods on IDataRecord that take the field name, get the ordinal and return the value using the standard methods but won't show them for brevity.)

Does any of this shed any light on what is causing the failure?

(Btw, I am using AutoMapper v2.1.267)

SonOfPirate
  • 5,642
  • 3
  • 41
  • 97

1 Answers1

1

I can't recreate this directly as it is missing some information (such as the concrete class for the IDataRecord interface, the content of theDataReader, etc). However the first check would be to ensure your mapping is correct. Do you test it with AssertConfigurationIsValid?

For example, if you have the following AutoMapper profile:

public class MyProfile : Profile
{
    public override string ProfileName
    {
        get
        {
            return "MyProfile";
        }
    }

    protected override void Configure()
    {
        CreateMap<IDataRecord, Address>()
            .ForMember(
                target => target.Latitude,
                opt => opt.MapFrom(
                    record => GetDoubleFrom(record, "Latitude", 0)))
            .ForMember(
                target => target.Longitude,
                opt => opt.MapFrom(
                    record => GetDoubleFrom(record, "Longitude", 0)));
        CreateMap<IDataRecord, PhoneNumber>()
            .ForMember(
                target => target.Extension,
                opt => opt.MapFrom(
                    record => GetStringFrom(record, "Extension", string.Empty)))
            .ForMember(
                target => target.Number,
                opt => opt.MapFrom(
                    record => GetStringFrom(record, "PhoneNumber", string.Empty)));
        CreateMap<IDataRecord, Order>()
            .ForMember(
                target => target.Address,
                opt => opt.ResolveUsing(
                    record => Mapper.Map<IDataRecord, Address>(record)))
            .ForMember(
                target => target.PhoneNumber,
                opt => opt.ResolveUsing(
                    record => Mapper.Map<IDataRecord, PhoneNumber>(record)));
    }
}

Will this Unit Test pass?

[Test]
public void AutoMapper_Configuration_IsValid()
{
    Mapper.Initialize(m => m.AddProfile<MyProfile>());
    Mapper.AssertConfigurationIsValid();
}

UPDATE

Can you try

CreateMap<IDataRecord, Order>()
    .ForMember(
        target => target.Address,
        opt => opt.MapFrom(record => Mapper.Map<IDataRecord, Address>(record)))
    .ForMember(
        target => target.PhoneNumber,
        opt => opt.MapFrom(record => Mapper.Map<IDataRecord, PhoneNumber>(record)));
Mightymuke
  • 5,094
  • 2
  • 31
  • 42
  • IDataRecord is part of ADO.NET and is implemented by all DataReader classes. – SonOfPirate Nov 28 '12 at 23:38
  • Yes, we use AssertConfigurationIsValid at runtime as well as in our unit tests and it passes consistently. – SonOfPirate Nov 28 '12 at 23:39
  • Sorry, yeah, I know what `IDataRecord` is, was just implying that I don't know how to stub it for this particular scenario. – Mightymuke Nov 28 '12 at 23:43
  • Is certainly a weird error. Without being able to recreate it locally, I can only suggest to either try upgrading to the latest version of `AutoMapper` (2.2.0), and / or posting on the `AutoMapper` [mailing list](https://groups.google.com/forum/?fromgroups#!forum/automapper-users). – Mightymuke Nov 28 '12 at 23:48
  • Any suggestions how else I might be able to troubleshoot what is happening? I mean, Mapper.Map is a black box. I've looked through the source code but nothing gives me any idea what it's trying to do internally that results in such a screwy error message. – SonOfPirate Nov 29 '12 at 00:08
  • Instead of `ResolveUsing` in the configuration, can you try `MapFrom`? I have an example that I'll try and dig out, but I suspect you can test that quicker than I can find it. – Mightymuke Nov 29 '12 at 00:16
  • Unbelievable. As of right now it appears to be working perfectly. – SonOfPirate Nov 29 '12 at 19:37
  • With or without changes? Thx. – Mightymuke Nov 29 '12 at 20:17
  • Sorry, after changing it to use MapFrom instead of ResolveUsing. – SonOfPirate Nov 30 '12 at 00:21
  • OK, awesome. Glad I could help. – Mightymuke Nov 30 '12 at 00:29