Mapping cu AutoMapper între DTO și Model în .NET

  • Doru Bulubasa
  • 23 July 2025

Într-o aplicație ASP.NET modernă, separarea clară între ceea ce vine din exterior (DTO – Data Transfer Object) și modelele noastre de domeniu (Domain Models) este o bună practică esențială. Această separare permite validare, control al datelor expuse și respectarea principiilor DDD (Domain-Driven Design).

🎯 Obiectiv

Să folosim AutoMapper pentru a transforma:

  • CreatePostDtoPost – în cazul comenzilor (scriere),

  • PostPostDto – în cazul răspunsurilor API (citire).


🧱 Structura claselor

// DTO primit de la frontend pentru creare articol
public class CreatePostDto
{
    public string Title { get; set; } = string.Empty;
    public string Content { get; set; } = string.Empty;
}

// Modelul de domeniu
public class Post
{
    public Guid Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public string Content { get; set; } = string.Empty;
    public DateTime CreatedAt { get; set; }
}

// DTO returnat în răspunsurile API
public class PostDto
{
    public Guid Id { get; set; }
    public string Title { get; set; } = string.Empty;
    public string Snippet { get; set; } = string.Empty;
}


🔁 Configurarea AutoMapper

Instalează pachetul:

dotnet add package AutoMapper.Extensions.Microsoft.DependencyInjection

✅ Profilul de mapare

public class PostMappingProfile : Profile

{

    public PostMappingProfile()

    {

        CreateMap<CreatePostDto, Post>()

            .ForMember(dest => dest.Id, opt => opt.Ignore())

            .ForMember(dest => dest.CreatedAt, opt => opt.MapFrom(_ => DateTime.UtcNow));


        CreateMap<Post, PostDto>()

            .ForMember(dest => dest.Snippet, opt => opt.MapFrom(src => src.Content.Substring(0, Math.Min(src.Content.Length, 100))));

    }

}

🧩 Înregistrarea în containerul DI

builder.Services.AddAutoMapper(typeof(PostMappingProfile));


🛠️ Utilizarea în CommandHandler

public class CreatePostCommandHandler : IRequestHandler<CreatePostCommand, Guid>

{

    private readonly IMapper _mapper;

    private readonly IPostRepository _repository;

    public CreatePostCommandHandler(IMapper mapper, IPostRepository repository)

    {

        _mapper = mapper;

        _repository = repository;

    }

    public async Task<Guid> Handle(CreatePostCommand request, CancellationToken cancellationToken)

    {

        var post = _mapper.Map<Post>(request.Dto);

        await _repository.AddAsync(post);

        return post.Id;

    }

}


📤 Utilizarea la întoarcerea datelor (queries/API)

[HttpGet("{id}")]

public async Task<ActionResult<PostDto>> GetById(Guid id)

{

    var post = await _repository.GetByIdAsync(id);

    if (post is null) return NotFound();

    return _mapper.Map<PostDto>(post);

}


🧠 Concluzii

  • AutoMapper elimină maparea manuală, menținând codul curat și scalabil.

  • Ne permite să definim clar ce date acceptăm și ce date expunem.

  • Ne asigurăm că modelele noastre de domeniu rămân independente de structura API-ului.

Scrie un comentariu

Adresa de mail nu va fi publicata. Campurile obligatorii sunt marcate cu *