DTO — nima uchun, Entity dan ajratish, request/response DTO
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:
- Javobda
ParolXesh— hamma ko'radi - Ro'yxatdan o'tishda mijoz
{ "email": "...", "rol": "admin" }yuboradi — va admin bo'ladi (over-posting) Buyurtmalar→ har bir buyurtmadaFoydalanuvchi→ yanaBuyurtmalar... — JSON da cheksiz sikl- 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
Rolni yubora olmaydi — DTO da bunday maydon yo'q - Javobda
ParolXeshyo'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]