Класс сопоставления с абстрактным свойством к месту назначения - PullRequest
0 голосов
/ 07 августа 2020

У меня возникают некоторые проблемы с сопоставлением класса контейнера, содержащего абстрактное свойство, с классом назначения моей модели представления.

Сопоставление исходных классов

//Container class
public class GiftcardDetailResponse : Response
{
    //Instance of either UserGiftcardDTO or ImportedGiftcardDTO
    public GiftcardInstanceDTO UserGiftcard { get; set; }
}

public abstract class GiftcardInstanceDTO : BaseDTO
{
    public int UserId { get; set; }   
    public decimal Balance { get; set; } 
    public string BarcodeValue { get; set; }
    public string BarcodeUrl { get; set; }
    public string Code { get; set; }
    public string Pin { get; set; }
    public bool RefreshBalanceSupported { get; set; }
    public bool Viewed { get; set; }
    public bool IsArchived { get; set; }
    public virtual UserDTO User { get; set; }
}

public class UserGiftcardDTO : GiftcardInstanceDTO
{
    public int GiftcardId { get; set; }
    public DateTimeOffset? ActivatedAt { get; set; }
    public DateTimeOffset? BalanceUpdatedAt { get; set; }
    public string ClaimUrl { get; set; }
    public string ClaimSecret { get; set; }
    public PrivacySettings Privacy { get; set; }
    public bool IsPending { get; set; }
    public bool BoughtAsGift { get; set; }
    public virtual GiftcardDTO Giftcard { get; set; }
}

public class ImportedGiftcardDTO : GiftcardInstanceDTO
{
    public string RetailerName { get; set; }
    public string FrontImage { get; set; }
    public string BackImage { get; set; }
}

Сопоставление целевых классов

//Front-end view model
public class GiftcardDetailViewModel
{
    public int Id { get; set; }
    public string RetailerName { get; set; }
    public decimal Amount { get; set; }
    public string Image { get; set; }
}

Конфигурация сопоставления

        CreateMap<GiftcardDetailResponse, GiftcardDetailViewModel>()
            .IncludeMembers(src => src.UserGiftcard);

        //Automapper should pick a concrete mapping for this
        CreateMap<GiftcardInstanceDTO, GiftcardDetailViewModel>()
            .IncludeAllDerived();

        //Concrete mappings to View Model
        CreateMap<UserGiftcardDTO, GiftcardDetailViewModel>()
            .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
            .ForMember(dest => dest.Image, opt => opt.MapFrom(src => src.Giftcard.Image))
            .ForMember(dest => dest.Amount, opt => opt.MapFrom(src => src.Balance))
            .ForMember(dest => dest.RetailerName, opt => opt.MapFrom(src => src.Giftcard.Merchant.Name));

        CreateMap<ImportedGiftcardDTO, GiftcardDetailViewModel>()
            .ForMember(dest => dest.Id, opt => opt.MapFrom(src => src.Id))
            .ForMember(dest => dest.Image, opt => opt.MapFrom(src => src.FrontImage))
            .ForMember(dest => dest.Amount, opt => opt.MapFrom(src => src.Balance))
            .ForMember(dest => dest.RetailerName, opt => opt.MapFrom(src => src.RetailerName));

Проблема в том, что Automapper не выбирает мои явные сопоставления для производного класса моего абстрактного свойства, когда я сопоставляю GiftcardDetailResponse с GiftcardDetailViewModel. Например, если мой код выглядел так:

        var containerClass = new GiftcardDetailResponse();
        containerClass.UserGiftcard = new ImportedGiftcardDTO();

        var viewModel = MapperWrapper.Mapper.Map<GiftcardDetailViewModel>(containerClass);

Дерево выполнения выглядит так

