У меня возникают некоторые проблемы с сопоставлением класса контейнера, содержащего абстрактное свойство, с классом назначения моей модели представления.
Сопоставление исходных классов
//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
для сглаживания дочерних объектов в целевой объект, когда уже определены сопоставления. Но это поведение, похоже, не работает правильно в этом случае, когда дочерний объект является абстрактным.