Automapper自定义多对一转换
本文关键字:转换 多对一 自定义 Automapper | 更新日期: 2023-09-27 17:49:29
Automapper多对一转换
如何将源对象中的许多属性值转换为目标对象中的单个类型?我可以在这种情况下使用值解析器吗?或者有更好的解决办法?
下面是文档中的示例- 一对一转换
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
}
案例我想把两个对象转换成一个对象(多对一转换)。例如:
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!
谢谢你的建议。
我的解决方案我想出了两个解决方案,但我不喜欢它们(太脏了)
解决方案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>
这太可怕了!
如果我理解正确,您需要做以下映射:从(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));
也许你可以这样映射:
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>();
如果您确定每个文档类型都要这样做:
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);