web-dev-qa-db-fra.com

Utilisation d'AutoMapper pour mapper une chaîne à une énumération

J'ai le domaine de classes et les classes Dto suivants:

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

J'ai utilisé la méthode suivante:

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

Ensuite, j'obtiens l'erreur suivante:

La valeur demandée "Niveau deux" est introuvable.

Comment le mapper correctement?

15
gog

Étant donné que vous mappez à partir du nom d'affichage et non du nom enum vous devrez créer une fonction de mappage personnalisée pour analyser les attributs afin de trouver l'énumération avec ce nom d'affichage. Vous pouvez utiliser ResolveUsing au lieu de MapFrom pour utiliser une fonction de mappage personnalisée:

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
}

Vous pouvez mettre en cache les noms dans un dictionnaire statique afin de ne pas utiliser la réflexion à chaque fois.

Quelques méthodes pour le faire peuvent être trouvées ici .

16
D Stanley

Développer sur la réponse de D Stanley ci-dessus avec un peu plus de détails et modifier la classe EnumHelper de cette autre discussion à concentrez-vous sur votre situation spécifique car cette question couvre vraiment deux domaines, AutoMapper et l'obtention correcte d'une valeur Enum à partir d'une chaîne.

Améliorer la réponse originale de 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);
    }
}

J'ai ajusté EnumHelper à partir de l'exemple mentionné pour afficher rapidement une option par laquelle vous pouvez modifier la méthode Parse pour essayer d'abord le standard Enum.Parse (), et à défaut, essayer de faire une comparaison plus détaillée du type Enum en créant un dictionnaire des valeurs basé soit sur le nom de la valeur énumérée, soit sur le texte de l'attribut Display (si utilisé).

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

dans la configuration de mappage

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

convertisseurs

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