【发布时间】:2020-08-06 22:16:30
【问题描述】:
我在将包含抽象属性的容器类映射到我的视图模型目标类时遇到了一些问题。
映射源类
//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 没有为我的抽象属性的派生类选择我的显式映射。例如,如果我的代码看起来像这样
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 将子对象展平到目标对象。但是当子对象是抽象的时,这种行为在这种情况下似乎不能正常工作。
【问题讨论】:
-
IncludeMembers并非旨在以这种方式工作。您可以使用AfterMap调用Map并传递目标对象。 -
只是因为我的子成员是抽象的吗?因为文档中显示的示例非常相似docs.automapper.org/en/stable/Flattening.html#includemembers
-
不,这是因为子成员的映射在运行时没有解析,是预先解析的,在配置时,它是静态的。当您调用
Map时,Include通过在运行时解析地图来工作。 -
到目前为止,添加 AfterMap 似乎不起作用。如果我尝试用我的派生映射覆盖目标,则只返回一个空白对象。我会尝试添加一个自定义解析器,看看是否可行
-
AfterMap((source, destination, context) => context.Map(source.UserGiftcard, destination)
标签: c# inheritance polymorphism automapper automapper-9