Подтвердить что ты не робот

Автоматическое переключение между автопилотом

Преобразование 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>

Это ужасно!

4b9b3361

Ответ 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);