DTO-Entity Conversion: Writing It by Hand or Using AutoMapper?
We discuss the conversion between entity and DTO. The upsides of manual mapping, when AutoMapper is a convenience and when a hidden cost, and a practical look at which to choose in your project.
In the seventh article we got to know DTOs and talked about why returning the entity directly is bad. In that article, we did the conversion from entity to DTO by hand, assigning each field one by one. It worked, but it was a bit tiring. Today we make this conversion our topic: should we keep writing it by hand, or switch to a tool like AutoMapper? The answer is a little more nuanced than you'd think.
Let's Recall the Problem
The same story repeats at every endpoint: you pull an entity from the database, then copy it into a DTO to return it to the outside. Or the opposite: you take an input DTO from the outside and convert it into an entity to write it to the database. Done by hand, it looks like this:
var dto = new UserDto
{
Id = user.Id,
Name = user.Name,
Email = user.Email
};
No problem for three fields. But if your entity has fifteen fields and you do this conversion in dozens of places in the application, it means you're writing the same tedious assignment code over and over. This is where AutoMapper comes in, with the promise of eliminating exactly this repetition.
What Does AutoMapper Promise?
AutoMapper is a library that does the conversion between two classes automatically for you. Its basic idea is this: if the field names in your entity are the same as the field names in your DTO, AutoMapper figures out on its own which field goes where. Instead of assigning one by one, you just say "convert this to that."
First you add the package to your project:
dotnet add package AutoMapper
Then you write a profile class that defines which conversions will exist. This is the way to tell AutoMapper "there's a transition from this type to that type":
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap();
CreateMap();
}
}
The CreateMap<User, UserDto>() here says "conversion from User to UserDto is possible." As long as the field names match, you don't need to write anything else; AutoMapper resolves the mapping on its own. Now the conversion inside your service comes down to a single line:
var dto = _mapper.Map(user);
A three-line assignment came down to one big Map call. You can imagine how much bigger this difference gets on an entity with fifteen fields. At first glance AutoMapper seems to win by a wide margin. But there's another side to the coin.
AutoMapper's Hidden Cost
AutoMapper reduces lines of code, that's true. But it brings something along with it: magic. The conversion no longer stands before your eyes as open code; it happens inside a profile and a library, with hidden rules. In small projects this isn't a problem. But when things get tangled, a few disadvantages appear.
First, debugging becomes harder. If a field arrives unexpectedly empty, you'd see the reason instantly in hand-written code; in AutoMapper, you have to figure out what maps to what by knowing the library's behavior. Second, silent breaks. You add a new field to your entity but forget to add it to your DTO; AutoMapper often glosses over this silently, and you only notice the problem at runtime. In hand-written code, such a field would have caught your eye.
Third, the readability illusion. AutoMapper shortens the code but doesn't always make it understandable. Someone newly joining the project has to open the profile file and picture the library's rules in their mind to see what exactly the conversion does. Whereas a hand-written assignment reads plainly, as is.
Is Manual Mapping Really That Bad?
No, in fact it's often not bad at all. The greatest virtue of hand-written conversion is transparency: you see exactly what's happening, there's no magic. Which field goes where stands plainly in the code, debugging is easy, and a new developer understands at a glance. In modern C#, there are ways to tidy up this code even further; for example, you can add the conversion to the entity as a method:
public UserDto ToDto()
{
return new UserDto
{
Id = this.Id,
Name = this.Name,
Email = this.Email
};
}
This way the conversion logic gathers in a single place, and where you call it you just write user.ToDto(). You get both the tidiness AutoMapper provides and transparent code, far from magic. This approach is a practical middle ground that combines the good sides of both worlds.
So Which One Should I Choose?
The decision depends on the project's scale and the team's habit. AutoMapper saves serious time in large projects where entities are big, the number of fields is high, and conversions repeat a lot. If your team is used to this tool, consistency is a reason in itself.
But in a small-to-medium project, or if you're in the learning phase, I recommend starting with manual mapping. Seeing with your own eyes what goes where settles the conversion logic much more firmly. Before relying on a magical tool, you need to understand what that tool does in your place. When the need genuinely arises, that is, when repetitive code starts tiring you, you switch to AutoMapper. Don't rush to add it from the start.
A Small Experiment
First write the entity-to-DTO conversion by hand, like in the seventh article. Then move the same conversion into a ToDto method you add to the entity, and use user.ToDto() where you call it. See how the calling side of the code simplifies. If you like, take one more step, set up AutoMapper, and do the same job with it too; when you place the three approaches side by side, you'll decide for yourself which is more readable for you. This comparison answers the "which is better?" question in the context of your own project.
In the next article we'll cover dependency injection in depth: we'll talk about the lifetimes we use when registering services (singleton, scoped, transient) and why they matter so much. The services we've created so far are exactly the examples we need to make those concepts concrete.