AutoMapper — DTO ↔ Entity mapping, Profile, ProjectTo

~20 daq · .NET Backend · Ilg'or ASP.NET Core

Muammodan boshlaylik

DTO lar kerak (45-dars) — lekin har bir entity uchun qo'lda nusxalash:

var dto = new MahsulotDto
{
    Id = m.Id,
    Nomi = m.Nomi,
    Narx = m.Narx,
    Tavsif = m.Tavsif,
    Qoldiq = m.Qoldiq,
    KategoriyaNomi = m.Kategoriya.Nomi,
    // ... yana 12 ta maydon
};

Bu kod 5 ta joyda takrorlanadi. Entity ga yangi maydon qo'shilsa — hamma joyni topib yangilash kerak; bitta joyda unutilsa — API bo'sh qiymat qaytaradi.


AutoMapper nima?

AutoMapper — ob'ektlarni nomlar bo'yicha avtomatik nusxalaydigan kutubxona. Bir xil nomdagi xususiyatlar o'zi ko'chiriladi, farqlilari — bir marta sozlanadi.

dotnet add package AutoMapper

[!INFO] AutoMapper 15-versiyadan boshlab tijoriy litsenziyaga o'tgan (kichik kompaniyalar va notijorat loyihalar uchun bepul "Community" litsenziya bor, kalit talab qilinadi). Eski 14.x versiyasi MIT litsenziyasida qoladi. Loyihaga qo'shishdan oldin litsenziya shartlarini tekshiring — shuning uchun quyida muqobillar ham ko'rsatilgan.


Hayotiy o'xshatma

Ko'chish paytidagi yuk tashuvchilar. Siz har bir buyumni qaysi xonaga qo'yishni alohida aytmaysiz — qutilarda "oshxona", "yotoqxona" deb yozilgan, va ular bir xil nomli xonaga qo'yiladi. Faqat istisnolarni aytasiz: "katta ko'zgu — mehmonxonaga emas, dahlizga".


Profil — sozlamalar

using AutoMapper;

public class MahsulotProfili : Profile
{
    public MahsulotProfili()
    {
        // Entity → DTO
        CreateMap<Mahsulot, MahsulotDto>()
            .ForMember(d => d.KategoriyaNomi, o => o.MapFrom(s => s.Kategoriya.Nomi))
            .ForMember(d => d.Mavjud, o => o.MapFrom(s => s.Qoldiq > 0));

        // DTO → Entity
        CreateMap<MahsulotYaratDto, Mahsulot>()
            .ForMember(d => d.Id, o => o.Ignore())
            .ForMember(d => d.Yaratilgan, o => o.Ignore());
    }
}

Flattening — KategoriyaNomi nomi Kategoriya.Nomi ga mos keladi, shuning uchun aslida ForMember siz ham avtomatik ishlaydi.

Ro'yxatdan o'tkazish

builder.Services.AddAutoMapper(cfg => { }, typeof(MahsulotProfili));

Berilgan assembly dagi barcha Profile klasslari topiladi.


Ishlatish

public class MahsulotService(DokonDbContext db, IMapper mapper)
{
    public async Task<MahsulotDto?> OlAsync(int id, CancellationToken ct)
    {
        var m = await db.Mahsulotlar.Include(x => x.Kategoriya).FirstOrDefaultAsync(x => x.Id == id, ct);
        return m is null ? null : mapper.Map<MahsulotDto>(m);
    }

    public async Task<MahsulotDto> YaratAsync(MahsulotYaratDto dto, CancellationToken ct)
    {
        var m = mapper.Map<Mahsulot>(dto);
        db.Mahsulotlar.Add(m);
        await db.SaveChangesAsync(ct);
        return mapper.Map<MahsulotDto>(m);
    }

    public async Task YangilaAsync(int id, MahsulotYangilaDto dto, CancellationToken ct)
    {
        var m = await db.Mahsulotlar.FindAsync([id], ct) ?? throw new TopilmadiException(id);
        mapper.Map(dto, m);                 // mavjud ob'ektga nusxalash
        await db.SaveChangesAsync(ct);
    }
}

ProjectTo — so'rov darajasida

using AutoMapper.QueryableExtensions;

var royxat = await db.Mahsulotlar
    .ProjectTo<MahsulotDto>(mapper.ConfigurationProvider)
    .ToListAsync(ct);

ProjectTo mapping ni Select ga aylantiradi — bazadan faqat kerakli ustunlar olinadi, Include shart emas (53-dars, projection).


Konfiguratsiyani tekshirish

[Fact]
public void AutoMapper_konfiguratsiyasi_togri()
{
    var config = new MapperConfiguration(c => c.AddProfile<MahsulotProfili>(), NullLoggerFactory.Instance);
    config.AssertConfigurationIsValid();     // mos kelmagan maydon bo'lsa — xato
}

Bu test DTO ga yangi maydon qo'shilib, mapping unutilganini darhol ko'rsatadi.


Muqobillar

Qo'lda — kengaytma metodlar

public static class MahsulotMapping
{
    public static MahsulotDto ToDto(this Mahsulot m) =>
        new(m.Id, m.Nomi, m.Narx, m.Kategoriya.Nomi, m.Qoldiq > 0);
}

var dto = m.ToDto();

Oddiy, tez, kompilyator tekshiradi, "sehr" yo'q. Kichik va o'rta loyihalar uchun ko'pincha eng yaxshi tanlov.

Mapperly — source generator

dotnet add package Riok.Mapperly
[Mapper]
public partial class MahsulotMapper
{
    public partial MahsulotDto ToDto(Mahsulot m);
}

Mapping kodi kompilyatsiya vaqtida generatsiya qilinadi — reflection yo'q, qo'lda yozilgandek tez; mos kelmagan maydonlar — kompilyator ogohlantirishi.

AutoMapper Qo'lda Mapperly
Kod hajmi Kam Ko'p Kam
Xatolar aniqlanishi Runtime / test Kompilyatsiya Kompilyatsiya
Tezlik Reflection/expression Eng tez Eng tez
Debug Qiyinroq Oson Oson (generatsiya qilingan kod)
Litsenziya v15+ tijoriy — MIT

[!TIP] Yangi loyihada: oddiy holatlarda — qo'lda yozilgan ToDto() kengaytmalari; ko'p DTO li katta loyihada — Mapperly. AutoMapper — u allaqachon ishlatilgan loyihalarda va ProjectTo qulayligi juda kerak bo'lganda.


Tez-tez uchraydigan xatolar

Mapping ichida biznes mantiq (MapFrom(s => s.Narx * kurs.Ol())) — yashirin, test qilish qiyin; mapping faqat nusxalash uchun.

DTO → Entity da Id yoki maxfiy maydonlar ustidan yozish — Ignore() yoki alohida yaratish DTO si.

Include siz Map — navigatsiya null, NullReferenceException; yoki ProjectTo.


Xulosa

AutoMapper — nomlar bo'yicha avtomatik nusxalash: Profile da CreateMap<Manba, Maqsad>(), farqlilar — ForMember(... MapFrom ...), keraksizlar — Ignore(). AddAutoMapper, IMapper.Map<T>(), mavjud ob'ektga Map(dto, entity); ProjectTo — so'rov darajasida faqat kerakli ustunlar. AssertConfigurationIsValid — testda.

v15+ tijoriy litsenziya. Muqobillar: qo'lda ToDto() kengaytmalari (oddiy, kompilyator tekshiradi) va Mapperly (source generator, tez). Mapping da biznes mantiq bo'lmasin.


Keyingi dars

65-dars: Global Exception Handling — IExceptionHandler, ProblemDetails