【问题标题】:How can I map between two enums using Automapper?如何使用 Automapper 在两个枚举之间进行映射?
【发布时间】:2012-07-01 05:22:16
【问题描述】:

我有一个面向公众的界面,我试图将两个不同的枚举相互映射。我尝试使用以下代码:

Mapper.CreateMap<Contract_1_1_0.ValidationResultType, Common.ValidationResultType>();

当这不起作用时,我尝试了:

Mapper.CreateMap<Contract_1_1_0.ValidationResultType, Common.ValidationResultType>().ConvertUsing(x => (Common.ValidationResultType)((int)x));

但这似乎也不起作用。有没有办法让 automapper 来处理这种情况?

【问题讨论】:

  • 您收到错误了吗?什么不起作用?
  • 我收到“缺少类型映射配置或不支持的映射”。错误。
  • 你能发布你的枚举吗?

标签: c# automapper


【解决方案1】:

我知道这个问题很老了,但是如果像我这样的人经过这里...

AutoMapper documentation 现在有一个 AutoMapper.Extensions.EnumMapping Nuget package 提供了一种简单的方法。

引用自 AutoMapper 文档:


public enum Source
{
    Default = 0,
    First = 1,
    Second = 2
}

public enum Destination
{
    Default = 0,
    Second = 2
}

internal class YourProfile : Profile
{
    public YourProfile()
    {
        CreateMap<Source, Destination>()
            .ConvertUsingEnumMapping(opt => opt
                // optional: .MapByValue() or MapByName(), without configuration MapByValue is used
                .MapValue(Source.First, Destination.Default)
            )
            .ReverseMap(); // to support Destination to Source mapping, including custom mappings of ConvertUsingEnumMapping
    }
}

【讨论】:

  • 但是除了ReverseMap之外,基本包都可以轻松搞定,所以用处不大。
  • @LucianBargaoanu 抱歉,但在 Google 上搜索会导致我在答案中链接的文档页面或这个 SO 问题。但是,在上面的所有答案中,我看不到使用 AutoMapper 基本包的任何直接方法。就我个人而言,我不喜欢使用ConvertUsing 方法并在里面编写一个switch case。我发现我的路更短,因此更清晰。您能否提供使用**基本包**的方法?谢谢
  • 如上所述,默认情况下按名称获取地图(回退到值),如果创建地图,则按值获取。然后switch 表达式比您的代码更清晰、更易于理解。使用包参考较少。
  • 好的,我的回答实际上是展示如何使用不同的属性名称映射枚举。在我的情况下,我有 2 个具有相同常量名称的枚举,除了一个。在这种情况下,我无法使用默认名称映射,并且编写 switch case 将迫使我手动映射所有常量,即使它们具有相同的名称。使用ConvertUsingEnumMapping,我可以只为与源名称不同的目标常量名称指定一个映射。我会留下我的答案,因为我认为它仍然可以使其他人受益。在我看来比开关盒更方便
  • 不,Enum.TryParse 将覆盖其余部分,仅包含两个分支。并且值在您的情况下匹配,您甚至不需要它,它只是一个演员表。我想一个简单的条件可以工作,你甚至不需要switch
【解决方案2】:

这里的其他答案对我不起作用。

你需要创建一个类来实现:

ITypeConvertor<SourceType ,DestinationType>

举个例子

 Mapper.CreateMap<EnumType1.VatLevel, EnumType2.VatRateLevel>()
       .ConvertUsing(new VatLevelConvertor());

还有班级:

internal class VatLevelConvertor : ITypeConverter<EnumType1.VatLevel, EnumType2.VatRateLevel>
{
    public EnumType2.VatRateLevel Convert(ResolutionContext context)
    {
        EnumType1.VatLevel value = (EnumType1.VatLevel)context.SourceValue;
        switch (value)
        {
            case EnumType1.VatLevel.Standard:
                return EnumType2.VatRateLevel.Normal;
            case EnumType1.VatLevel.Reduced:
                return EnumType2.VatRateLevel.Lower;
            case EnumType1.VatLevel.SuperReduced:
                return EnumType2.VatRateLevel.Other;
            default:
                return EnumType2.VatRateLevel.Other;
        }
    }
}

