将摘要 属性 映射 class 到目的地

Map class with abstract property to destination

我在将包含抽象 属性 的容器 class 映射到我的视图模型目标 class.

时遇到了一些问题

映射源类

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

问题是,当我将 GiftcardDetailResponse 映射到 GiftcardDetailViewModel 时,Automapper 没有为我的摘要 属性 的派生 class 选择我的显式映射。例如,如果我的代码看起来像这样

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

它似乎只选择在 GiftcardInstanceDTOGiftcardDetailViewModel 中共享相同名称的属性,而不是使用我定义的映射。

但是我寻找的东西类似于执行树,当我只明确地将我的抽象属性映射到我的视图模型时,例如

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 将子对象展平到目标对象。但是当子对象是抽象的时,这种行为在这种情况下似乎无法正常工作。

IncludeMembers 没有实现这种“动态”行为,但您可以这样做:

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