Dependency Injection — AddSingleton, AddScoped, AddTransient, nima farqi
Muammodan boshlaylik
public class BuyurtmaController : ControllerBase
{
private readonly BuyurtmaService _service = new BuyurtmaService(
new BuyurtmaRepository(new DokonDbContext("Host=...;Password=...")),
new EmailYuboruvchi(new SmtpClient("smtp.mail.uz")),
new Logger());
}
Muammolar:
- Controller o'zi barcha bog'liqliklarni yaratadi va ularning bog'liqliklarini ham — butun zanjirni biladi
- Connection string kodda; email ni Telegram ga almashtirish — controller ni o'zgartirish
- Test qilib bo'lmaydi — haqiqiy baza va haqiqiy email
- Har so'rovda yangi baza ulanishi yoki aksincha — hamma uchun bitta, nazoratsiz
Dependency Injection nima?
DI — klass o'z bog'liqliklarini o'zi yaratmaydi, ularni tashqaridan (odatda konstruktor orqali) oladi:
public class BuyurtmaController(IBuyurtmaService service) : ControllerBase
{
[HttpPost]
public async Task<IActionResult> Yarat(BuyurtmaDto dto) => Ok(await service.YaratAsync(dto));
}
public class BuyurtmaService(IBuyurtmaRepository repo, IXabarYuboruvchi xabar, ILogger<BuyurtmaService> logger)
: IBuyurtmaService
{
// ...
}
Kim yaratadi? ASP.NET Core ning DI konteyneri. Siz faqat Program.cs da "qaysi interfeys uchun qaysi klass" ekanini aytasiz:
builder.Services.AddScoped<IBuyurtmaService, BuyurtmaService>();
builder.Services.AddScoped<IBuyurtmaRepository, BuyurtmaRepository>();
builder.Services.AddScoped<IXabarYuboruvchi, EmailYuboruvchi>();
So'rov kelganda konteyner BuyurtmaController ni yaratadi → unga IBuyurtmaService kerak → BuyurtmaService ni yaratadi → unga repo, xabar, logger kerak → ularni ham yaratadi. Butun zanjir — avtomatik.
Hayotiy o'xshatma
Restoran oshpazi. U o'zi bozorga borib go'sht sotib olmaydi, pichoqni o'zi charxlamaydi — har ertalab kerakli masalliq va asboblar uning stoliga yetkazib beriladi. Oshpaz faqat "menga go'sht va o'tkir pichoq kerak" deydi (konstruktor). Ta'minotchini almashtirish — oshpazning ishiga ta'sir qilmaydi.
Uch xil umr (lifetime)
builder.Services.AddSingleton<IKesh, XotiraKeshi>();
builder.Services.AddScoped<IBuyurtmaService, BuyurtmaService>();
builder.Services.AddTransient<IParolGenerator, ParolGenerator>();
| Lifetime | Nechta nusxa | Qachon |
|---|---|---|
| Singleton | Butun ilova uchun bitta | Holatsiz yoki thread-safe: kesh, sozlamalar, HttpClient fabrikasi |
| Scoped | Har bir HTTP so'rov uchun bitta | Ko'pchilik servislar: DbContext, repository lar, biznes servislar |
| Transient | Har so'ralganda yangi | Yengil, holatsiz yordamchilar |
So'rov 1: Controller → ServiceA(scoped #1) → Repo(scoped #1)
→ ServiceB(scoped #1) → Repo(scoped #1) ← bir xil nusxa
So'rov 2: Controller → ServiceA(scoped #2) → Repo(scoped #2) ← yangi nusxa
Singleton: hamma so'rovlarda bitta
Ko'rish uchun tajriba
public class Hisoblagich { public Guid Id { get; } = Guid.NewGuid(); }
builder.Services.AddScoped<Hisoblagich>();
app.MapGet("/test", (Hisoblagich a, Hisoblagich b) => new { a = a.Id, b = b.Id, bir_xil = a.Id == b.Id });
// Scoped: bitta so'rovda bir_xil = true, keyingi so'rovda — yangi Id
// Transient: bir_xil = false
// Singleton: doim bir xil Id
Captive dependency — xavfli xato
[!WARNING] Singleton servis Scoped servisni konstruktorda olmasligi kerak. Singleton bir marta yaratiladi va scoped servis (masalan,
DbContext) uning ichida abadiy qolib ketadi — barcha so'rovlar bittaDbContextni bir vaqtda ishlatadi: eskirgan ma'lumot, xatolar va thread-safety muammolari. Development da ASP.NET Core buni aniqlaydi:InvalidOperationException: Cannot consume scoped service 'DokonDbContext' from singleton 'X'.
Qoida: servis faqat o'zidan uzoqroq yoki teng umrli servislarga bog'lansin:
| Kim ← kimni oladi | Singleton | Scoped | Transient |
|---|---|---|---|
| Singleton | ✅ | ❌ | ⚠️ |
| Scoped | ✅ | ✅ | ✅ |
| Transient | ✅ | ✅ | ✅ |
Singleton ichida scoped servis kerak bo'lsa — IServiceScopeFactory bilan qisqa scope yaratish (masalan, fon vazifalarida).
Ro'yxatdan o'tkazish usullari
builder.Services.AddScoped<IMahsulotService, MahsulotService>(); // interfeys → klass
builder.Services.AddScoped<MahsulotService>(); // klassning o'zi
builder.Services.AddSingleton<ISozlama>(new Sozlama { Til = "uz" }); // tayyor nusxa
builder.Services.AddScoped<IXabar>(sp => // fabrika
{
var config = sp.GetRequiredService<IConfiguration>();
return config["Xabar:Kanal"] == "telegram"
? new TelegramYuboruvchi()
: new EmailYuboruvchi();
});
Framework servislari — tayyor
ILogger<T>, IConfiguration, IWebHostEnvironment, IHttpClientFactory, IMemoryCache — konstruktorga yozsangiz bas.
Qayerda olish mumkin
// konstruktor — asosiy usul
public class MahsulotService(DokonDbContext db, ILogger<MahsulotService> logger) { }
// controller action parametri
public IActionResult Ol([FromServices] IKesh kesh) => Ok();
// Minimal API — avtomatik
app.MapGet("/x", (IMahsulotService s) => s.Hammasi());
// ❌ Service Locator — bog'liqliklar yashirin, test qilish qiyin
var s = HttpContext.RequestServices.GetService<IMahsulotService>();
DI ning foydasi
| DI siz | DI bilan | |
|---|---|---|
| Bog'liqlikni almashtirish | Kodni o'zgartirish | Program.cs dagi bitta qator |
| Test | Haqiqiy baza va email | Mock/soxta implementatsiya (77-dars) |
| Umrni boshqarish | Qo'lda | Konteyner — Singleton/Scoped/Transient |
| Resurslarni yopish | Qo'lda Dispose |
Konteyner scope tugaganda o'zi |
[!TIP] Konstruktorda 5–6 dan ko'p bog'liqlik bo'lsa — klass juda ko'p ish qilyapti. Uni kichikroq servislarga bo'lish haqida o'ylang.
Tez-tez uchraydigan xatolar
Ro'yxatdan o'tkazilmagan servis:
InvalidOperationException: Unable to resolve service for type 'IMahsulotService' while attempting to activate 'MahsulotlarController'.
builder.Services.AddScoped<IMahsulotService, MahsulotService>() ni unutgansiz.
Singleton ichida DbContext — captive dependency.
Servis ichida new bilan bog'liqlik yaratish — DI ning foydasi yo'qoladi.
Xulosa
DI — klass bog'liqliklarini o'zi yaratmaydi, konstruktor orqali oladi; ASP.NET Core konteyneri butun zanjirni avtomatik yaratadi. Ro'yxatdan o'tkazish — Program.cs da: builder.Services.AddScoped<IInterfeys, Klass>().
Lifetime: Singleton — ilova uchun bitta, Scoped — har so'rovga bitta (DbContext va ko'pchilik servislar), Transient — har safar yangi. Singleton scoped servisni olmasin (captive dependency). Bog'liqliklarni konstruktor orqali oling, service locator emas. Natija — almashtirish va test qilish oson.
Keyingi dars
39-dars: Middleware — nima, pipeline, tartib, custom middleware yaratish