Автоматическое переключение между автопилотом
Преобразование Autoapper Many To One
Как преобразовать значения многих свойств из исходного объекта в один тип в целевом объекте?
Могу ли я использовать в этом случае Value Resolvers? Или, может быть, есть лучшее решение?
Документация
Вот пример из документация - одна к одному преобразованию
Mapper.CreateMap<Source, Destination>()
.ForMember(dest => dest.Total,
opt => opt.ResolveUsing<CustomResolver>().FromMember(src => src.SubTotal));
Mapper.CreateMap<OtherSource, OtherDest>()
.ForMember(dest => dest.OtherTotal,
opt => opt.ResolveUsing<CustomResolver>().FromMember(src => src.OtherSubTotal));
public class CustomResolver : ValueResolver<decimal, decimal> {
// logic here
}
Case
Я хочу передать два объекта в одно (много к одному преобразованию). Например:
public class Document
{
public int CurrencyId {get; set;}
public int ExchangeRateId {get; set;}
}
public class DocumentDto
{
public Currency Currency {get; set;}
}
public class CurrencyDetails
{
public Currency Currency {get; private set;}
public ExchangeRate ExchangeRate {get; private set;}
public CurrencyDetails(Currency currency, ExchangeRate exchangeRate)
{
Currency = currency;
ExchangeRate = exchangeRate;
}
}
Я хотел бы добиться чего-то подобного:
public class CurrencyResolver : ValueResolver<int, int, CurrencyDetails>
{
protected override Currency ResolveCore(int currencyId, int exchangeRateId)
{
var currency = new Currency(currencyId); //simplified logic
var exchangeRate = new ExchangeRate(exchangeRateId);
var currencyDetails = new CurrencyDetails(currency, exchangeRate);
return currencyDetails;
}
}
Я знаю, что могу передать весь объект в качестве исходного объекта, но для меня это не решение:
ValueResolver<Document, Currency>
Я не могу использовать полный объект, потому что у меня много типов документов, и я не хочу создавать новый распознаватель для каждого документа.
Игнорирование элемента (для ручного преобразования) также не допускается в моем случае. Логика преобразования валюты должна быть выполнена с помощью AutoMapper.
Для меня важно, чтобы преобразование происходило в фоновом режиме (при преобразовании основного объекта).
Например:
Document document;
var documentDto = Mapper.Map<DocumentDto>(document); // and in this moment i have proper CurrencyDetails object!
Спасибо за ваш совет.
Мои решения
Я решил два решения, но мне они не нравятся (soooo dirty)
Решение 1 - заверните класс с интерфейсом:
public interface ICurrencyHolder
{
int CurrencyId {get; set;}
int ExchangeRateId {get; set;}
}
public class Document : ICurrencyHolder
{
public int CurrencyId {get; set;}
public int ExchangeRateId {get; set;}
}
и использовать преобразователь со следующими параметрами:
ValueResolver<ICurrencyHolder, Currency>
Решение 2 - взять в качестве исходного объекта элемента источника и принять значения через отражение
ValueResolver<object, Currency>
Это ужасно!
Ответы
Ответ 1
Если я правильно понял, вам нужно сделать следующее отображение: from (CurrencyId
, ExchangeRateId
) до Currency
. Вы можете достичь этого, используя Tuple
(это стандартный .Net класс, очень удобный в этих случаях):
Mapper.CreateMap<Tuple<int,int>, Currency>()
.ForMember(x => x.Currency, cfg => cfg.MapFrom(y => new Currency(y.Item1, y.Item2));
Вызвать картограф следующим образом:
Mapper.Map<Tuple<int,int>, Currency>(Tuple.Create(doc.CurrencyId, doc.ExchangeRateId));
Ответ 2
Maybee вы можете сопоставить его так:
Mapper.CreateMap<Source, Destination>()
.ConstructUsing(s => Mapper.Map<Source, Currency>(s));
Mapper.CreateMap<Source, Currency>()
.ForMember(dst => dst.CurrencySymbol, map => map.MapFrom(src => src.DocumentDto.CurrencySymbol))
.ForMember(dst => dst.ExchangeRate , map => map.MapFrom(src => src.Document.ExchangeRate ));
Также возможно:
Mapper.CreateMap<Source, Destination>()
.ConstructUsing(s => Mapper.Map<Source, Currency>(s));
Mapper.CreateMap<Source, Currency>()
.ConstructUsing(s => Mapper.Map<DocumentDto, Currency>(s))
.ConstructUsing(s => Mapper.Map<Document, Currency>(s));
Mapper.CreateMap<DocumentDto, Currency>();
Mapper.CreateMap<Document, Currency>();
Ответ 3
Если вы уверены, что будете делать это для каждого типа документа:
Document document;
var documentDto = Mapper.Map<DocumentDto>(document);
Затем вам нужно будет определить сопоставления для каждого из них. Поэтому я определенно займусь идеей ICurrencyHolder и использую такое решение:
Преобразователь
public class CurrencyResolver : ValueResolver<ICurrencyHolder, Currency>
{
protected override Currency ResolveCore(ICurrencyHolder source)
{
return new Currency(source.CurrencyId, source.ExchangeRateId);
}
}
Документ "типы"
public class Document : ICurrencyHolder
{
public int CurrencyId { get; set; }
public int ExchangeRateId { get; set; }
}
public class ExtendedDocument : ICurrencyHolder
{
public DateTime SomeDate { get; set; }
public int CurrencyId { get; set; }
public int ExchangeRateId { get; set; }
}
public interface ICurrencyHolder
{
int CurrencyId { get; set; }
int ExchangeRateId { get; set; }
}
И отображения:
Mapper.CreateMap<Document, DocumentDto>().ForMember(m => m.Currency, opt => opt.ResolveUsing<CurrencyResolver>());
Mapper.CreateMap<ExtendedDocument, DocumentDto>().ForMember(m => m.Currency, opt => opt.ResolveUsing<CurrencyResolver>());
С помощью этого вы можете создать свое dto, как это, и получить разрешение "Валюта" для вас на этапе сопоставления:
var dto = Mapper.Map<DocumentDto>(document);
var extendedDto = Mapper.Map<DocumentDto>(extendedDocument);