//Automapper generated execution plan
(src, dest, ctxt) =>
{
    GiftcardDetailViewModel typeMapDestination;
    return (src == null)
    ? null
    : {
        typeMapDestination = dest ?? new GiftcardDetailViewModel();
        try
        {
            var resolvedValue = ((src == null) || ((src.UserGiftcard == null) || false)) ? default(int) : src.UserGiftcard.Id;
            typeMapDestination.Id = resolvedValue;
        }
        catch (Exception ex)
        {
            throw new AutoMapperMappingException(
                "Error mapping types.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                PropertyMap);

            return default(int);
        }

        return typeMapDestination;
    };
}

Кажется, что выбираются только те свойства, которые имеют одинаковое имя в обоих GiftcardInstanceDTO и GiftcardDetailViewModel вместо использования моих определенных сопоставлений.

Но то, что я ищу , будет чем-то похожим на дерево выполнения, когда я только явно сопоставляю свое абстрактное свойство с моей моделью представления например,

var propertyModel = MapperWrapper.Mapper.Map<GiftcardDetailViewModel>(containerClass.UserGiftcard);

, который правильно показывает мне мое производное сопоставление

//Automapper generated execution plan
(src, dest, ctxt) =>
{
GiftcardDetailViewModel typeMapDestination;
return (src == null)
    ? null
    : {
        typeMapDestination = dest ?? new GiftcardDetailViewModel();
        try
        {
            var resolvedValue =
            {
                try
                {
                    return ((src == null) || false) ? default(int) : src.Id;
                }
                catch (NullReferenceException)
                {
                    return default(int);
                }
                catch (ArgumentNullException)
                {
                    return default(int);
                }
            };

            typeMapDestination.Id = resolvedValue;
        }
        catch (Exception ex)
        {
            throw new AutoMapperMappingException(
                "Error mapping types.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                PropertyMap);

            return default(int);
        }
        try
        {
            var resolvedValue =
            {
                try
                {
                    return ((src == null) || false) ? null : src.RetailerName;
                }
                catch (NullReferenceException)
                {
                    return null;
                }
                catch (ArgumentNullException)
                {
                    return null;
                }
            };

            var propertyValue = (resolvedValue == null) ? null : resolvedValue;
            typeMapDestination.RetailerName = propertyValue;
        }
        catch (Exception ex)
        {
            throw new AutoMapperMappingException(
                "Error mapping types.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                PropertyMap);

            return null;
        }
        try
        {
            var resolvedValue =
            {
                try
                {
                    return ((src == null) || false) ? null : src.FrontImage;
                }
                catch (NullReferenceException)
                {
                    return null;
                }
                catch (ArgumentNullException)
                {
                    return null;
                }
            };

            var propertyValue = (resolvedValue == null) ? null : resolvedValue;
            typeMapDestination.Image = propertyValue;
        }
        catch (Exception ex)
        {
            throw new AutoMapperMappingException(
                "Error mapping types.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                PropertyMap);

            return null;
        }
        try
        {
            var resolvedValue =
            {
                try
                {
                    return ((src == null) || false) ? default(decimal) : src.Balance;
                }
                catch (NullReferenceException)
                {
                    return default(decimal);
                }
                catch (ArgumentNullException)
                {
                    return default(decimal);
                }
            };

            typeMapDestination.Amount = resolvedValue;
        }
        catch (Exception ex)
        {
            throw new AutoMapperMappingException(
                "Error mapping types.",
                ex,
                AutoMapper.TypePair,
                TypeMap,
                PropertyMap);

            return default(decimal);
        }

        return typeMapDestination;
    };
}

В документации говорится, что я могу использовать IncludeMembers для сглаживания дочерних объектов в целевой объект, когда уже определены сопоставления. Но это поведение, похоже, не работает правильно в этом случае, когда дочерний объект является абстрактным.

1 Ответ

0 голосов
/ 07 августа 2020

IncludeMembers не реализует это поведение "Dynami c", но вы можете сделать что-то вроде этого:

 CreateMap<Source, Destination>().AfterMap((source, destination, context) => context.Mapper.Map(source.InnerSource, destination));
...