DTO — nima uchun, Entity dan ajratish, request/response DTO

~20 daq · .NET Backend · Web API — Controller va Routing

Muammodan boshlaylik

EF Core entity sini to'g'ridan-to'g'ri qaytarish:

public class Foydalanuvchi
{
    public int Id { get; set; }
    public string Email { get; set; } = "";
    public string ParolXesh { get; set; } = "";
    public string Rol { get; set; } = "user";
    public bool Bloklangan { get; set; }
    public List<Buyurtma> Buyurtmalar { get; set; } = new();
}

[HttpGet("{id}")]
public async Task<Foydalanuvchi?> Ol(int id) => await db.Foydalanuvchilar.FindAsync(id);

[HttpPost]
public async Task<IActionResult> Yarat(Foydalanuvchi f) { db.Add(f); await db.SaveChangesAsync(); return Ok(f); }

Muammolar:

  1. Javobda ParolXesh — hamma ko'radi
  2. Ro'yxatdan o'tishda mijoz { "email": "...", "rol": "admin" } yuboradi — va admin bo'ladi (over-posting)
  3. Buyurtmalar → har bir buyurtmada Foydalanuvchi → yana Buyurtmalar... — JSON da cheksiz sikl
  4. Bazadagi ustun nomini o'zgartirsangiz — API javobi ham o'zgaradi va mobil ilova buziladi

DTO nima?

DTO (Data Transfer Object) — faqat ma'lumot tashish uchun mo'ljallangan oddiy ob'ekt: API ga nima kirishi va nima chiqishi aniq belgilanadi.

// kiruvchi — ro'yxatdan o'tish
public record RoyxatDto(string Email, string Parol, string Ism);

// chiquvchi — profil
public record FoydalanuvchiDto(int Id, string Email, string Ism);
[HttpPost]
public async Task<ActionResult<FoydalanuvchiDto>> Royxat(RoyxatDto dto)
{
    var f = new Foydalanuvchi
    {
        Email = dto.Email.Trim().ToLowerInvariant(),
        Ism = dto.Ism,
        ParolXesh = BCrypt.Net.BCrypt.HashPassword(dto.Parol),
        Rol = "user",                          // server o'zi belgilaydi
    };
    db.Foydalanuvchilar.Add(f);
    await db.SaveChangesAsync();

    return CreatedAtAction(nameof(Ol), new { id = f.Id }, new FoydalanuvchiDto(f.Id, f.Email, f.Ism));
}
  • Mijoz Rol ni yubora olmaydi — DTO da bunday maydon yo'q
  • Javobda ParolXesh yo'q
  • Entity o'zgarsa ham — API shartnomasi (DTO) o'zgarmaydi

Hayotiy o'xshatma

Bank hujjatlari. Bankning ichki kartotekasida mijoz haqida hamma narsa bor: kredit tarixi, ichki baholar, xodimlar izohlari (entity). Mijozga beriladigan hisob ko'chirmasida esa faqat unga kerakli va ruxsat etilgan ma'lumot (response DTO). Kredit arizasi — bankka faqat belgilangan maydonlar bilan keladi (request DTO); mijoz arizada "foiz stavkasi: 0%" deb o'zi yoza olmaydi.


Request va response DTO lari

Bitta entity — har xil vaziyat uchun har xil DTO:

public record MahsulotYaratDto(string Nomi, decimal Narx, int Qoldiq, int KategoriyaId);
public record MahsulotYangilaDto(string Nomi, decimal Narx, int Qoldiq);
public record MahsulotDto(int Id, string Nomi, decimal Narx, bool Mavjud, string Kategoriya);
public record MahsulotQisqaDto(int Id, string Nomi, decimal Narx);       // ro'yxat uchun
public record MahsulotBatafsilDto(int Id, string Nomi, decimal Narx, string Tavsif,
    IReadOnlyList<string> Rasmlar, double Reyting);                        // sahifa uchun
