LINQ bilan so'rov — Where, FirstOrDefault, Include, AsNoTracking
Muammodan boshlaylik
Mahsulotlar sahifasi sekin — 3 soniya. SQL loglarini yoqsangiz:
SELECT * FROM "Mahsulotlar" -- 50 000 qator, barcha ustunlar
SELECT * FROM "Kategoriyalar" WHERE "Id" = 1
SELECT * FROM "Kategoriyalar" WHERE "Id" = 2
SELECT * FROM "Kategoriyalar" WHERE "Id" = 1
... (yana 20 ta)
Kod esa "oddiy" ko'rinadi:
var hammasi = await db.Mahsulotlar.ToListAsync(); // butun jadval xotiraga
var sahifa = hammasi.Where(m => m.Faol).OrderBy(m => m.Narx).Take(20);
foreach (var m in sahifa)
Console.WriteLine(m.Kategoriya.Nomi); // har biri uchun alohida so'rov
LINQ bir xil ko'rinadi — lekin qayerda bajarilishi (bazada yoki xotirada) natijani butunlay o'zgartiradi.
IQueryable va IEnumerable
IQueryable<Mahsulot> sorov = db.Mahsulotlar.Where(m => m.Faol); // hali bajarilmadi — SQL quriladi
List<Mahsulot> natija = await sorov.ToListAsync(); // shu yerda SQL yuboriladi
IQueryable<T> |
IEnumerable<T> (List) |
|
|---|---|---|
| Qayerda bajariladi | Bazada — SQL ga o'giriladi | Xotirada (C#) |
Where, OrderBy, Take |
SQL ning bir qismi | Barcha qatorlar yuklanganidan keyin |
Qoida: filtrlash, saralash, sahifalash va Select — ToListAsync() dan oldin.
// ❌ 50 000 qator yuklanadi, keyin 20 tasi olinadi
var r = (await db.Mahsulotlar.ToListAsync()).Where(m => m.Faol).Take(20);
// ✅ bazadan faqat 20 qator
var r = await db.Mahsulotlar.Where(m => m.Faol).OrderBy(m => m.Narx).Take(20).ToListAsync();
Hayotiy o'xshatma
Ombordan buyurtma. Noto'g'ri yo'l — butun omborni yuk mashinasida uyga olib kelib, keyin kerakli 20 ta qutini tanlash. To'g'ri yo'l — omborchiga ro'yxat berish ("faqat qizil, kichik o'lchamdagi, birinchi 20 tasi"), u o'zi tanlab, faqat shularni jo'natadi. IQueryable — omborchiga ro'yxat; ToListAsync() — jo'natish.
Asosiy so'rovlar
// filtr
var arzonlar = await db.Mahsulotlar
.Where(m => m.Narx < 100_000 && m.Faol)
.ToListAsync(ct);
// matn qidiruv
var topilgan = await db.Mahsulotlar
.Where(m => EF.Functions.ILike(m.Nomi, quot;%{q}%")) // PostgreSQL — katta-kichik harfsiz
.ToListAsync(ct);
// saralash va sahifalash
var sahifa = await db.Mahsulotlar
.OrderByDescending(m => m.YaratilganVaqt)
.ThenBy(m => m.Id)
.Skip((sahifaRaqam - 1) * hajm)
.Take(hajm)
.ToListAsync(ct);
// bitta
var m = await db.Mahsulotlar.FirstOrDefaultAsync(x => x.Id == id, ct);
var yagona = await db.Mahsulotlar.SingleOrDefaultAsync(x => x.Slug == slug, ct);
// agregatlar
var jami = await db.Mahsulotlar.SumAsync(m => m.Narx * m.Qoldiq, ct);
var bormi = await db.Mahsulotlar.AnyAsync(m => m.Qoldiq == 0, ct);
Async versiyalari (ToListAsync, FirstOrDefaultAsync, CountAsync) — Microsoft.EntityFrameworkCore dan.
Select — faqat kerakli ustunlar (projection)
var royxat = await db.Mahsulotlar
.Where(m => m.Faol)
.Select(m => new MahsulotRoyxatDto(m.Id, m.Nomi, m.Narx, m.Kategoriya.Nomi))
.ToListAsync(ct);
SELECT m."Id", m."Nomi", m."Narx", k."Nomi"
FROM "Mahsulotlar" m
INNER JOIN "Kategoriyalar" k ON m."KategoriyaId" = k."Id"
WHERE m."Faol"
- Faqat kerakli ustunlar — kam ma'lumot
m.Kategoriya.Nomi— EF Core o'ziJOINqiladi- Change tracking yo'q — tezroq
[!TIP] O'qish uchun so'rovlar (API ga ma'lumot qaytarish) — deyarli har doim
Selectbilan DTO ga (45-dars). To'liq entity yuklash — faqat uni o'zgartirmoqchi bo'lganda.
Include — bog'liq ma'lumotlarni yuklash
var buyurtma = await db.Buyurtmalar
.Include(b => b.Mijoz)
.Include(b => b.Qatorlar)
.ThenInclude(q => q.Mahsulot)
.FirstOrDefaultAsync(b => b.Id == id, ct);
Navigation property lar (54-dars) standart holatda yuklanmaydi — Include bilan aniq so'rash kerak.
N+1 muammosi
var buyurtmalar = await db.Buyurtmalar.ToListAsync(); // 1 so'rov
foreach (var b in buyurtmalar)
Console.WriteLine(b.Mijoz.Ism); // ❌ Mijoz yuklanmagan — null
Lazy loading yoqilgan bo'lsa (UseLazyLoadingProxies) — har bir buyurtma uchun alohida so'rov: 1 + N. 100 buyurtma — 101 so'rov.
var buyurtmalar = await db.Buyurtmalar
.Select(b => new { b.Id, MijozIsmi = b.Mijoz.Ism, b.Summa }) // ✅ bitta so'rov
.ToListAsync();
[!WARNING] Bir nechta kolleksiyani
Includeqilish (Include(b => b.Qatorlar).Include(b => b.Tolovlar)) — "kartezian portlash": SQL natijasida qatorlar soni ko'paytiriladi va so'rov juda sekinlashadi. Bunday holatda.AsSplitQuery()— har bir kolleksiya uchun alohida, lekin oldindan rejalashtirilgan so'rov.
AsNoTracking — faqat o'qish
var royxat = await db.Mahsulotlar
.AsNoTracking()
.Where(m => m.Faol)
.ToListAsync(ct);
DbContext yuklangan ob'ektlarni kuzatmaydi — kamroq xotira, tezroq. O'zgartirilmaydigan ma'lumot uchun (ro'yxatlar, hisobotlar). Select bilan DTO ga — avtomatik kuzatilmaydi.
Butun DbContext uchun standart:
options.UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking);
Dinamik filtrlar
IQueryable<Mahsulot> q = db.Mahsulotlar.AsNoTracking();
if (filtr.KategoriyaId is not null)
q = q.Where(m => m.KategoriyaId == filtr.KategoriyaId);
if (filtr.Min is not null)
q = q.Where(m => m.Narx >= filtr.Min);
if (!string.IsNullOrWhiteSpace(filtr.Qidiruv))
q = q.Where(m => EF.Functions.ILike(m.Nomi, quot;%{filtr.Qidiruv}%"));
q = filtr.Saralash switch
{
"arzon" => q.OrderBy(m => m.Narx),
"qimmat" => q.OrderByDescending(m => m.Narx),
_ => q.OrderByDescending(m => m.YaratilganVaqt)
};
var jami = await q.CountAsync(ct);
var royxat = await q.Skip((filtr.Sahifa - 1) * filtr.Hajm).Take(filtr.Hajm)
.Select(m => new MahsulotRoyxatDto(m.Id, m.Nomi, m.Narx, m.Kategoriya.Nomi))
.ToListAsync(ct);
IQueryable ga shartlarni bosqichma-bosqich qo'shish — natijada bitta SQL so'rov.
Xom SQL — kerak bo'lganda
var natija = await db.Mahsulotlar
.FromSql(quot;SELECT * FROM \"Mahsulotlar\" WHERE \"Narx\" < {maks}")
.ToListAsync(ct);
FromSql interpolatsiyasi avtomatik parametrlarga aylanadi — SQL injection dan himoyalangan. FromSqlRaw + satr birlashtirish — xavfli.
Tez-tez uchraydigan xatolar
ToListAsync dan keyin filtrlash — butun jadval xotiraga.
SQL ga o'girib bo'lmaydigan metod:
db.Mahsulotlar.Where(m => MeningFormatim(m.Nomi) == "x")
// ❌ InvalidOperationException: The LINQ expression could not be translated
So'rovda faqat EF Core tushunadigan amallar; murakkab C# mantiq — natija olingandan keyin.
Include siz navigation property — null yoki N+1.
Xulosa
IQueryable — so'rov bazada bajariladi; Where, OrderBy, Skip/Take, Select — ToListAsync() dan oldin. Async metodlar: ToListAsync, FirstOrDefaultAsync, CountAsync, AnyAsync, SumAsync.
Select bilan DTO ga — faqat kerakli ustunlar, avtomatik JOIN, eng samarali. Include/ThenInclude — bog'liq ma'lumot; N+1 va kartezian portlashdan ehtiyot (AsSplitQuery). AsNoTracking — faqat o'qish uchun. Dinamik filtrlar — IQueryable ga bosqichma-bosqich. Xom SQL — FromSql interpolatsiyasi bilan.
Keyingi dars
54-dars: One-to-Many munosabat — navigation property, foreign key, Include