হ্যালো হাসিব! তুমি এখন 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:
- Web Project (
DIExample)-এরDependencies-এ রাইট-ক্লিক করো। Add Project Reference...এ ক্লিক করো।- লিস্ট থেকে
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]
লেকচারার ব্রেকপয়েন্ট বসিয়ে এক্সিকিউশন ফ্লো দেখিয়েছেন। এটি বোঝা খুব জরুরি:
- ইউজার যখন ব্রাউজারে
localhost:xxxxলিখে এন্টার দেয়, তখন রিকোয়েস্টটিProgram.cs-এর Middleware পার হয়েHomeController-এ আসে। HomeController-এর অবজেক্ট তৈরি হওয়ার সময় তার Constructor এক্সিকিউট হয়।- Constructor-এর ভেতরে
new CitiesService()থাকায়, তখনCitiesService-এর Constructor এক্সিকিউট হয় এবং লিস্টে ডাটা সেট হয়। - এরপর
IndexAction Method কল হয়। - সেখানে
_citiesService.GetCities()কল হলে Service ডাটা রিটার্ন করে। - Controller সেই ডাটা নিয়ে
Index.cshtmlView-কে এক্সিকিউট করে। - View-এর ভেতরে লুপ চলে HTML জেনারেট হয়।
- সবশেষে 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:
- NEVER use
newkeyword: Controller বা অন্য কোনো ক্লাসে অন্য কোনো ক্লাসের অবজেক্ট (Service) তৈরি করার জন্য কখনোইnewকি-ওয়ার্ড ব্যবহার করবে না। সবসময় Dependency Injection ব্যবহার করবে। - Use
readonly: ফিল্ড ডিক্লেয়ার করার সময় সবসময়private readonlyব্যবহার করবে, যাতে কনস্ট্রাক্টর ছাড়া অন্য কোথাও ঐ ফিল্ডের রেফারেন্স চেঞ্জ করা না যায়। - 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 কি-ওয়ার্ড ব্যবহার করা খারাপ!