Tur Vazifa Maydonlar
Create POST body Mijoz kiritadigan maydonlar; Id yo'q
Update PUT/PATCH body O'zgartirish mumkin bo'lganlar
Response Javob Ochiq ma'lumot; hisoblangan maydonlar (Mavjud), bog'liq nomlar
List item Ro'yxat Minimal maydonlar — kichik JSON

record — DTO lar uchun ideal: qisqa, o'zgarmas (25-dars).


Mapping — Entity ↔ DTO

Qo'lda

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

    public static Mahsulot ToEntity(this MahsulotYaratDto d) =>
        new() { Nomi = d.Nomi.Trim(), Narx = d.Narx, Qoldiq = d.Qoldiq, KategoriyaId = d.KategoriyaId };
}

var dto = mahsulot.ToDto();

Extension metodlar (31-dars) — aniq, tez, kompilyator tekshiradi.

EF Core so'rovida — Select bilan

var royxat = await db.Mahsulotlar
    .Where(m => m.Qoldiq > 0)
    .Select(m => new MahsulotQisqaDto(m.Id, m.Nomi, m.Narx))
    .ToListAsync(ct);

Bazadan faqat kerakli ustunlar olinadi (SELECT id, nomi, narx) — butun entity va uning bog'liqliklari emas. Eng samarali usul (53-dars).

Kutubxonalar

AutoMapper, Mapster — konvensiya bo'yicha avtomatik mapping (64-dars). Ko'p DTO li katta loyihalarda qulay, lekin "sehrli" — xatolar faqat ishlash paytida ko'rinadi.

[!TIP] Kichik va o'rta loyihalarda qo'lda mapping (extension metodlar yoki Select) ko'pincha yaxshiroq: aniq, debug oson, va yangi maydon qo'shilganda nima o'zgarishi ko'rinib turadi.


DTO va validatsiya

Validatsiya atributlari — request DTO larga (entity ga emas):

public record RoyxatDto(
    [Required, EmailAddress] string Email,
    [Required, MinLength(8)] string Parol,
    [Required, StringLength(50, MinimumLength = 2)] string Ism);

46–47-darslar.


Papka tuzilishi

DokonApi/
├── Entities/          ← EF Core modellari
│   └── Mahsulot.cs
├── Dtos/
│   └── Mahsulotlar/
│       ├── MahsulotYaratDto.cs
│       ├── MahsulotYangilaDto.cs
│       └── MahsulotDto.cs
└── Mapping/
    └── MahsulotMapping.cs

[!WARNING] Hech qachon entity ni to'g'ridan-to'g'ri [FromBody] sifatida qabul qilmang va javob sifatida qaytarmang. Bu — maxfiy maydonlar sizib chiqishi, over-posting orqali huquqlarni oshirish va JSON siklik havolalar xatolarining eng keng tarqalgan sababi.


Tez-tez uchraydigan xatolar

Siklik havola:

System.Text.Json.JsonException: A possible object cycle was detected.

Entity ni qaytaryapsiz — DTO ga o'giring.

Bitta DTO hamma narsa uchun — Create da Id bor, response da parol bor. Har bir vaziyat — o'z DTO si.

Mapping da yangi maydonni unutish — javobda null/0; mapping testlari yoki Select bilan aniq yozish.


Xulosa

DTO — API ga kiradigan va chiqadigan ma'lumotning aniq shakli. Entity ni to'g'ridan-to'g'ri ishlatish: maxfiy maydonlar sizib chiqadi, over-posting, siklik JSON, API shartnomasi bazaga bog'lanib qoladi.

Request DTO — faqat mijoz kiritadigan maydonlar (Create, Update); response DTO — ochiq va hisoblangan maydonlar; ro'yxat va batafsil uchun alohida. record — qulay. Mapping — extension metodlar, EF Core da Select (faqat kerakli ustunlar), katta loyihalarda AutoMapper/Mapster. Validatsiya — request DTO larda.


Keyingi dars

46-dars: Validation — Data Annotations, ModelState, [Required], [Range]