🔥 Pragmatic .NET Code Rules Course is on Presale - 40% off!BUY NOW

How and why I create my own mapper (avoid Automapper)?

May 27 2024

Sponsored

• Postman has brought a lot of new and great things with the new v11 version. For example, today, you can generate API documentation in a few clicks.

Read more about it here here.

• Dive deep into the React ecosystem and harness the power of modern development techniques. Learn everything from React fundamentals to mobile development with React Native.

Know more here.

Many thanks to the sponsors who make it possible for this newsletter to be free for readers. Become a sponsor.

Background

Automapper is a popular library in the .NET ecosystem that can help developers automate the mapping of data between different object models.

In the beginning I used Automapper constantly and it was a great replacement for the tedious work of typing mapping code. Until the moment when I encountered bigger projects where using Automapper only caused me big problems.

Here I will share my experiences and ways to replace Automapper.

Why am I not using Automapper?

• Poor code navigation experience When using the default configuration of Automapper to map objects, it can be difficult to trace where a particular field is getting its value from. Even with helpful tools like Visual Studio or Rider and using the "Find Usages" feature, it's not possible to locate the assignment or usage of that field.

• Hard to debug The problem here: fluent configuration. I cannot explain this better than: "Even if you provide mapping code within MapFrom<> method, you can’t put there a breakpoint and expect that program invocation stops when you call Mapper.Map<>() method. And if you have a bug in your mapping code you don’t get an exception in the place where you could potentially expect it." - Cezary Piatek

• Performance AutoMapper can impact the performance of your application, as it takes some time to load during project startup and when mapping between objects. However, in most cases, this should not cause any significant issues.

So what do I do?

I create my own mapper

Depending on the complexity of the project and what I want to achieve, I choose a certain way of implementing mapping. In the following, I will highlight a few general ways that I use regularly.

#1 Using reflection

If I don't have many properties to map, all property names that I need to map in both objects are identical, and performance is not the most important thing for me at the moment (although this is certainly faster than Automapper), I use simple reflection to map all properties.

C#
public class Mapper<TSource, TDestination>{ public TDestination Map(TSource source) { var destination = Activator.CreateInstance<TDestination>();  foreach (var sourceProperty in typeof(TSource).GetProperties()) { var destinationProperty = typeof(TDestination).GetProperty(sourceProperty.Name);  if (destinationProperty != null) { destinationProperty.SetValue(destination, sourceProperty.GetValue(source)); } }  return destination; }  public List<TDestination> Map(List<TSource> sourceList) { return sourceList.Select(source => Map(source)).ToList(); }}

Usage:

C#
var mapper = new Mapper<User, UserModel>();List<UserModel> userModels = mapper.Map(users);

Potential problem: The names of the properties in User and UserModel are different. The solution is to introduce projection by implementing the .Project() method that will enable such mapping.

#2 Specific Mapper Service

This is the most common way I implement mapping. For a certain entity/DTO, I'm creating a service class that I put in DI. Inside the service class, I implement both mappings. This way I have complete control over the mapping with tremendous ease in debugging and testing the code.

C#
public class UserMapperService{ public User MapToUser(UserModel userModel) { return new User { Id = userModel.Id, Address = userModel.Address, City = userModel.City, Email = userModel.Email, FirstName = userModel.FirstName, LastName = userModel.LastName, Password = userModel.Password, PostalCode = userModel.PostalCode, Region = userModel.Region }; }  public UserModel MapToUserModel(User user) { return new UserModel { Id = user.Id, Address = user.Address, City = user.City, Email = user.Email, FirstName = user.FirstName, LastName = user.LastName, Password = user.Password, PostalCode = user.PostalCode, Region = user.Region }; }}

Usage:

C#
UserModel userModel = _userMapperService.MapToUserModel(user);

Potential problem: Very boring and tedious work - manually writing property mappings. I have a solution for you: Mapping Generator (Visual Studio plugin) which is able to scaffold mapping code in design time. Mapper Generator in Visual Studio

#3 LINQ .Select() method

I use the .Select() method from EntityFramework mainly when I directly map entities from the domain/database to the DTO without any changes. In an ideal world, I believe there is nothing better than this mapping.

C#
List<User> users = _userRepository.GetUsers(); List<UserModel> userModels = users.Select(x => new UserModel { Address = x.Address, City = x.City, Email = x.City, FirstName = x.FirstName, Id = x.Id, LastName = x.LastName, Password = x.Password, PostalCode = x.PostalCode, Region = x.Region }).ToList();

Wrapping up

That's all from me for today.

Using AutoMapper is not wrong. Any of these ways has its pros and cons.

I used Automapper myself, but as the project got bigger and bigger, I realized that it was a serious maintenance problem.

These are some of the methods I use. Try it yourself. Make a coffee and check out source code directly on my GitHub repository.


Want to enforce clean code automatically? My Pragmatic .NET Code Rules course shows you how to set up analyzers, CI quality gates, and architecture tests - a production-ready system that keeps your codebase clean without manual reviews. Or grab the free Starter Kit to try it out.

About the Author

Stefan Djokic is a Microsoft MVP and senior .NET engineer with extensive experience designing enterprise-grade systems and teaching architectural best practices.

There are 3 ways I can help you:

1. Pragmatic .NET Code Rules Course

Stop arguing about code style. In this course you get a production-proven setup with analyzers, CI quality gates, and architecture tests — the exact system I use in real projects. Join here.

Not sure yet? Grab the free Starter Kit — a drop-in setup with the essentials from Module 01.

2. Design Patterns Ebooks

Design Patterns that Deliver — Solve real problems with 5 battle-tested patterns (Builder, Decorator, Strategy, Adapter, Mediator) using practical, real-world examples. Trusted by 650+ developers.

Just getting started? Design Patterns Simplified covers 10 essential patterns in a beginner-friendly, 30-page guide for just $9.95.

3. Join 20,000+ subscribers

Every Monday morning, I share 1 actionable tip on C#, .NET & Architecture that you can use right away. Join here.

Join 20,000+ subscribers who mass-improve their .NET skills with actionable tips on C#, Architecture & Best Practices.

Subscribe to
TheCodeMan.net

Subscribe to the TheCodeMan.net and be among the 20,000+ subscribers gaining practical tips and resources to enhance your .NET expertise.