简体   繁体   English

我如何使用 Automapper 在两个枚举之间进行 map?

[英]How can I map between two enums using Automapper?

I have a public facing interface that I'm trying to map two different enumerations to each other.我有一个面向公众的界面,我正在尝试 map 两个不同的枚举。 I tried to use the following code:我尝试使用以下代码:

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

When that didn't work, I tried:当那不起作用时,我尝试了:

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

But that doesn't seem to work either.但这似乎也不起作用。 Is there anyway to get automapper to handle this scenario?无论如何让自动映射器来处理这种情况?

Alternatively to writing custom converters, just use ConvertUsing()或者编写自定义转换器,只需使用 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;
    }
});

You don't need to do CreateMap for enum types.您不需要为枚举类型执行 CreateMap。 Just get rid of the CreateMap call and it should work, as long as the names and/or values match up between enum types.只要摆脱 CreateMap 调用,它应该可以工作,只要枚举类型之间的名称和/或值匹配。

My Automapper works this way:我的 Automapper 是这样工作的:

If I create a map: Automapper will match enums by value, even if name match perfectly.如果我创建地图: Automapper 将按值匹配枚举,即使名称完全匹配。

If I do not create a map: Automapper will match enums by name.如果我不创建地图: Automapper 将按名称匹配枚举。

The Simplest Way I found that work for me is as below:我发现对我有用的最简单方法如下:

My Enum is a nested in another class so I Use ForMember method and MapFrom as below:我的 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));

The ProblemType and JudgmentType are Enums. ProblemType 和 JudgmentType 是枚举。 And their related View Models are ProblemTypeViewModel and JudgmentTypeViewModel with same members as their related Models.并且它们相关的视图模型是 ProblemTypeViewModel 和 JudgmentTypeViewModel 与它们的相关模型具有相同的成员。

Although I don't test, But I think below line should work for you:虽然我不测试,但我认为下面这行应该适合你:

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

Hope it Help.希望它有帮助。

The other answers here didn't work for me.这里的其他答案对我不起作用。

You need to create a class that implements:您需要创建一个实现的类:

ITypeConvertor<SourceType ,DestinationType>

So as an example所以作为一个例子

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

And the class:和班级:

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

Here's one possibility for making a conversion between two Enum types that both have different values, while still using AutoMapper.这是在两个具有不同值的 Enum 类型之间进行转换的一种可能性,同时仍然使用 AutoMapper。 In my case, I needed to use AutoMapper because the Enum types were properties on other entities being converted by AutoMapper;就我而言,我需要使用 AutoMapper,因为 Enum 类型是 AutoMapper 转换的其他实体的属性; using AutoMapper for these entities was a requirement.对这些实体使用 AutoMapper 是一项要求。

The first step is to setup the Mapper configuration like so:第一步是像这样设置 Mapper 配置:

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

Calling .ConstructUsing(...) allows us to pass in our own method for making the conversion.调用.ConstructUsing(...)允许我们传入我们自己的方法来进行转换。 The method for conversion is pretty straight forward:转换方法非常简单:

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

We simply switch through the values of the source Enum and return the appropriate destination Enum value arbitrarily.我们简单地switch源 Enum 的值并任意返回适当的目标 Enum 值。 AutoMapper takes care of the rest. AutoMapper 负责其余的工作。

Just create a mapper for two Enums, that's it!只需为两个枚举创建一个映射器,就是这样! Automapper will map by the either the matching value or index value of the Enum. Automapper 将通过 Enum 的匹配值或索引值进行映射。 (eg Draft -> Step1) (例如草稿 -> 步骤 1)

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



    }
}

I was trying to map between "equal" enums using Automapper, but unfortunately it didn't work.我试图使用 Automapper 在“相等”枚举之间进行映射,但不幸的是它不起作用。 I suspect the problem is a difference in casing:我怀疑问题是大小写不同:

public enum Foo {
    val1,
    val2
}

