Introduction
AutoMapper est une bibliothèque .NET mature qui automatise le mapping entre objets, éliminant les boilerplates manuels fastidieux. En 2026, avec .NET 10 et ses optimisations AOT, elle reste indispensable pour les APIs REST, les microservices et les applications Blazor, réduisant le code de 70 % en moyenne selon les benchmarks officiels.
Pourquoi l'utiliser ? Imaginez mapper un DTO vers une entité : sans AutoMapper, 20 lignes de code répétitif ; avec, une ligne fluide. Ce tutoriel intermédiaire couvre la configuration complète dans un projet console extensible à ASP.NET Core, des mappings basiques aux résolveurs custom, en passant par l'injection de dépendances. À la fin, vous maîtriserez les performances et éviterez les pièges courants pour des apps scalables. Prêt à booster votre productivité ? (128 mots)
Prérequis
- .NET SDK 10.0 ou supérieur (vérifiez avec
dotnet --version) - Éditeur comme Visual Studio 2022 ou VS Code avec C# extension
- Connaissances de base en C# (classes, LINQ) et DI (.NET)
- Terminal (PowerShell ou bash)
Créer le projet console
dotnet new console -o AutoMapperDemo --framework net10.0
cd AutoMapperDemo
code .Cette commande crée un projet console .NET 10 minimal, navigue dedans et ouvre VS Code. Elle pose les fondations pour tester AutoMapper sans complexité superflue, en ciblant le framework LTS 2026.
Installer AutoMapper
Ajoutez les packages NuGet essentiels. AutoMapper core gère les mappings ; Extensions.Microsoft.DependencyInjection intègre la DI native .NET pour une configuration fluide en production.
Ajouter les packages NuGet
dotnet add package AutoMapper
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
dotnet add package AutoMapper.DependencyInjection
dotnet restoreCes packages installent AutoMapper (5.0+ en 2026) avec support DI. restore synchronise ; évitez les versions preview pour la stabilité AOT.
Définir les modèles source et destination
namespace AutoMapperDemo;
public class User
{
public int Id { get; set; }
public string FirstName { get; set; } = string.Empty;
public string LastName { get; set; } = string.Empty;
public DateTime BirthDate { get; set; }
public List<string> Hobbies { get; set; } = new();
}
public class UserDto
{
public int Id { get; set; }
public string FullName { get; set; } = string.Empty;
public int Age { get; set; }
public string[] Hobbies { get; set; } = Array.Empty<string>();
}Ces classes représentent une entité User (source DB) et un DTO UserDto (API). Notez les propriétés non alignées : FullName/Age nécessiteront un profil custom pour éviter les erreurs de mapping.
Créer le profil de mapping
Les profils centralisent la configuration comme un blueprint. Ils étendent Profile et utilisent CreateMap pour des règles déclaratives, rendant le code testable et maintenable.
Implémenter MappingProfile
using AutoMapper;
namespace AutoMapperDemo;
public class MappingProfile : Profile
{
public MappingProfile()
{
CreateMap<User, UserDto>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.FirstName + " " + src.LastName))
.ForMember(dest => dest.Age, opt => opt.MapFrom(src => DateTime.Now.Year - src.BirthDate.Year))
.ForMember(dest => dest.Hobbies, opt => opt.MapFrom(src => src.Hobbies.ToArray()));
}
}Ce profil mappe User vers UserDto avec ForMember pour les propriétés custom : concaténation, calcul d'âge (approximatif, affinez en prod), et conversion List vers array. L'ordre des règles compte pour les dépendances.
Configurer la DI et mapper
Intégrez AutoMapper via IServiceCollection comme un service singleton. Utilisez MapperConfiguration pour valider au démarrage, évitant les runtime errors.
Mettre à jour Program.cs avec DI
using AutoMapper;
using AutoMapper.Extensions.Microsoft.DependencyInjection;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace AutoMapperDemo;
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddAutoMapper(typeof(MappingProfile));
builder.Services.AddSingleton(provider =>
{
var config = new MapperConfiguration(cfg => cfg.AddProfile<MappingProfile>());
config.AssertConfigurationIsValid();
return config.CreateMapper();
});
var host = builder.Build();
var mapper = host.Services.GetRequiredService<IMapper>();
var user = new User
{
Id = 1,
FirstName = "Jean",
LastName = "Dupont",
BirthDate = new DateTime(1990, 5, 15),
Hobbies = new List<string> { "Lecture", "Natation" }
};
var dto = mapper.Map<UserDto>(user);
Console.WriteLine($"FullName: {dto.FullName}, Age: {dto.Age}, Hobbies: {string.Join(", ", dto.Hobbies)}");
await host.RunAsync();Ce Program.cs moderne (.NET 10 top-level) configure DI avec AddAutoMapper, valide la config via AssertConfigurationIsValid(), et teste le mapping. Sortie : "Jean Dupont, 36, Lecture, Natation". Scalable vers web API.
Mapping avancé avec résolveur custom
using AutoMapper;
namespace AutoMapperDemo;
public class AgeResolver : IValueResolver<User, UserDto, int>
{
public int Resolve(User source, UserDto destination, int destMember, ResolutionContext context)
{
var age = DateTime.Now.Year - source.BirthDate.Year;
if (DateTime.Now < source.BirthDate.AddYears(age)) age--;
return age;
}
}
public class AdvancedMappingProfile : Profile
{
public AdvancedMappingProfile()
{
CreateMap<User, UserDto>()
.ForMember(dest => dest.FullName, opt => opt.MapFrom(src => src.FirstName + " " + src.LastName))
.ForMember(dest => dest.Age, opt => opt.MapFrom<AgeResolver>())
.ForMember(dest => dest.Hobbies, opt => opt.Ignore()); // Ignorer pour test
}
}Introduit un IValueResolver précis pour l'âge (gère anniversaire). Ignore() skippe Hobbies. Remplacez dans Program.cs par AddProfile pour précision accrue, idéal pour business logic complexe.
Mapping bidirectionnel et collections
using AutoMapper;
namespace AutoMapperDemo;
public class BidirectionalProfile : Profile
{
public BidirectionalProfile()
{
CreateMap<User, UserDto>().ReverseMap();
CreateMap<List<User>, List<UserDto>>().ReverseMap();
}
}
// Usage exemple dans Program.cs :
// var users = new List<User> { user1, user2 };
// var dtos = mapper.Map<List<UserDto>>(users);
// var usersBack = mapper.Map<List<User>>(dtos);.ReverseMap() active le mapping inverse en un appel. Pour collections, mappez List explicitement. Pitfall : propriétés non assignables (ex: computed) nécessitent DoNotValidate() ou custom resolvers.
Bonnes pratiques
- Validez toujours :
AssertConfigurationIsValid()au boot pour catcher les erreurs early. - Profils par feature : Un Profile par bounded context (ex: UsersProfile, OrdersProfile) pour scalabilité.
- DI everywhere : Injectez
IMappervia ctor, jamaisnew Mapper()pour thread-safety. - Performances : Utilisez
ProjectTo()pour EF Core (évite materialization) ; profilez avecMapperConfiguration.AllowAdditiveProfileAssembly. - Tests : Mock
IMapperavecSubstitute.For(NSubstitute) pour unit tests.()
Erreurs courantes à éviter
- Oubli de DI :
new MapperConfiguration()sans services cause singletons non-partagés et fuites mémoire. - Mappings circulaires : Sans
DisableCircularReferenceHandling(), stack overflow ; utilisezConstructUsingServiceLocator(). - Propriétés nullables :
AllowNullCollections = trueouForMember(..., opt => opt.Condition(src => src != null)). - AOT/trimming : En .NET native, ajoutez
oufalse [DynamicallyAccessedMembers]sur resolvers.
Pour aller plus loin
- Docs officielles : AutoMapper GitHub
- Vidéo : Performance tuning
- Avancé : Intégrez avec EF Core
ProjectTo()pour queries optimisées. - Formations : Découvrez nos formations .NET avancées chez Learni pour maîtriser DDD + AutoMapper en microservices.