AutoMapper profile injections
I am a big fan of Jimmy Bogard's AutoMapper. It helps you focus on solving your problem. But once in a while you encounter mapping scenarios that just are a bit more complex and can't be solved out of the box.
I have a scenario in a system with subscriptions where each subscription can have different levels. In the domain model, I only store the subscription level id, but in the view model I’d like that to be translated into the actual name for the level.
For simplicity, I’ve only included the relevant properties in the models and code below.
The default AutoMapper profile can handle all properties having the same name and type
SubscriptionLevelId property needs custom code to be mapped - a resolver. Here are two different approaches to how that can be solved.
Resolving a static list
If the list of subscription levels is contained in code and accessible through a synchronous call then you can add an AutoMapper resolver and inject the class containing the list directly into it.
Here’s the simplified service containing the levels.
We can now create a resolver and inject the service into it for object lookup
Finally, in the mapper profile we can now specify that the SubscriptionLevelId should be resolved using the new resolver.
Resolving an async service
So far so good. But what if your service makes an async call to database or cache to fetch a list of subscription levels? AutoMapper has no support for async calls due to the complexity that would bring to the usage of reflection and expression trees. You therefore have to solve this another way. My suggested solution is to inject the subscription level list when you do the mapping.
Injection of values is done by using the AutoMapper context. In the resolver, you can use the Items dictionary inside the context to send variables. In our solution, it could look like this:
When requesting the mapping you now have to pass along the list of subscription levels.
This works fine, but I’m not really happy with the way the list is passed in - it’s quite easy to forget to pass in the list and it’s also possible to misspell the key in the dictionary. To make it a bit more visible, you can add an extension method to the mapper that asks for a list when you try to do this specific mapping.
The extension is added to the
AutoMapper namespace so it’ll be available each time you use the
Map function. This is how you now can call the function, and it even comes up as one of the suggested ways.
AutoMapper is a really helpful tool when you need to map different data classes. The resolver makes it possible to convert complex scenarios and you can even inject services to use when converting. However, async is not supported in the resolver so you have to call async functions before and pass in the values you need.