হ্যালো হাসিব! তুমি এখন Section 12: Dependency Injection-এর পঞ্চম লেকচার “Dependency Injection” (Lecture 132)-এ আছো। এই লেকচারটি আগের লেকচারগুলোর ফাইনাল পাজল পিস। আগে আমরা শিখেছি IoC Container কীভাবে সার্ভিস তৈরি করে, আর আজ শিখবো সেই তৈরি হওয়া সার্ভিসকে Controller কীভাবে রিসিভ করে।

চলো, এই লেকচারটি বিস্তারিতভাবে ডিকোড করি।


flowchart TB



%% ==========================

%% STARTUP PHASE

%% ==========================



subgraph STARTUP["🚀 Application Startup (শুধু একবার)"]



A["dotnet run

▶ Application চালু"] --> B["Program.cs

▶ Entry Point"]



B --> C["CreateBuilder()



📝 Host, Config,

Logging এবং DI Setup

করার প্রস্তুতি"]



C --> D["IServiceCollection



📦 সব Service-এর

Registration List"]



D --> E["AddSingleton()



🟢 App চলা পর্যন্ত

একই Object"]



D --> F["AddScoped()



🟡 প্রতি Request এ

একটি Object"]



D --> G["AddTransient()



🔵 যতবার চাইবে

নতুন Object"]



E --> H

F --> H

G --> H



H["builder.Build()



🏗️ DI Container

(ServiceProvider)

তৈরি"]



H --> I["Dependency Analysis



🔍 কে কাকে

প্রয়োজন দেখে"]



I --> J["Root ServiceProvider



🌳 Application-এর

মূল Container"]



J --> K["app.Run()



🌐 Kestrel Server

Listening শুরু"]



end





%% ==========================

%% REQUEST PHASE

%% ==========================



subgraph REQUEST["🌐 Request আসলে যা হয়"]



K --> R1["HTTP Request আসে



📨 Browser / Client

Request পাঠায়"]



R1 --> R2["Create Scope



🟡 এই Request-এর

নিজস্ব Container"]



R2 --> R3["Request ServiceProvider



📦 Scoped Service

রাখার জায়গা"]



R3 --> R4["Middleware Resolve



⚙️ Middleware-এর

Dependency Inject"]



R4 --> R5["Routing



🛣️ কোন Endpoint

ডাকতে হবে খুঁজে"]



R5 --> R6["Controller Resolve



🎮 Controller তৈরি"]



R6 --> R7["Constructor Injection



💉 প্রয়োজনীয় Service

Inject করা"]



R7 --> R8["Action Execute



🚀 Business Logic Run"]



R8 --> R9["Response Return



📤 Client-কে

Result পাঠানো"]



R9 --> R10["Dispose Scope



🗑️ Scoped Object

মুছে ফেলা"]



end





%% ==========================

%% DI RESOLUTION

%% ==========================



subgraph RESOLUTION["🔍 Service Resolve করার সময়"]



X1["Service প্রয়োজন



❓ IUserService

চাই"] --> X2{"Lifetime?"}



X2 -->|Singleton| X3["Root Cache Check



🔍 আগে আছে?"]



X3 -->|Yes| X4["আগের Object ফেরত"]



X3 -->|No| X5["নতুন Object তৈরি"]



X5 --> X6["Root Cache-এ রাখা"]



X6 --> X4



X2 -->|Scoped| X7["Request Cache Check



🔍 এই Request-এ

আগে তৈরি হয়েছে?"]



X7 -->|Yes| X8["একই Object ফেরত"]



X7 -->|No| X9["নতুন Scoped Object"]



X9 --> X10["Scope Cache-এ রাখা"]



X10 --> X8



X2 -->|Transient| X11["সবসময় নতুন Object



🆕 Cache ব্যবহার হয় না"]



end





%% ==========================

%% CONTROLLER EXAMPLE

%% ==========================



subgraph EXAMPLE["📦 Real Example"]



C1["HomeController"]



C1 --> C2["IUserService



👨‍💼 Business Logic"]



C2 --> C3["IUserRepository



📂 Data Access"]



C3 --> C4["ApplicationDbContext



🗄️ EF Core"]



C4 --> C5["SQL Server



💾 Database"]



end





%% ==========================

%% DISPOSAL

%% ==========================



subgraph DISPOSAL["🗑️ Request শেষ হলে"]



D1["Request Complete"]



D1 --> D2["Scoped Service Dispose"]



D2 --> D3["DbContext.Dispose()



🔒 Connection Close"]



D3 --> D4["Memory Release"]



D4 --> D5["Next Request Ready"]



end





%% ==========================

%% LIFETIME SUMMARY

%% ==========================



subgraph LIFETIME["🎯 Lifetime Summary"]



L1["Singleton



🚀 App Start

⬇

🛑 App Stop



একই Object"]



L2["Scoped



📨 Request Start

⬇

📤 Request End



এক Object"]



L3["Transient



❓ Resolve

⬇

🆕



সবসময় নতুন Object"]



end





%% ==========================

%% LINKS

%% ==========================



R7 -.Service Resolve.-> X1

R6 -.Example.-> C1

R10 -.Cleanup.-> D1





%% ==========================

%% COLORS

%% ==========================



classDef startup fill:#d4f4dd,stroke:#1b5e20,color:#000;

classDef request fill:#d6eaff,stroke:#0d47a1,color:#000;

classDef resolve fill:#fff3cd,stroke:#ff8f00,color:#000;

classDef dispose fill:#ffd6d6,stroke:#b71c1c,color:#000;

classDef life fill:#e8d5ff,stroke:#6a1b9a,color:#000;



class A,B,C,D,E,F,G,H,I,J,K startup;

class R1,R2,R3,R4,R5,R6,R7,R8,R9,R10 request;

class X1,X2,X3,X4,X5,X6,X7,X8,X9,X10,X11 resolve;

class D1,D2,D3,D4,D5 dispose;

class L1,L2,L3 life;


সারসংক্ষেপ (Quick Revision List)

  • Dependency Injection (DI) কী: এটি হলো Inversion of Control (IoC) ইমপ্লিমেন্ট করার একটি ডিজাইন প্যাটার্ন বা টেকনিক। এর কাজ হলো IoC Container-এর তৈরি করা অবজেক্টকে ক্লায়েন্টের (Controller-এর) ভেতরে পুশ (Inject) করে দেওয়া।
  • How it Works (The Flow): ১. Controller রান করার সময় IoC Container-কে বলে, “আমাকে ICitiesService টাইপের একটি অবজেক্ট দাও।” ২. Container চেক করে যে এই ইন্টারফেসের জন্য কোন ক্লাস রেজিস্টার করা আছে (আমাদের ক্ষেত্রে CitiesService)। ৩. Container সেই ক্লাসের একটি অবজেক্ট বানায় এবং Controller-এর Constructor-এ প্যারামিটার হিসেবে পাঠিয়ে দেয়।
  • Constructor Injection: Controller-এর কনস্ট্রাক্টরে ইন্টারফেসের টাইপের একটি প্যারামিটার রাখা হয়। Container সেই প্যারামিটারে অবজেক্টটি দিয়ে দেয় এবং আমরা সেটি একটি readonly ফিল্ডে সেভ করে রাখি।
  • The Ultimate Rule: Controller-এর ভেতরে কখনোই সার্ভিসের আসল ক্লাসের নাম (যেমন CitiesService) লেখা যাবে না, শুধুমাত্র ইন্টারফেস (ICitiesService) ব্যবহার করতে হবে।

Comprehensive Breakdown

১. Dependency Injection (DI) এর ধারণা [Priority: 10/10]

লেকচারার খুব সহজ ভাষায় বলেছেন: “IoC Container supplies, Dependency Injection receives.” অর্থাৎ, IoC Container-এর কাজ হলো অবজেক্ট তৈরি করা আর Dependency Injection-এর কাজ হলো সেই অবজেক্টটিকে Controller-এর ভেতরে রিসিভ করা।

Dependency Injection মূলত তিনভাবে করা যায়:

  1. Constructor Injection (সবচেয়ে বেশি ব্যবহৃত ও রিকমেন্ডেড)
  2. Method Injection (FromServices)
  3. Property / Setter Injection

এই লেকচারে আমরা সবচেয়ে পপুলার পদ্ধতি— Constructor Injection শিখবো।

২. Constructor-এ Service Inject করা [Priority: 10/10]

আগের লেকচারে আমাদের Controller-এর কনস্ট্রাক্টর ছিল ফাঁকা এবং আমরা _citiesService = null লিখেছিলাম। এখন আমরা কনস্ট্রাক্টরের ভেতরে একটি প্যারামিটার রিসিভ করবো।

Code Implementation (Controllers/HomeController.cs):

using Microsoft.AspNetCore.Mvc;
using ServiceContracts; // শুধু Interface-এর namespace
 
namespace DIExample.Controllers
{
    public class HomeController : Controller
    {
        // ১. Private readonly field (Interface type)
        private readonly ICitiesService _citiesService;
 
        // ২. Constructor Injection
        // ASP.NET Core-এর IoC Container স্বয়ংক্রিয়ভাবে এই প্যারামিটারে অবজেক্ট পাঠিয়ে দেবে
        public HomeController(ICitiesService citiesService)
        {
            // ৩. প্যারামিটার থেকে পাওয়া অবজেক্টটিকে লোকাল ফিল্ডে সেভ করে রাখা হচ্ছে
            _citiesService = citiesService;
        }
 
        [Route("/")]
        public IActionResult Index()
        {
            // ৪. ফিল্ড ব্যবহার করে সার্ভিস কল করা হচ্ছে
            List<string> cities = _citiesService.GetCities();
            return View(cities);
        }
    }
}
 

৩. The Magic Explained (পর্দার আড়ালে কী হচ্ছে?) [Priority: 10/10]

তুমি হয়তো ভাবতে পারো, HomeController(ICitiesService citiesService)-এর প্যারামিটারে আর্গুমেন্টটা কে পাস করলো? আমরা তো কোথাও new HomeController(...) কল করিনি!

The Answer: ASP.NET Core ফ্রেমওয়ার্ক যখন রাউটিং করে দেখে যে ইউজার / রিকোয়েস্ট করেছে, তখন সে বুঝতে পারে তার HomeController দরকার। সে HomeController-এর অবজেক্ট বানাতে গিয়ে দেখে যে কনস্ট্রাক্টরে ICitiesService দরকার। তখন সে দৌড়ে Program.cs-এর IoC Container (builder.Services)-এর কাছে যায় এবং বলে “ভাই, ICitiesService এর অবজেক্ট লাগবে”। Container তার খাতা চেক করে দেখে যে ICitiesService এর জন্য CitiesService বানানো নিয়ম। সে অবজেক্টটা বানায় এবং ফ্রেমওয়ার্ককে দেয়। ফ্রেমওয়ার্ক সেই অবজেক্টটা নিয়ে HomeController-এর প্যারামিটারে ঢুকিয়ে দেয়! এই পুরো ম্যাজিকটাই হলো Dependency Injection

৪. The Ultimate Summary (DIP, IoC, and DI Working Together) [Priority: 10/10]

লেকচারার সবশেষে এই তিনটি কনসেপ্টকে মিলিয়ে একটি সামারি দিয়েছেন:

  1. Dependency Inversion Principle (DIP): এটি একটি রুল যা বলে “ক্লাসগুলোর মাঝে Interface ব্যবহার করে তাদের আলাদা রাখো (Clean Separation)”।
  2. Inversion of Control (IoC): এটি একটি রুল যা বলে “নিজে অবজেক্ট বানিও না, ফ্রেমওয়ার্কের Container-কে বানাতে দাও”।
  3. Dependency Injection (DI): এটি সেই মেকানিজম যার মাধ্যমে Container থেকে তৈরি হওয়া অবজেক্টটি Controller-এর Constructor-এ রিসিভ করা হয়। এই তিনটি কনসেপ্ট আসলে আলাদা কিছু নয়, বরং একটি আরেকটির পরিপূরক।

৫. Real-world Scale (বড় প্রজেক্টে কীভাবে কাজ করে?) [Priority: 8/10]

রিয়েল-ওয়ার্ল্ড প্রজেক্টে ডাটাবেসের প্রতিটি টেবিলের জন্য আলাদা আলাদা সার্ভিস থাকে (যেমন: CountriesService, PersonsService)।

  • সব সার্ভিসকে Program.cs-এ IoC Container-এ অ্যাড করতে হয়।
  • এক সার্ভিস থেকে অন্য সার্ভিসকেও ইনজেক্ট করা যায়!
  • যেমন: PersonsService যদি CountriesService-এর ডাটা চায়, তাহলে PersonsService-এর কনস্ট্রাক্টরে ICountriesService ইনজেক্ট করে দিলেই হবে। এই প্যাটার্নটি পুরো প্রজেক্ট জুড়েই চলতেই থাকে।

VS / VS Code Shortcuts (For robust coding)

  • Generate Constructor and Assign Field: তুমি যদি শুধু private readonly ICitiesService _citiesService; লিখে রাখো, তাহলে ঐ লাইনের উপর Ctrl + . চাপলে “Generate constructor…” অপশন আসবে। ক্লিক করলেই অটোমেটিক কনস্ট্রাক্টর এবং অ্যাসাইনমেন্ট তৈরি হয়ে যাবে!

Best Practices & .NET 10 Context

Best Practices for Dependency Injection:

  1. Always use Constructor Injection: Controller-এ সার্ভিস ইনজেক্ট করার ডিফল্ট এবং বেস্ট ওয়ে হলো Constructor Injection। অন্য কোনো মেথড (যেমন Property Injection) ব্যবহার না করাই ভালো।
  2. Keep Constructors Clean: Constructor-এর কাজ শুধু অবজেক্ট রিসিভ করে ফিল্ডে অ্যাসাইন করা। এর ভেতরে কখনোই ডাটাবেস কোয়েরি বা লজিক লিখবে না।

.NET 10 Context: ASP.NET Core 10-এ Constructor Injection-এর মেকানিজম .NET 6 এর মতোই হুবহু এক। তবে আগেই যেমন বলেছিলাম, .NET 10-এ Primary Constructors ব্যবহার করে কোডটি অনেক ছোট করে লেখা যায়।

.NET 10 Primary Constructor Example:

using Microsoft.AspNetCore.Mvc;
using ServiceContracts;
 
namespace DIExample.Controllers;
 
// .NET 10: 클래스 이름 바로 뒤에 Parameter를 넣어서 의존성 주입 (Primary Constructor)
public class HomeController(ICitiesService citiesService) : Controller
{
    [Route("/")]
    public IActionResult Index()
    {
        // 따로 readonly field를 안 만들어도 바로 매개변수 변수 사용 가능
        var cities = citiesService.GetCities();
        return View(cities);
    }
}
 

(এটি .NET 10-এ DI ইমপ্লিমেন্ট করার সবচেয়ে স্মার্ট এবং পপুলার পদ্ধতি)।

হাসিব, Dependency Injection-এর এই ম্যাজিকটা কি বুঝতে পেরেছো? তুমি রেডি থাকলে আমরা নেক্সট লেকচার “Method Injection - FromService”-এ মুভ করতে পারি, যেখানে আমরা শিখবো কনস্ট্রাক্টরের বদলে মেথডের প্যারামিটার দিয়ে কীভাবে সার্ভিস ইনজেক্ট করতে হয়!