হ্যালো হাসিব! এই লেকচারে আমরা Persons-এর ডাটাগুলো একটা সুন্দর টেবিল বা গ্রিডে দেখানোর কাজ করবো। গত লেকচারে আমরা যে Mock Data তৈরি করেছিলাম, সেটাকেই এখন ইউজারের সামনে ভিজ্যুয়ালি প্রেজেন্ট করার পালা।
চলো প্রথমে পুরো লেকচারটির একটা কুইক সামারি দেখে নিই।
📌 Quick Summary for Revision
- Dependency Injection (DI):
Program.csফাইলেICountriesServiceএবংIPersonsServiceরেজিস্টার করা। - Constructor Injection:
PersonsController-এ DI-এর মাধ্যমে Service ইনজেক্ট করা এবং_personsServiceফিল্ড ইনিশিয়ালাইজ করা। - Fetching Data:
Indexমেথড থেকেGetAllPersons()কল করে ডাটাগুলো View-তে পাঠানো। - Strongly Typed View:
Index.cshtml-কেIEnumerable<PersonResponse>টাইপের মডেল রিসিভ করার জন্য কনফিগার করা। - Data Rendering: View-তে
foreachলুপ চালিয়ে HTML Table-এর<tr>এবং<td>এর মাধ্যমে ডাটা শো করানো। - Safe Navigation: Date of Birth রেন্ডার করার সময় Null Reference Exception এড়াতে
?(Null-conditional operator) ব্যবহার করা। - Debugging: Controller এবং View-তে Breakpoint বসিয়ে এক্সিকিউশন ফ্লো চেক করা।
🚀 Comprehensive Breakdown & The “Why”
নিচে লেকচারের প্রতিটি কনসেপ্ট বিস্তারিত এবং কারণসহ এক্সপ্লেইন করা হলো:
১. IoC Container-এ Service রেজিস্টার করা (Program.cs) [Priority: 10/10]
The “Why”: ASP.NET Core-এ যখনই আমরা কোনো Controller-এ ইন্টারফেস (যেমন: IPersonsService) কল করবো, অ্যাপ্লিকেশনকে জানতে হবে ওই ইন্টারফেসের বদলে সে কোন আসল ক্লাসটা (Concrete Implementation) তৈরি করবে। এই পরিচয় করিয়ে দেওয়ার কাজটাই Program.cs-এ করা হয়।
নোট: লেকচারে প্রথমে Scoped বলে পরে Singleton-এর কথা বলা হয়েছে। যেহেতু আমরা ডাটাবেস ছাড়া ইন-মেমোরি (লিস্ট) ডাটা নিয়ে কাজ করছি, তাই অ্যাপ্লিকেশন চালু থাকা অবস্থায় ডাটা ধরে রাখতে AddSingleton ব্যবহার করাটাই লজিক্যাল।
Implementation:
// Program.cs
builder.Services.AddSingleton<ICountriesService, CountriesService>();
builder.Services.AddSingleton<IPersonsService, PersonsService>();
২. Controller-এ Constructor Injection [Priority: 10/10]
The “Why”: আমরা চাইলে Controller-এর ভেতরে new PersonsService() লিখে অবজেক্ট তৈরি করতে পারতাম। কিন্তু এতে Controller এবং Service একে অপরের সাথে খুব শক্তভাবে কাপলড (Tightly Coupled) হয়ে যায়। Dependency Injection (DI) ব্যবহার করলে IoC Container নিজ দায়িত্বে অবজেক্ট তৈরি করে Constructor-এ পাঠিয়ে দেয়। এতে কোড মেইনটেইন এবং টেস্ট করা সহজ হয় (Loose Coupling)।
Implementation:
using Microsoft.AspNetCore.Mvc;
using ServiceContracts;
using ServiceContracts.DTO;
namespace CRUDExample.Controllers
{
public class PersonsController : Controller
{
// Readonly field যাতে ভুল করে অন্য কোনো ডাটা অ্যাসাইন না হয়ে যায়
private readonly IPersonsService _personsService;
// Constructor Injection
public PersonsController(IPersonsService personsService)
{
_personsService = personsService;
}
[Route("persons/index")]
[Route("/")]
public IActionResult Index()
{
// Service থেকে ডাটা এনে View-তে পাঠানো হচ্ছে
List<PersonResponse> persons = _personsService.GetAllPersons();
return View(persons);
}
}
}
৩. Strongly Typed View এবং IEnumerable [Priority: 8/10]
The “Why”: View ফাইলে আমরা চাইলে @model List<PersonResponse> লিখতে পারতাম। কিন্তু লেকচারার @model IEnumerable<PersonResponse> ব্যবহার করতে বলেছেন। কারণ, IEnumerable হচ্ছে List, Array, বা অন্যান্য কালেকশনের প্যারেন্ট ইন্টারফেস। ভবিষ্যতে যদি Controller থেকে List-এর বদলে অন্য কোনো কালেকশন টাইপ পাঠানো হয়, তাহলেও View-এর কোড ব্রেক করবে না।
৪. HTML Table-এ ডাটা রেন্ডার করা [Priority: 9/10]
The “Why”: Controller থেকে আসা ডাটা (Model) আমরা foreach লুপ চালিয়ে এক এক করে টেবিলে প্রিন্ট করবো।
এখানে একটি গুরুত্বপূর্ণ বিষয় হলো Date of Birth ফরম্যাটিং। যদি কোনো Person-এর Date of Birth null থাকে এবং আমরা সরাসরি .ToString() কল করি, তাহলে অ্যাপ্লিকেশন ক্র্যাশ করবে (NullReferenceException)। তাই ?. (Null-conditional operator) ব্যবহার করা হয়েছে।
Implementation:
@* Index.cshtml *@
@model IEnumerable<ServiceContracts.DTO.PersonResponse>
@{
ViewBag.Title = "Persons";
}
<table class="table w-100 mt">
<thead>
<tr>
<th>Person Name</th>
<th>Email</th>
<th>Date of Birth</th>
<th>Age</th>
<th>Gender</th>
<th>Country</th>
<th>Address</th>
<th>Receive Newsletters</th>
</tr>
</thead>
<tbody>
@foreach (var person in Model)
{
<tr>
<td>@person.PersonName</td>
<td>@person.Email</td>
@* ? অপারেটর চেক করবে DateOfBirth null কি না। null না হলেই কেবল ToString কাজ করবে *@
<td>@person.DateOfBirth?.ToString("dd MMM yyyy")</td>
<td>@person.Age</td>
<td>@person.Gender</td>
<td>@person.Country</td>
<td>@person.Address</td>
<td>@person.ReceiveNewsLetters</td>
</tr>
}
</tbody>
</table>
৫. Debugging এবং Execution Flow [Priority: 6/10]
The “Why”: কোডে কোনো লজিক্যাল ভুল থাকলে বা ডাটায় কোনো সমস্যা হলে Breakpoint খুব কাজে দেয়। Controller-এর return View(persons); লাইনে Breakpoint বসালে দেখা যায় Controller ঠিকঠাক ডাটা আনছে কি না। আবার View-এর foreach লুপে Breakpoint বসালে Visual Studio-এর ‘Autos’ বা ‘Locals’ উইন্ডোতে Model-এর ভেতরের ভ্যালুগুলো লাইভ দেখা যায়।
🆕 .NET 10 & Modern Approaches (Primary Constructors)
.NET 8/9/10-এ C# 12 এর একটি দারুণ ফিচার হলো Primary Constructors। আগে Controller-এ Dependency Inject করার জন্য আলাদা ফিল্ড এবং Constructor ডিক্লেয়ার করতে হতো। এখন ক্লাস ডিক্লেয়ারেশনের সাথেই সেটা করা যায়, যা কোড অনেক ক্লিন রাখে।
Modern MVC Controller Implementation:
// .NET 10 Approach using Primary Constructor
public class PersonsController(IPersonsService personsService) : Controller
{
[Route("persons/index")]
[Route("/")]
public IActionResult Index()
{
// সরাসরি personsService ইউজ করা যাচ্ছে, আলাদা private field লাগে না
var persons = personsService.GetAllPersons();
return View(persons);
}
}
⌨️ IDE Shortcuts (Debugging)
যেহেতু এই লেকচারে Debugging নিয়ে কথা হয়েছে, তাই এর শর্টকাটগুলো জানা খুব জরুরি:
-
Visual Studio (Windows):
-
Toggle Breakpoint (ব্রেকপয়েন্ট বসানো/ওঠানো):
F9 -
Step Over (এক লাইন করে কোড রান করা):
F10 -
Step Into (মেথডের ভেতরে ঢোকা):
F11 -
Continue (পরের ব্রেকপয়েন্ট পর্যন্ত রান করা):
F5 -
Visual Studio Code:
-
Toggle Breakpoint:
F9 -
Step Over:
F10 -
Step Into:
F11 -
Continue:
F5(শর্টকাটগুলো দুই IDE-তেই সেম!)
💡 Best Practices (MVC Views & Controllers)
১. Use IEnumerable for Read-Only Data: View-তে ডাটা শুধুমাত্র দেখানোর জন্য IEnumerable<T> ব্যবহার করা বেস্ট প্র্যাকটিস, কারণ এটি Read-only এবং মেমোরি এফিশিয়েন্ট।
২. Avoid Logic in Views: View-তে ?.ToString("dd MMM yyyy") লেখাটা খুব কমন হলেও, একদম সলিড আর্কিটেকচারে এই ফরম্যাটিংয়ের কাজটা DTO (Data Transfer Object) বা Model ক্লাসের ভেতরেই করে নেওয়া উচিত। ভিউয়ের কাজ শুধু স্ট্রিংটা প্রিন্ট করা।
৩. Use Display Templates: একই ডেট ফরম্যাট যদি অ্যাপের ১০ জায়গায় লাগে, তবে বারবার .ToString("dd MMM yyyy") না লিখে MVC-এর DisplayTemplates ব্যবহার করা উচিত।
Persons-এর গ্রিড ভিউ তো রেডি হয়ে গেলো। এই টপিক নিয়ে কোনো প্রশ্ন আছে, নাকি আমরা নেক্সট লেকচার (Search Functionality)-তে মুভ করবো?