হ্যালো হাসিব! তুমি এখন Section 12: Dependency Injection-এর দ্বিতীয় লেকচার “Services - Part 2” (Lecture 129)-এ আছো। আগের লেকচারে আমরা Service Class তৈরি করেছিলাম একটি আলাদা Class Library প্রজেক্টে। আজকের লেকচারে আমরা শিখবো কীভাবে সেই Service-কে আমাদের মেইন Web Project (ASP.NET Core Project)-এর Controller-এর সাথে কানেক্ট করতে হয় এবং ডাটা View-তে পাঠাতে হয়।

চলো, পুরো লেকচারটি ধাপে ধাপে ডিকোড করি।


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

  • Project Reference: যেহেতু Service একটি আলাদা প্রজেক্টে (Class Library) আছে, তাই Web Project-কে Service-এর ডাটা অ্যাক্সেস করতে হলে Web Project-এ Service প্রজেক্টের Reference অ্যাড করতে হয়।
  • Controller Setup: Controller-এর ভেতরে Service-এর একটি private readonly ফিল্ড ডিক্লেয়ার করতে হয় এবং Constructor-এর মাধ্যমে তার অবজেক্ট তৈরি করতে হয় (এটি একটি Bad Practice, যা আমরা পরে Dependency Injection দিয়ে ঠিক করবো)।
  • Execution Flow: Request -> Middleware -> Controller Constructor -> Service Constructor -> Controller Action Method -> Service Method -> View -> Layout -> Browser।
  • Model Binding in View: Service থেকে আসা ডাটা (List of String)-কে View-তে রিসিভ করার জন্য @model IEnumerable<string> ব্যবহার করা বেস্ট প্র্যাকটিস, কারণ IEnumerable হলো সব কালেকশনের প্যারেন্ট ইন্টারফেস।

Comprehensive Breakdown

১. Controller এবং Layout সেটআপ (The Basics) [Priority: 3/10]

লেকচারের শুরুতে বরাবরের মতোই একটি বেসিক HomeController এবং Index.cshtml তৈরি করা হয়েছে। এরপর Shared ফোল্ডারে _Layout.cshtml এবং _ViewStart.cshtml তৈরি করে লেআউট সেটআপ করা হয়েছে। (এই অংশগুলো তুমি আগের সেকশনগুলোতে অনেকবার করেছো, তাই এখানে বিস্তারিত স্কিপ করছি)।

২. Adding Project Reference (সবচেয়ে গুরুত্বপূর্ণ ধাপ) [Priority: 10/10]

যেহেতু আমাদের CitiesService ক্লাসটি Services নামক আলাদা একটি Class Library প্রজেক্টে আছে, আমাদের মেইন প্রজেক্ট (DIExample) বাই ডিফল্ট তাকে চিনবে না।

How to Add Reference:

  1. Web Project (DIExample)-এর Dependencies-এ রাইট-ক্লিক করো।
  2. Add Project Reference... এ ক্লিক করো।
  3. লিস্ট থেকে Services প্রজেক্টের চেকবক্সে টিক চিহ্ন দাও এবং OK করো। (এখন Web Project-এর সাথে Services প্রজেক্টের একটি লিংক তৈরি হয়ে গেলো)।

VS Code Shortcut: টার্মিনালে Web Project-এর ফোল্ডারে গিয়ে এই কমান্ডটি রান করো:

dotnet add reference ../Services/Services.csproj
 

৩. Controller-এ Service কল করা (The Bad Practice Version) [Priority: 10/10]

Reference অ্যাড করার পর, এখন আমরা HomeController-এর ভেতরে CitiesService-কে কল করতে পারবো।

Code Implementation (Controllers/HomeController.cs):

using Microsoft.AspNetCore.Mvc;
using Services; // ১. Service-এর Namespace ইমপোর্ট করতে হবে
 
namespace DIExample.Controllers
{
    public class HomeController : Controller
    {
        // ২. Private Readonly Field ডিক্লেয়ার করা
        // readonly দিলে ভুল করে এর ভ্যালু অন্য কোথাও চেঞ্জ হয়ে যাওয়ার ভয় থাকে না
        private readonly CitiesService _citiesService;
 
        // ৩. Constructor তৈরি করা
        public HomeController()
        {
            // ৪. অবজেক্ট তৈরি করা (এটি Bad Practice)
            _citiesService = new CitiesService();
        }
 
        [Route("/")]
        public IActionResult Index()
        {
            // ৫. Service-এর মেথড কল করে ডাটা আনা
            List<string> cities = _citiesService.GetCities();
 
            // ৬. ডাটা View-তে পাস করা
            return View(cities);
        }
    }
}
 

৪. View-তে ডাটা রিসিভ করা (Strongly Typed View) [Priority: 9/10]

যেহেতু Controller থেকে আমরা একটি List (List<string>) পাঠাচ্ছি, View-তে সেটি রিসিভ করতে হবে। লেকচারার এখানে একটি চমৎকার টিপস দিয়েছেন— সরাসরি List<string> না লিখে, এর প্যারেন্ট ইন্টারফেস IEnumerable<string> ব্যবহার করা।

Why IEnumerable? ভবিষ্যতে যদি Service থেকে List-এর বদলে Array বা Dictionary রিটার্ন করা হয়, তাহলেও এই ভিউটি ব্রেক করবে না (এরর খাবে না), কারণ এরা সবাই IEnumerable থেকে এসেছে।

Code Implementation (Views/Home/Index.cshtml):

