Automapper自定义多对一转换

Paw*_*aga 18 c# mapping converter dto automapper

Automapper 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
}
Run Code Online (Sandbox Code Playgroud)

案件

我想将两个对象转换为一个(多对一转换).例如:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我想实现这样的目标:

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;
    }
}
Run Code Online (Sandbox Code Playgroud)

我知道我可以将整个对象作为源对象传递,但对我来说这不是一个解决方案:

ValueResolver<Document, Currency>
Run Code Online (Sandbox Code Playgroud)

我不能使用完整对象,因为我有很多文档类型,我不想为每个文档创建新的解析器.在我的情况下,也不允许忽略元素(用于手动转换).货币转换逻辑必须由AutoMapper执行.

转换发生在背景中(在主题转换期间)对我来说很重要.

例如:

Document document;
var documentDto = Mapper.Map<DocumentDto>(document); // and in this moment i have proper CurrencyDetails object!
Run Code Online (Sandbox Code Playgroud)

感谢您的意见.

我的解决方案

我想了两个解决方案,但我不喜欢它们(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;}
}
Run Code Online (Sandbox Code Playgroud)

并使用具有以下参数的解析器:

ValueResolver<ICurrencyHolder, Currency>
Run Code Online (Sandbox Code Playgroud)

解决方案2 - 作为源元素对象类型并通过反射获取值

ValueResolver<object, Currency>
Run Code Online (Sandbox Code Playgroud)

这很糟糕!

Joa*_*nvo 8

如果我理解正确,您需要执行以下映射:from(CurrencyId,ExchangeRateId)to Currency.你可以使用来实现它Tuple(在这些情况下它是一个非常方便的标准.Net类):

Mapper.CreateMap<Tuple<int,int>, Currency>()
   .ForMember(x => x.Currency, cfg => cfg.MapFrom(y => new Currency(y.Item1, y.Item2));
Run Code Online (Sandbox Code Playgroud)

按如下方式调用映射器:

Mapper.Map<Tuple<int,int>, Currency>(Tuple.Create(doc.CurrencyId, doc.ExchangeRateId));
Run Code Online (Sandbox Code Playgroud)


Guy*_*vin 2

也许你可以这样映射它:

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 ));
Run Code Online (Sandbox Code Playgroud)

也可能:

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>();
Run Code Online (Sandbox Code Playgroud)