【讨论】:

    【解决方案3】:

    我的 Automapper 是这样工作的:

    如果我创建地图: Automapper 将按值匹配枚举,即使名称完全匹配。

    如果我不创建地图: Automapper 将按名称匹配枚举。

    【讨论】:

      【解决方案4】:

      只需为两个枚举创建一个映射器,就是这样! Automapper 将通过 Enum 的匹配值或索引值进行映射。 (例如草稿 -> Step1)

      public enum SourceStatus
      {
          Draft,
          Submitted,
          Deleted
      }
      
      public enum DestinationStatus
      {
          Step1,
          Step2,
          Step3
      }
      
      public class SourceObj
      {
          public SourceStatus Status { get; set; }
      }
      
      public class DestinationObj
      {
          public DestinationStatus Status { get; set; }
      }
      
      class Program
      {
          static void Main(string[] args)
          {
              //Static APi style - this is obsolete now. From Version 5.0 onwards    this will be removed.
              SourceObj mySrcObj = new SourceObj();
              mySrcObj.Status = SourceStatus.Deleted;
      
              Mapper.CreateMap<SourceStatus, DestinationStatus>();
              Mapper.CreateMap<SourceObj, DestinationObj>();
      
              DestinationObj myDestObj = Mapper.Map<SourceObj, DestinationObj>(mySrcObj);
      
              //New way of doing it
              SourceObj mySrcObj2 = new SourceObj();
              mySrcObj2.Status = SourceStatus.Draft;
      
              var config = new MapperConfiguration(cfg =>
              {
                  cfg.CreateMap<SourceObj, DestinationObj>();
              });
      
              IMapper mapper = config.CreateMapper();
              var source = new SourceObj();
              var dest = mapper.Map<SourceObj, DestinationObj>(source);
      
      
      
          }
      }
      

      【讨论】:

      • 这样的枚举映射非常危险。你知道当一种类型有数字而另一种没有数字时会发生什么吗?它得到了 REKT 我不知道为什么,但它不再起作用了,特别是如果我们想要一个 null 默认值。
      【解决方案5】:

      我试图使用 Automapper 在“相等”枚举之间进行映射,但不幸的是它不起作用。我怀疑问题出在外壳上:

      public enum Foo {
          val1,
          val2
      }
      
      public enum Bar {
          Val1,
          Val2
      }
      

      Foo 是从 XSD 自动生成的东西,供应商很烂。还有 30 多岁的值,我不想把这么大的 switch 放在任何地方,因为这很愚蠢。

      我采用的方法是将源值转换为字符串并将其解析为目标值:

      static Foo ConvertEnum(Bar source)
      {
          Foo result;
          var parsed = Enum.TryParse(source.ToString().ToLowerInvariant(), true, out result);
          if(!parsed)
               // throw or return default value
               throw new ArgumentOutOfRangeException("source", source, "Unknown source value");
          return result;
      }
      

      当然,这只适用于您的枚举只有大小写不同的情况。您可以通过清理输入字符串(例如删除下划线等)或根据需要向其中添加内容来使其更精细。

      【讨论】:

        【解决方案6】:

        我发现对我有用的最简单的方法如下:

        我的 Enum 嵌套在另一个类中,所以我使用 ForMember 方法和 MapFrom 如下:

         Mapper.CreateMap<ProblematicCustomer, ProblematicCustomerViewModel>()                
                    .ForMember(m=> m.ProblemType, opt=> opt.MapFrom(x=> (ProblemTypeViewModel)(int)x.ProblemType))
                    .ForMember(m=> m.JudgmentType, opt=> opt.MapFrom(x=> (JudgmentTypeViewModel)(int)x.JudgmentType));
        

        ProblemType 和 JudgmentType 是枚举。其相关的 View Model 是 ProblemTypeViewModel 和 JudgmentTypeViewModel,其成员与其相关的 Model 相同。

        虽然我没有测试,但我认为下面的行应该适合你:

        Mapper.CreateMap<Contract_1_1_0.ValidationResultType, Common.ValidationResultType>()
                   .ForMember(m=> m, opt => opt.MapFrom(x=> (Common.ValidationResultType)(int)x);
        

        希望对您有所帮助。

        【讨论】:

          【解决方案7】:

          除了编写自定义转换器,只需使用 ConvertUsing()

          Mapper.CreateMap<EnumSrc, EnumDst>().ConvertUsing(value => 
          {
              switch(value)
              {
                  case EnumSrc.Option1:
                      return EnumDst.Choice1;
                  case EnumSrc.Option2:
                      return EnumDst.Choice2;
                  case EnumSrc.Option3:
                      return EnumDst.Choice3;
                  default:
                      return EnumDst.None;
              }
          });
          

          【讨论】:

          • 我使用的是ConstructUsing,它以某种方式返回了错误的枚举,即使我的代码是一个巨大的 switch 语句返回正确的枚举。使用ConvertUsing 解决了这个问题。
          • 如果我尝试这个,我会收到 A lambda expression with a statement body cannot be converted to an expression tree 错误...不过这个方法很棒。
          • 您必须匹配表达式签名,在这种情况下,上面的示例缺少 destination 参数。将 value =&gt; 替换为 (value, destination) =&gt; 将解决此问题。
          【解决方案8】:

          这是在两种具有不同值的 Enum 类型之间进行转换的一种可能性,同时仍使用 AutoMapper。就我而言,我需要使用 AutoMapper,因为 Enum 类型是由 AutoMapper 转换的其他实体的属性;对这些实体使用 AutoMapper 是一项要求。

          第一步是像这样设置 Mapper 配置:

          Mapper.CreateMap<EnumSrc, EnumDst>()
              .ConstructUsing(EnumConversion.FromSrcToDst);
          

          调用.ConstructUsing(...) 允许我们传入我们自己的方法来进行转换。转换方法非常简单:

          public class EnumConversion
          {
              internal static EnumDst FromSrcToDst(ResolutionContext arg)
              {
                  EnumSrc value = (EnumSrc)arg.SourceValue;
                  switch(value)
                  {
                      case EnumSrc.Option1:
                          return EnumDst.Choice1;
                      case EnumSrc.Option2:
                          return EnumDst.Choice2;
                      case EnumSrc.Option3:
                          return EnumDst.Choice3;
                      default:
                          return EnumDst.None;
                  }
              }
          }
          

          我们只是通过源 Enum 的值switch 并任意返回适当的目标 Enum 值。 AutoMapper 负责其余的工作。

          【讨论】:

          • 我尝试了这个实现,它只是根据整数值进行映射(AutoMapper 3.3.1)。我将在下面添加我的答案
          【解决方案9】:

          您不需要为枚举类型执行 CreateMap。只要去掉 CreateMap 调用,它就可以工作,只要枚举类型之间的名称和/或值匹配。

          【讨论】:

          • 如果名称和值不匹配怎么办?
          • 那么你需要创建一个自定义类型转换器。
          • “你不需要用 Y 做 X”不是“我如何用 Y 做 X”的答案。当然,询问 Y 是否真的有必要从来没有什么坏处——通常情况下并非如此。
          • @threed 问题是“如何使用 AutoMapper 映射两个枚举”。我的答案就是答案。问题的其余部分开始于关于 OP 尝试的事情的错误道路。我的回答让他们走上了正确的道路。
          • 哦,哇,很抱歉评论泛滥,但显然错误是由其他原因引起的,类似于 github.com/AutoMapper/AutoMapper/issues/280 。创建枚举映射后,我得到“参数类型不匹配”,发现导致所述枚举属性可为空的问题。然后我发现我实际上可以删除自定义枚举映射。很奇怪,但如果有人发现同样的事情,希望这条评论会有所帮助。
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-08-03
          • 2013-08-12
          • 2021-03-07
          相关资源
          最近更新 更多