Map string to enum with Automapper
Asked Answered
T

2

7

My problem is hydrating a Viewmodel from a Linq2Sql object that has been returned from the database. We have done this in a few areas and have a nice layered pattern worked up for it but the latest item calls for some enums to be used and this has caused headaches all round. Currently we pull back from the database then use Automapper to hydrate (or flatten) into our Viewmodels but having the enums in the model seems to be causing issues with Automapper. I've tried to create custom resovlers which have sufficed for all my other mapping requirements but it doesn't work in this instance.

A sample of the code looks like:

public class CustomerBillingTabView{
    public string PaymentMethod {get; set;}
    ...other details
}

public class BillingViewModel{
    public PaymentMethodType PaymentMethod {get; set;}
    ...other details
}

public enum PaymentMethodType {
    Invoice, DirectDebit, CreditCard, Other
}

public class PaymentMethodTypeResolver : ValueResolver<CustomerBillingTabView, PaymentMethodType>
{
    protected override PaymentMethodType ResolveCore(CustomerBillingTabView source)
    {

        if (string.IsNullOrWhiteSpace(source.PaymentMethod))
        {
            source.PaymentMethod = source.PaymentMethod.Replace(" ", "");
            return (PaymentMethodType)Enum.Parse(typeof(PaymentMethodType), source.PaymentMethod, true);
        }

        return PaymentMethodType.Other;
    }
}

        CreateMap<CustomerBillingTabView, CustomerBillingViewModel>()
        .ForMember(c => c.CollectionMethod, opt => opt.ResolveUsing<PaymentMethodTypeResolver>())

I get the following error

[ArgumentException: Type provided must be an Enum.
Parameter name: enumType]
   System.Enum.TryParseEnum(Type enumType, String value, Boolean ignoreCase, EnumResult& parseResult) +9626766
   System.Enum.Parse(Type enumType, String value, Boolean ignoreCase) +80
   AutoMapper.Mappers.EnumMapper.Map(ResolutionContext context, IMappingEngineRunner mapper) +231
   AutoMapper.MappingEngine.AutoMapper.IMappingEngineRunner.Map(ResolutionContext context) +720

I'd like to stick with Automapper for all of our mapping actions but I've seen a lot of people say that it doesn't do this type of mappings so I'm starting to wonder if I'm using it in the wrong way? Also, I've seen a few mentions of ValueInjecter - is this an alternative to Automapper, or will it be useful to just plug the holes in Automapper for the hydration of models and use Automapper for flattening?

Yes I could just use a string in my ViewModel, but I'm not a fan of magic strings, and this particular item is used by helpers to perform some logic in a number of places.

Trudey answered 20/9, 2010 at 13:56 Comment(2)
D'oh after looking closer at the examples in the source and my models I realized a couple of things firstly, for some reason I'd made the enum property on the ViewModel nullable which caused the main problems??!! The second thing was that I hadn't considered the white space returned by our view "Direct Debit" should have been DirectDebit.. Once I removed these issues Automapper did it's thang with out custom resolvers etc.. Woo hooTrudey
+1 for mentioning ValueInjecter ;)Ingar
L
10

This is an issue with the AutoMapper documentation. If you download the AutoMapper source there are examples in there. The code you want will look like this:

public class PaymentMethodTypeResolver : ValueResolver<CustomerBillingTabView, PaymentMethodType>
{
    protected override PaymentMethodType ResolveCore(CustomerBillingTabView source)
    {

        string paymentMethod = source.Context.SourceValue as string;

        if (string.IsNullOrWhiteSpace(paymentMethod))
        {
            paymentMethod  = paymentMethod.Replace(" ", "");
            return source.New((PaymentMethodType)Enum.Parse(typeof(PaymentMethodType), paymentMethod, true));
        }

        return source.New(PaymentMethodType.Other);
    }
}
Lupulin answered 20/9, 2010 at 18:59 Comment(2)
Thanks Jason.. I went to try you solution but I can't understand where the .Context.SourceValue and the source.New have come from? Am I missing soemthing.. sorry bit of a noob I know.Trudey
Hi Jason Thanks for the pointers if you see my comment above I got it sorted with out the need for custom resolvers. Thanks all the same and pointing me at the samples was a great helpTrudey
I
5

here's a solution with the ValueInjecter: since you already solved the problem I'm just going to point you to something similar:

AutoMapper strings to enum descriptions

in this question the requirements were a bit more than just doing from string to enum, but it includes this conversion also

about the ValueInjecter being an alternative: yes, it does stuff more generic no configuration for every little thing required, and build whatever convention you can imagine

Ingar answered 24/9, 2010 at 19:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.