public enum Bar {
    Val1,
    Val2
}

Foo is something auto-generated from an XSD, and the supplier sucks. Foo是从 XSD 自动生成的东西,供应商很糟糕。 Also there are thirty-something values and I didn't want to put a switch that large anywhere for something so silly.还有三十多岁的值,我不想在任何地方放一个这么大的switch来做这么愚蠢的事情。

The approach I took was to convert the source value to string and parse that as the destination value:我采用的方法是将源值转换为字符串并将其解析为目标值:

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

Of course, this only works if your enums only have differences in casing.当然,这仅在您的枚举仅在大小写上有差异时才有效。 You could make it more elaborate by cleaning up the input string (eg remove underscores etc.) or by adding stuff to it as required.您可以通过清理输入字符串(例如删除下划线等)或根据需要向其中添加内容来使其更加复杂。

I know this question is old but if some people like me pass by here...我知道这个问题很老,但如果像我这样的人路过这里......

From the AutoMapper documentation there's now a AutoMapper.Extensions.EnumMapping Nuget package providing a simple way to do it.AutoMapper 文档中,现在有一个AutoMapper.Extensions.EnumMapping Nuget 包提供了一种简单的方法来做到这一点。

Quote from the AutoMapper documentation:引用 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
    }
}

I know this is a very old topic, but it's the main one I get when googling, so decided to show my findings.我知道这是一个非常古老的话题,但它是我在谷歌搜索时得到的主要话题,所以决定展示我的发现。

I have source and destination enums.我有源和目标枚举。 They don't fully match, but sometimes there might be an overlap of the value (the numeric one), but that match might not be correct.它们不完全匹配,但有时值(数字值)可能会重叠,但该匹配可能不正确。 To illustrate, here are 2 enums:为了说明,这里有 2 个枚举:

enum EnumA {
  Item1 = 5,
  Item2 = 10,
  Item3 = 15
}

enum EnumB {
  I1 = 15,
  I2 = 10,
  I3 = 5
}

I wanted to have an explicit map between the two, so I have a switch case like this:我想在两者之间有一个明确的 map,所以我有一个这样的开关盒:

source switch {
  EnumA.Item1 => EnumB.I1,
  EnumA.Item2 => EnumB.I2,
  EnumA.Item3 => EnumB.I3,
  _ => throw new ArgumentException("")
}

So far, so good, this works as expected.到目前为止,一切顺利,这按预期工作。 The interesting part comes when I tried to hook it to AutoMapper.当我试图将它挂接到 AutoMapper 时,有趣的部分出现了。

I've defined a mapping like this我定义了这样的映射

CreateMap<EnumA, EnumB>().ConvertUsing((src, dest) => src switch {
  EnumA.Item1 => EnumB.I1,
  EnumA.Item2 => EnumB.I2,
  EnumA.Item3 => EnumB.I3,
  _ => throw new ArgumentException("")
});

And then used it as a regular member map然后作为普通会员使用 map

.ForMember(dest => dest.PropA, opts => opts.MapFrom(src => src.PropB))

With this code however, when there is a match of the number ids, I would get the wrong enum member.但是,使用此代码,当数字 ID 匹配时,我会得到错误的枚举成员。

The only way I could make it work was to defined a value converter我能让它工作的唯一方法是定义一个值转换器

public class ItemConverter : IValueConverter<EnumA, EnumB>
{
  public CountryCode Convert(EnumA sourceMember, ResolutionContext context) => sourceMember switch
  {
    EnumA.Item1 => EnumB.I1,
    EnumA.Item2 => EnumB.I2,
    EnumA.Item3 => EnumB.I3,
    _ => throw new ArgumentException("")
  }
}

And use it like this:并像这样使用它:

.ForMember(dest => dest.PropA, opts => opts.ConvertUsing(new ItemConverter(), src => src.PropB))

Hope it helps someone like me in the future希望它能帮助将来像我这样的人

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM