2015-03-04 6 views
6

Ho il seguente dominio classi e Dto classi:Utilizzando automapper per mappare una stringa in un enum

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

ho usato il seguente metodo:

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

In seguito, ottengo il seguente errore:

Requested value 'Level Two' was not found.

Come si esegue il mapping correttamente?

+0

Qualsiasi motivo per cui si passa il valore dell'attributo di visualizzazione per l'enumerazione? Cioè perché stai usando "Level Two" invece di "LevelTwo". Ciò rende questo compito un po 'più difficile (richiederà una riflessione per risolverlo). – Umair

+0

L'uso di 'LevelTwo' consentirà all'automapper di mappare direttamente (non è necessario il bit' ForMember') – Umair

+0

Qual è la domanda? –

risposta

11

Dal momento che si sta mappando dal nome visualizzazione e non il enum nome avrete bisogno di buid una funzione di mappatura personalizzata per eseguire la scansione gli attributi per trovare l'enum con quel nome visualizzato. È possibile utilizzare ResolveUsing invece di MapFrom di utilizzare una funzione di mappatura personalizzata:

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 
} 

Si potrebbe memorizzare nella cache i nomi in un dizionario statico in modo da non utilizzare la riflessione ogni volta.

Alcuni metodi per farlo sono disponibili here.

6

Ampliando risposta D Stanley s' dall'alto in un po' più dettagliato, e modificato il EnumHelper class from this other discussion di concentrarsi sulla vostra situazione specifica come questa domanda in realtà si estende su due aree, automapper e di ottenere correttamente il valore di un Enum da una stringa.

Migliorare risposta originale D Stanley s':

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

ho regolato l'EnumHelper dall'esempio citato per mostrare rapidamente un'opzione dove da è possibile modificare il metodo Parse provare per primo lo standard Enum. Parse() e, in caso contrario, per provare a eseguire un confronto più dettagliato del tipo Enum creando un dizionario dei valori basato sul nome del valore enum o sul testo dell'attributo Display (se utilizzato).

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