web-dev-qa-db-ja.com

AutoMapperを使用して文字列を列挙型にマップする

次のクラスドメインと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))

その後、次のエラーが発生します。

要求された値「レベル2」が見つかりませんでした。

どうすれば正しくマッピングできますか?

15
gog

enumの名前ではなく、display nameからマッピングしているためカスタムマッピング関数を作成して属性をスキャンし、その表示名を持つ列挙型を見つける必要があります。 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
}

静的辞書に名前をキャッシュして、毎回リフレクションを使用しないようにすることができます。

それを行うためのいくつかの方法 ここ を見つけることができます。

16
D Stanley

上記からのD Stanleyの回答をもう少し詳しく展開し、 この他の議論のEnumHelperクラス を変更しましたこの質問は実際にはAutoMapperと文字列からEnumの値を正しく取得する2つの領域にまたがっているため、特定の状況に焦点を当てます。

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を調整して、最初に標準のEnum.Parse()を試すようにParseメソッドを変更し、それを作成してEnum型のより詳細な比較を行うことができないオプションをすばやく表示しました列挙値名、またはそのDisplay属性テキスト(使用されている場合)に基づく値のディクショナリ。

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);
    }
}
8
Scott

マッピング構成

{
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;
            }
        }
0
m4a