Search code examples
c#.netautomapper

I cannot tryparse string from API to enum with AutoMapper


I'm trying to TryParse a string from API to an Enum with AutoMapper, and if cannot parse, he should return an "default value".

My model is: Class Expense

public long Id { get; set; }
public string Description { get; set; }
public double Value { get; set; }
public DateTime Date { get; set; }
public Category Categories { get; set; }

My DTO is: Class ExpenseDTO

public long Id { get; set; }
public string Description { get; set; }
public double Value { get; set; }
public DateTime Date { get; set; }
public string Categories { get; set; }

Enum contain:

public enum Category : int {
   Others = 0,
   Food = 1,
   Health = 2,
   Home = 3,
   Transport = 4,
   Education = 5,
   Chill = 6,
   Unexpected = 7
}

I already tried a resolution that I saw here on Stack OverFlow, which is:

CreateMap<ExpenseDTO, Expense>()
        .ForMember(dest => dest.Category,
            opt =>
            {
                object? parsedResult;
                opt.MapFrom(src =>
                    Enum.TryParse(typeof(Category), src.Categories, true, out parsedResult) ? parsedResult : Category.Others);
            })
        .ReverseMap().ForMember(dest => dest.Category,
            opt => opt.MapFrom(src => src.Categories.ToString()));

That resolution still throw an exception when I try to send a category that not exist like "Test".

The API return that error:

Mapping types:
ExpenseDTO -> Expense
Challenge.API.ExpenseDTO -> Challenge.Domain.Expense
 
Type Map configuration:
ExpenseDTO -> Expense
Challenge.API.ExpenseDTO -> Challenge.Domain.Expense

Someone could help me, please?


Solution

  • If you need something more complicated than a simple src.Field => dest.Field type mapping, you can use a value resolver to plug in that custom logic like so:

    public class CategoriesEnumValueResolver : IValueResolver<ExpenseDTO, Expense, Category>
    {
        public Category Resolve(ExpenseDTO source, Expense destination, Category destMember, ResolutionContext context)
        {
            if (Enum.TryParse<Category>(source.Categories, out var category))
                return category;
    
            return Category.Others;
        }
    }
    

    You then tell AutoMapper to use that value resolver when you create your map:

    CreateMap<ExpenseDTO, Expense>()
        .ForMember(m => m.Categories, opt => opt.MapFrom<CategoriesEnumValueResolver>());