【问题标题】:Automapper custom many-to-one conversionAutomapper 自定义多对一转换
【发布时间】:2015-05-18 10:24:04
【问题描述】:

Automapper 多对一转换

如何将源对象中的许多属性的值转换为目标对象中的单一类型? 我可以在这种情况下使用Value Resolvers吗?或者也许有更好的解决方案?

文档

这里是 example 来自 documentation - 一对一的转换

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>

这太可怕了!

【问题讨论】:

    标签: c# mapping converter automapper dto


    【解决方案1】:

    如果我理解正确,您需要进行以下映射:从 (CurrencyId, ExchangeRateId) 到 Currency。您可以使用Tuple 来实现它(it is a standard .Net class 在这些情况下非常方便):

    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));
    

    【讨论】:

    • 这是一个很好的解决方案,但在单个文档中我有 3-4 个这样的案例,我不会以手动方式映射此属性。我想在主对象上创建映射(使用或不使用某些解析器),并在映射后获得正确状态的完整对象。
    【解决方案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 ));
    

    也可以:

    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);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-09-10
        • 1970-01-01
        • 2018-08-18
        • 2016-01-13
        • 2017-02-17
        相关资源
        最近更新 更多