Невозможно взломать карту AutoMapper, если TSource имеет значение Nullable <DateTime> - PullRequest
0 голосов
/ 11 октября 2018

У меня есть карта AutoMapper, которая определена следующим образом

CreateMap<DateTime?, DateString>()
    .ForMember(dest => dest.Value, opt => opt.ResolveUsing(src => src.HasValue ? src.Value.ToString("yyyyMMdd") : String.Empty));

DateString определена как таковая

public class DateString
{
    public String Value { get; set; }
}

Свойство Value является String, которое представляет собойдата в формате yyyymmdd, чтобы приспособиться к этому ожидаемому формату, я конвертирую мои даты, представленные в виде DateTime типов, в String в этом формате.

Карта выше вызвана другой картой, которая определенавот так

CreateMap<DateTimeDetails, PickupDetails>()
    .ForMember(dest => dest.time, opt => opt.MapFrom(src => src));

Классы DateTimeDetails и PickupDetails определены как

public class DateTimeDetails
{
    public Nullable<DateTime> Date { get; set; }
    public String Time { get; set; }
    public Int32 TimeZoneOffset { get; set; }
}

public class PickupDetails
{
    public TimeString time { get; set; }
    public DateString date { get; set; }
}

Итак, общий поток таков:

Когда DateTimeDetailsпреобразованные в PickupDetails, свойства PickupDetails.date и DateTimeDetails.Date автоматически сопоставляются без использования .ForMember() и .MapFrom() и вызывают карту DateTime? -> DateString.

Где свойство DateString.Value установлено на yyyymmdd или String.Empty в зависимости от того, имеет значение исходный объект Nullable<DateTime> или нет.

Проблема, с которой я столкнулся,что если я сделаю TSource для моей DateTime? -> DateString карты, обнуляемой либо с Nullable<DateTime>, либо с DateTime?, AutoMapper не распознает подпись <TSource, TDestination> и не сопоставит свойство PickupDetails.date.Она выдаст ошибку

Error mapping types.

Mapping types:
DateTimeDetails -> PickupDetails

Type Map configuration:
DateTimeDetails -> PickupDetails

Property:
date

Если я сделаю свою карту более явной, например,

CreateMap<DateTime?, DateString>()
    .ForMember(dest => dest.Value, opt => opt.ResolveUsing(src =>
    {
        if (src.HasValue)
        {
            return src.Value.ToString("yyyyMMdd");
        }
        else
        {
            return String.Empty;
        }
    }));

И попытаюсь проникнуть в логику распознавателя, я не достигну ни одной установленной точки останова.где-нибудь внутри распознавателя.

Единственный способ заставить его работать, это если я поменяю TSource на ненулевой тип DateTime и внедрю проверку != null вместо проверки Nullable<DateTime>.HasValueв троичном выражении для возврата вот так

CreateMap<DateTime, DateString>()
    .ForMember(dest => dest.Value, opt => opt.ResolveUsing(src => src != null ? src.ToString("yyyyMMdd") : String.Empty));

В этом формате AutoMapper распознает подпись <TSource, TDestination> и автоматически сопоставляет PickupDetails.date с картой DateTimeDetails -> PickupDetails.

Iне могу понять, где я ошибаюсь и почему TSource для моей карты DateTime? -> DateString не может быть Nullable<DateTime> или DateTime? и должно быть ненулевым DateTime

Здесь является репо для проблемы.Этот онлайн-компилятор не может запускать AutoMapper V7.0.1, поэтому V6.2.2 - это то, что используется, и поведение с 6.2.2 по-прежнему некорректно, но не совпадает с тем, что я испытываю в 7.0.1, который я объяснил вышев этом посте.

** РЕДАКТИРОВАТЬ 1: **

С помощью Люциана Баргаоану я узнал больше о том, что происходит с этим отображением.Ниже приведены планы выполнения для той же карты с тем же оператором .ForMember(), единственное, что изменилось, это TSource

AutoMapper Execution Plans

ДляНа карте, использующей Nullable<DateTime> TSource, выполняется блок if-else, который проверяет свойство $src.Если null, он возвращает не экземпляр new класса TDestination, а только указатель на него, в результате чего свойство destObj.date не устанавливается равным пустому экземпляру его типа, в котором он будет содержать .Value свойство полного пути доступа destObj.date.Value, destObj.date просто установлено в ноль.

В чем я не уверен, так это в поведении платформы .Net Core, которое вставляет эту проверку if($src == null)если лямбда работает с переменной Nullable<T> или AutoMapper добавляет ее.Потому что, как вы можете видеть ниже для плана выполнения с правой стороны изображения выше, я пытаюсь обработать сценарий $src == null самостоятельно.Но на данный момент я никогда не доберусь туда.

Я также не уверен, что ролл переменная $typeMapDestination воспроизводит во всем AutoMapper, но сейчас я получаю ошибку Error mapping types. для моей карты CreateMap<Nullable<DateTime>, DateString>().

Я думал, что это может быть из-за того, что блок if($src == null) останавливает манипулирование / возврат переменной $typeMapDestination, и поэтому свойство назначения, обозначенное в .ForMemeber(), на самом деле никогда не "отображается", поэтому ошибка

Еще одно поведение, на которое стоит обратить внимание, это ненулевое DateTime -> DateString отображение, если ему отправляется нулевое значение, несмотря на следующий блок кода

$resolvedValue = .Try {
    .If($src != null) {
        .Call $src.ToString("yyyyMMdd")
    } .Else {
        System.String.Empty
    }
} .Catch(System.NullReferenceException) {
    .Default(System.String)
} .Catch(System.ArgumentNullException) {
    .Default(System.String)
};

что-то вызываеткоторые пытаются потерпеть неудачу и перейти к одному из двух блоков catch ниже.Часть else { System.String.Empty } блока if / else не сработает.И destObj.date будет равен нулю вместо нового экземпляра DateString со свойством .Value "".

...