使用AutoMapper将字符串映射到枚举

时间:2015-03-04 14:52:08

标签: c# .net automapper

我有以下类domain和Dto类:

public class Profile
{
   public string Name { get; set; }
   public string SchoolGrade { get; set; } 
}

public class ProfileDTO
{
   public string Name { get; set; }
   public SchoolGradeDTO SchoolGrade { get; set; } 
}

public enum SchoolGradeDTO 
{
   [Display(Name = "Level One"]
   LevelOne,
   [Display(Name = "Level Two"]
   LevelTwo,
}

我使用了以下方法:

 Mapper.CreateMap<Profile, ProfileDTO>()
       .ForMember(d => d.SchoolGrade , op => op.MapFrom(o => o.SchoolGrade))

之后,我收到以下错误:

  

未找到请求值“第二级”。

如何正确映射?

3 个答案:

答案 0 :(得分:15)

由于您是从显示名称而不是 enum 名称进行映射,因此您需要建立自定义映射函数来扫描属性以查找枚举那个显示名称。您可以使用ResolveUsing代替MapFrom来使用自定义映射功能:

Mapper.CreateMap<Profile, ProfileDTO>()
      .ForMember(d => d.SchoolGrade, 
                op => op.ResolveUsing(o=> MapGrade(o.SchoolGrade)));

public static SchoolGradeDTO MapGrade(string grade)
{
    //TODO: function to map a string to a SchoolGradeDTO
}

您可以将名称缓存在静态字典中,这样每次都不会使用反射。

可以找到一些方法here

答案 1 :(得分:8)

更详细地扩展 D Stanley 的答案,并修改EnumHelper class from this other discussion以关注您的具体情况,因为此问题确实涵盖两个方面,AutoMapper和正确从字符串中获取Enum的值。

增强 D Stanley 的原始答案:

public static class QuestionAutoMapperConfig
{
    public static void ConfigureAutoMapper()
    {
        Mapper.CreateMap<Profile, ProfileDTO>()
            .ForMember(d => d.SchoolGrade,
                op => op.ResolveUsing(o => MapGrade(o.SchoolGrade)));
    }

    public static SchoolGradeDTO MapGrade(string grade)
    {
        //TODO: function to map a string to a SchoolGradeDTO
        return EnumHelper<SchoolGradeDTO>.Parse(grade);
    }
}

我已经从上述示例调整了EnumHelper以快速显示一个选项,您可以通过修改Parse方法来首先尝试标准的Enum.Parse(),并且无法尝试对Enum进行更详细的比较通过根据枚举值名称或显示属性文本(如果使用)创建值的字典来键入。

public static class EnumHelper<T>
{
    public static IDictionary<string, T> GetValues(bool ignoreCase)
    {
        var enumValues = new Dictionary<string, T>();

        foreach (FieldInfo fi in typeof(T).GetFields(BindingFlags.Static | BindingFlags.Public))
        {
            string key = fi.Name;

            var display = fi.GetCustomAttributes(typeof(DisplayAttribute), false) as DisplayAttribute[];
            if (display != null)
                key = (display.Length > 0) ? display[0].Name : fi.Name;

            if (ignoreCase)
                key = key.ToLower();

            if (!enumValues.ContainsKey(key))
                enumValues[key] = (T)fi.GetRawConstantValue();
        }

        return enumValues;
    }

    public static T Parse(string value)
    {
        T result;

        try
        {
            result = (T)Enum.Parse(typeof(T), value, true);
        }
        catch (Exception)
        {
            result = ParseDisplayValues(value, true);
        }


        return result;
    }

    private static T ParseDisplayValues(string value, bool ignoreCase)
    {
        IDictionary<string, T> values = GetValues(ignoreCase);

        string key = null;
        if (ignoreCase)
            key = value.ToLower();
        else
            key = value;

        if (values.ContainsKey(key))
            return values[key];

        throw new ArgumentException(value);
    }
}

答案 2 :(得分:0)

在映射配置中

{
CreateMap<string, CUSTOM_ENUM>().ConvertUsing<StringToEnumConverter<CUSTOM_ENUM>>();
}

转换器

public class StringToEnumConverter<T> : ITypeConverter<string, T>, ITypeConverter<string, T?> where T : struct
    {
        public T Convert(ResolutionContext context)
        {
            T t;
            if (Enum.TryParse(source, out t))
            {
                return t;
            }

            var source = (string)context.SourceValue;
            if (StringToEnumBase<T>.HasDisplayAttribute())
            {
                var result = StringToEnumBase<T>.Parse(source);
                return result;
            }

            throw new ConverterException();
        }

        T? ITypeConverter<string, T?>.Convert(ResolutionContext context)
        {
            var source = (string)context.SourceValue;
            if (source == null) return null;

            return Convert(context);
        }
    }

    public static class StringToEnumBase<T> where T:struct
        {
            public static T Parse(string str)
            {
                var type = typeof (T);

                var enumMembers = type.GetMembers(BindingFlags.Public | BindingFlags.Static);

                var enumMembersCollection = enumMembers
                    .Select(enumMember => new
                    {
                        enumMember,
                        attributes = enumMember.GetCustomAttributes(typeof(DisplayAttribute), false)
                    })
                    .Select(t1 => new
                    {
                        t1, value = ((DisplayAttribute) t1.attributes[0]).Name
                    })
                    .Select(t1 => new Tuple<string, string>(t1.value, t1.t1.enumMember.Name))
                    .ToList();
                var currentMember = enumMembersCollection.FirstOrDefault(item => item.Item1 == str);
                if (currentMember == null) throw new ConverterException();

                T t;
                if (Enum.TryParse(currentMember.Item2, out t))
                {
                    return t;
                }

                throw new ConverterException();
            }

            public static bool HasDisplayAttribute()
            {
                var type = typeof (T);
                var attributes = type.GetCustomAttributes(typeof(DisplayAttribute), false);
                return attributes.Length > 0;
            }
        }