Anthony Steele

Bloggy

AutoMapper considered harmful

TL:DR: You don’t need it. Just write the mapping code.

Why mapping

Consider a simple .NET data API. How many different kinds of Data Transfer Object do you need? In a simple example, there has to be a class that maps to the data store - the “data model”, and there has to be a class that is serialized as the HTTP response - the “view model”.

I view the data model complexity as part of how complex the code needs to be. Right after the File|New Project, the data model and the view model might be the same type, call it CustomerModel. But as the project grows in size and complexity, it is very likely that you will want to separate these two concerns. e.g. When some fields on the data model are not exposed to the web, or some fields on the view model are computed or come from another source, and not stored in the data model. So at that point you split CustomerDataModel from CustomerViewModel, and now you have to map between them.

Why AutoMapper

AutoMapper automates this task of mapping. It is a popular tool in the .NET space. I have seen pull request reviews where someone with the audacity to manually map four or five fields from one object to another is quickly told “you must use AutoMapper for this!”. This common idea is IMHO not just wrong (using AutoMapper is a choice), it is harmful.

There are pros and cons to every design choice. People so often see the upsides of AutoMapper and not the downsides, but of course they do exist.

With AutoMapper, the selling point is that you don’t have to write boilerplate “boring mapping code”.

But now you have another NuGet package to keep up to date, the IMapper to configure and inject, so there are more moving parts. But these are side issues to the main objections.

Mapping code is not worth abstracting

If your AutoMapper mappings are trivial, why not map with trivial code? It’s dumb code, but I like dumb code for dumb tasks. It is easy to read, easy to test, hard to get wrong, and has no dependencies that need updates. The example is given in the Example section below.

If your AutoMapper mappings are not trivial - then that configuration in the AutoMapper DSL is the mapping code and now it’s just hidden and more complex due to extra layers of indirection - would you rather program in AutoMapper DSL or in plain old C#?

Let me repeat that:

Mappings in dumb code is Boring Technology. AutoMapper is Clever code. Clever code doesn’t impress me. Boring code with zero dependencies does.

AutoMapper will attract hidden business logic

AutoMapper is supposedly transparent. It’s pretty clear what it should be doing in e.g. var viewModel = mapper.Map<CustomerViewModel>(dataModel);.

Emphasis on should. It’s completely hiding the details of what it is actually doing in the mapping. I have run into confusion and complexity on occasion here. It would be nice if the mapping was straightforward, but often it isn’t. And the mapping is “non-local” - i.e. not easy to step into or find from the call site.

AutoMapper configuration tends to attract business logic. I have seen significant facts about how the business logic works, embedded in AutoMapper field mappings, which is not the place for them.

When I have to maintain a “mature” codebase that has been worked on for several years, and I see that it uses AutoMapper, my heart sinks a bit because I know that there are almost always going to be difficulties hidden therein. I know that they should not be difficulties of business logic in the mapping. And yet there usually are. It attracts trouble rather than being the “pit of success”.

Testing

Did you know that there is a method AssertConfigurationIsValid for testing AutoMapper configurations? It “checks to make sure that every single Destination type member has a corresponding type member on the source” which is one kind of error. I did not know about it either: I never see it used. Yet it’s best practice that you should always test AutoMapper configurations wity this. But here’s nothing forcing use of it.

How should you test a class that uses an IMapper? The obvious idea is to mock the IMapper and the response from it. But this means that your test mocks are more complex, the test doesn’t reflect an actual run, and the mapping logic does not get tested.

Libraries to solve known problems

AutoMapper solves a problem. But it’s a trivial problem that doesn’t take a library to solve. I tend to prefer libraries (e.g. a JSON Serializer, or SQL mapper) when:

When to Use AutoMapper

The author, Jimmy Bogard says:

I set out to build a tool that:

  • Enforced a convention for destination types
  • Removed all those null reference exceptions
  • Made it super easy to test

AutoMapper works because it enforces a convention. It assumes that your destination types are a subset of the source type. It assumes that everything on your destination type is meant to be mapped. It assumes that the destination member names follow the exact name of the source type. With AutoMapper, we could enforce our view model design philosophy. This is the true power of conventions - laying down a set of enforceable design rules that help you streamline development along the way.

So there is a better case for AutoMapper when the mapping is is “wide but shallow” i.e. lots of fields to map, but the names map exactly without complex configuration. The value add seems to be in object flattening and null handling - but this might not be so important any more, since modern c# has several new tricks to deal with nulls.

It also is key if you can enforce the convention that view model member names match. I am in favour of names matching as much as possible anyway, simply because it is less unnecessary complexity to keep in mind. But when they do easily match, there is a better case for AutoMapper. Lets consider when that’s easy and when it is not:

If you find yourself hating a tool, it’s important to ask - for what problems was this tool designed to solve? And if those problems are different than yours, perhaps that tool isn’t a good fit.

I don’t disagree with that, but I think it’s a corollary the cases at which AutoMapper excels are a small subset of the real-world uses that it is put to. And that the required discipline to make the usage of it transparent by sorting out the underlying mismatches is seldom present, because there’s nothing enforcing that discipline other than higher maintenance costs later on.

Example

You should be able to code and test like this code below:

public class CustomerDataModel
{
    public string Name { get; set; }
    public DateTimeOffset Start { get; set; }
}

public class CustomerViewModel
{
    public string Name { get; set; }
    public DateTimeOffset Start { get; set; }
}

public static class CustomerMapper
{
    public static CustomerViewModel Map(CustomerDataModel dataModel)
    {
        return new CustomerViewModel
        {
            Name = dataModel.Name,
            Start = dataModel.Start
        };
    }
}

CustomerMapper.Map is a “Pure function”, i.e output depends only on input, and there is no “side effect”, no other state either accessed or altered by it. So it is easily testable, if you feel the need.

You can make CustomerMapper.Map an extension method as CustomerViewModel Map(this CustomerDataModel dataModel) if you want. You can pull it out to a separate namespace to both the Data Model and View Model if you want. It can be coupled or decoupled as you need.

Horses for courses

So you disagree, and like AutoMapper, and we can do different things, what’s the problem? The issue comes when I have to maintain code damaged by AutoMapper. Or when lack of AutoMapper is assumed to be a defect and not a virtue.

Or when a Junior developer says

I’ve seen a lot of logic stuffed into AutoMapper. It becomes this weird magic thing that is very hard to understand. Especially as a junior dev.

The next person to review a Pull Request with the suggestion that “you must use AutoMapper for this!” will be directed to this essay, in the hope that they might learn something. AutoMapper is very much optional.