<!-- Parent Interface দিয়ে Model রিসিভ করা -->
@model IEnumerable<string>
 
<ul class="list">
    @foreach (string city in Model)
    {
        <li>@city</li>
    }
</ul>
 

৫. The Execution Flow (কীভাবে পুরো কোড রান হচ্ছে?) [Priority: 10/10]

লেকচারার ব্রেকপয়েন্ট বসিয়ে এক্সিকিউশন ফ্লো দেখিয়েছেন। এটি বোঝা খুব জরুরি:

  1. ইউজার যখন ব্রাউজারে localhost:xxxx লিখে এন্টার দেয়, তখন রিকোয়েস্টটি Program.cs-এর Middleware পার হয়ে HomeController-এ আসে।
  2. HomeController-এর অবজেক্ট তৈরি হওয়ার সময় তার Constructor এক্সিকিউট হয়।
  3. Constructor-এর ভেতরে new CitiesService() থাকায়, তখন CitiesService-এর Constructor এক্সিকিউট হয় এবং লিস্টে ডাটা সেট হয়।
  4. এরপর Index Action Method কল হয়।
  5. সেখানে _citiesService.GetCities() কল হলে Service ডাটা রিটার্ন করে।
  6. Controller সেই ডাটা নিয়ে Index.cshtml View-কে এক্সিকিউট করে।
  7. View-এর ভেতরে লুপ চলে HTML জেনারেট হয়।
  8. সবশেষে Layout-এর সাথে মিলে পুরো পেজটি ব্রাউজারে রেসপন্স হিসেবে চলে যায়।

৬. The Bad Practice (The Big Reveal) [Priority: 10/10]

লেকচারের একদম শেষে লেকচারার একটি বিশাল বোম ফাটিয়েছেন! তিনি বলেছেন, এই যে আমরা Controller-এর Constructor-এর ভেতরে _citiesService = new CitiesService(); লিখলাম— এটি একটি চরম Bad Practice!

Why is it bad? কারণ, তুমি Controller-কে Service-এর সাথে শক্তভাবে (Tightly Coupled) জুড়ে দিয়েছো। যদি কাল Service-এর কনস্ট্রাক্টরে কোনো প্যারামিটার অ্যাড করতে হয়, তাহলে যেখানে যেখানে তুমি new CitiesService() লিখেছো, সব জায়গায় কোড চেঞ্জ করতে হবে!

The Solution: এই সমস্যা সমাধানের জন্যই জন্ম হয়েছে Dependency Injection (DI)-এর। আমরা new কি-ওয়ার্ড দিয়ে নিজে অবজেক্ট বানাবো না, ফ্রেমওয়ার্ককে বলবো আমাদের অবজেক্ট বানিয়ে দিতে। (এটিই আমরা পরবর্তী লেকচারগুলোতে শিখবো)।


Best Practices & .NET 10 Context

Best Practices for Services in Controller:

  1. NEVER use new keyword: Controller বা অন্য কোনো ক্লাসে অন্য কোনো ক্লাসের অবজেক্ট (Service) তৈরি করার জন্য কখনোই new কি-ওয়ার্ড ব্যবহার করবে না। সবসময় Dependency Injection ব্যবহার করবে।
  2. Use readonly: ফিল্ড ডিক্লেয়ার করার সময় সবসময় private readonly ব্যবহার করবে, যাতে কনস্ট্রাক্টর ছাড়া অন্য কোথাও ঐ ফিল্ডের রেফারেন্স চেঞ্জ করা না যায়।
  3. Program to Interfaces: CitiesService ক্লাস সরাসরি ব্যবহার করার চেয়ে একটি Interface (যেমন ICitiesService) ব্যবহার করা ইন্ডাস্ট্রির বেস্ট প্র্যাকটিস।

.NET 10 Context: আধুনিক C# 12/13 (যা .NET 10 এ ব্যবহৃত হচ্ছে)-এ Primary Constructors নামে একটি নতুন ফিচার এসেছে, যা এই কোডটিকে আরও অনেক বেশি ক্লিন এবং ছোট করে দেয়।

.NET 10-এ Constructor লেখার স্মার্ট পদ্ধতি (Primary Constructor):

using Microsoft.AspNetCore.Mvc;
using Services;
 
namespace DIExample.Controllers;
 
// Primary Constructor: ক্লাসের নামের সাথেই প্যারামিটার দিয়ে দেওয়া যায়!
// এখানে আর আলাদা করে Constructor বা Private readonly field লেখার দরকার নেই।
public class HomeController(CitiesService citiesService) : Controller
{
    [Route("/")]
    public IActionResult Index()
    {
        // সরাসরি citiesService ব্যবহার করা যাচ্ছে!
        var cities = citiesService.GetCities();
        return View(cities);
    }
}
 

(এই Primary Constructor ফিচারটি .NET 8 থেকে শুরু হয়েছে এবং .NET 10-এ এটিই ডিফল্ট স্ট্যান্ডার্ড। তবে এটি ঠিকমতো কাজ করানোর জন্য আগে Dependency Injection সেটআপ করতে হয়)।

হাসিব, Service-কে প্রজেক্টে যুক্ত করার কনসেপ্ট কি ক্লিয়ার হয়েছে? তুমি রেডি থাকলে আমরা পরবর্তী লেকচার “Dependency Inversion Principle”-এ মুভ করতে পারি, যেখানে আমরা শিখবো কেন new কি-ওয়ার্ড ব্যবহার করা খারাপ!