AutoMapperMappingException Типы сопоставления: Double -> Double

Я получаю следующее исключение, когда пытаюсь вызвать Mapper.Map в своем приложении:

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'.

Помимо очевидного разочарования в связи с ошибкой, указывающей, что он не может отображать объекты одного и того же типа (и при этом примитивные типы), факт в том, что я НИКОГДА не сопоставляю тип «Acme.Order» с типом «Acme.Address»!

Вот фактический используемый вызов:

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

Моя объектная модель выглядит так:

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; }
}

И у меня есть следующая конфигурация, определенная в моем приложении:

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)))
;

С помощью этих вспомогательных методов:

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;
}

(У меня есть методы расширения в IDataRecord, которые принимают имя поля, получают порядковый номер и возвращают значение, используя стандартные методы, но не отображают их для краткости.)

Что-то из этого проливает свет на то, что вызывает сбой?

(Кстати, я использую AutoMapper v2.1.267)


person SonOfPirate    schedule 28.11.2012    source источник


Ответы (1)


Я не могу воссоздать это напрямую, так как отсутствует некоторая информация (например, конкретный класс для интерфейса IDataRecord, содержимое theDataReader и т. д.). Однако первая проверка должна заключаться в том, чтобы убедиться, что ваше сопоставление правильное. Вы тестируете его с AssertConfigurationIsValid?

Например, если у вас есть следующий профиль AutoMapper:

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)));
    }
}

Пройдёт ли этот модульный тест?

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

ОБНОВЛЕНИЕ

Ты можешь попробовать

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)));
person Mightymuke    schedule 28.11.2012
comment
IDataRecord является частью ADO.NET и реализуется всеми классами DataReader. - person SonOfPirate; 29.11.2012
comment
Да, мы используем AssertConfigurationIsValid во время выполнения, а также в наших модульных тестах, и он проходит последовательно. - person SonOfPirate; 29.11.2012
comment
Извините, да, я знаю, что такое IDataRecord, просто имел в виду, что я не знаю, как заглушить его для этого конкретного сценария. - person Mightymuke; 29.11.2012
comment
Это, конечно, странная ошибка. Не имея возможности воссоздать его локально, я могу только предложить либо попробовать обновиться до последней версии AutoMapper (2.2.0), и/или опубликовать на AutoMapper список рассылки. - person Mightymuke; 29.11.2012
comment
Любые предложения, как еще я мог бы устранить неполадки, что происходит? Я имею в виду, что Mapper.Map — это черный ящик. Я просмотрел исходный код, но ничто не дает мне никакого представления о том, что он пытается сделать внутри, что приводит к такому странному сообщению об ошибке. - person SonOfPirate; 29.11.2012
comment
Вместо ResolveUsing в конфигурации можно попробовать MapFrom? У меня есть пример, который я попытаюсь раскопать, но я подозреваю, что вы сможете проверить его быстрее, чем я его найду. - person Mightymuke; 29.11.2012
comment
Невероятный. На данный момент он работает отлично. - person SonOfPirate; 29.11.2012
comment
С изменениями или без? Спасибо. - person Mightymuke; 30.11.2012
comment
Извините, после изменения для использования MapFrom вместо ResolveUsing. - person SonOfPirate; 30.11.2012
comment
ОК, круто. Рад, что смог помочь. - person Mightymuke; 30.11.2012