I am not the biggest fan of AutoMapper. It starts with good intentions but often ends up being a big mess. I have seen it used in many projects, and the configuration of the mappings is often scattered all over the place, and or they are huge!
I know that many are using AutoMapper and are happy with it, but I have some major issues with it. I wrote down my opinion almost exactly a year ago, and nothing really changed:
[...] mapping is often times tedious and most often considered boilerplate code. And that is a good thing! That means it is well-known and easier to understand. If you use libraries like AutoMapper, you still have this boilerplate... but now somewhere else, with more complexity.
It always starts easy, and you have direct one-to-one mappings. But as the product grows, there are exceptions to that. Often times I discovered that people then stick to the library and start configuring it. So now, instead of having obvious mapping code, you have the mapper and, at some place, some configuration, and often the configuration is way more complex than doing everything by hand.
Also consider that you introduced a new 3rd party library in your project you will have to support and maintain. Oh yeah, you might also consider performance regression. But to be honest, where this is absolutely true, it often doesn't matter at all. Yes using reflection is slower than mapping those simple properties by hand, but one call to a database or Web API is magnitude 2 to 3 slower, so that doesn't count in most cases. All in all, the steadily increasing complexity of such approaches is the culprit for me. If you have a super simple mapping that you don't have to configure the library, why not do this on your own? And if it is complex, usually such libraries are more complex than doing it via hand.
But there is a world between AutoMapper and manually mapping everything. This is where SourceGenerator comes into play. Basically source generators are a way to generate code at compile time. You can imagine that mapping logic is a prime target for this. Both sides of the equation (source and target) are mostly known at compile time, so why not generate the mapping code?
A famous library is Mapperly. The basic code would look like this:
var personDto = new PersonDto(1, "Steven");
var person = personDto.PersonDtoToPerson();
Console.WriteLine(person.Id);
Console.WriteLine(person.FullName);
[Mapper]
public static partial class PersonMapper
{
public static partial Person PersonDtoToPerson(this PersonDto dto);
}
public class Person
{
public int Id { get; set; }
public string FullName { get; set; }
}
public record PersonDto(int Id, string FullName);
Of course there are many other cases where you want a class you put in the DI container, and yes that works. I don't want to go too much into details here, just checkout the repository I linked above. My point goes into a different direction. The advantage of this approach is that you can have compile-time errors. And yes, that is a big deal for me (one reason I don't like AutoMapper).
Another one is related to that: As this is a source generator, you can easily copy&paste the code and make it your own! That has the advantage, that even if the library isn't maintained anymore, you have zero trouble to resolve the issue, as you can easily use the generated code and make it your own! That also includes cases where you might have an easier life writing that custom code than configuring a third party library!
So all in all, if you want to go down that route, I do feel that is something that gives your the "best" out of both worlds.