Skip to content
Learni
Voir tous les tutoriels
.NET

Comment configurer AutoMapper en .NET en 2026

Read in English

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

terminal-init.sh
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

terminal-packages.sh
dotnet add package AutoMapper
dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection
dotnet add package AutoMapper.DependencyInjection
dotnet restore

Ces 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

Models.cs
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

MappingProfile.cs
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

Program.cs
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

CustomResolverProfile.cs
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

BidirectionalProfile.cs
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 IMapper via ctor, jamais new Mapper() pour thread-safety.
  • Performances : Utilisez ProjectTo() pour EF Core (évite materialization) ; profilez avec MapperConfiguration.AllowAdditiveProfileAssembly.
  • Tests : Mock IMapper avec Substitute.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 ; utilisez ConstructUsingServiceLocator().
  • Propriétés nullables : AllowNullCollections = true ou ForMember(..., opt => opt.Condition(src => src != null)).
  • AOT/trimming : En .NET native, ajoutez false ou [DynamicallyAccessedMembers] sur resolvers.

Pour aller plus loin