হ্যালো হাসিব! এই লেকচারটি মূলত কোড অপটিমাইজেশন এবং রিফ্যাক্টরিং নিয়ে। আগের লেকচারে আমরা Person Name-এর জন্য যে Sorting লজিকটি লিখেছিলাম, সেটি সব কলামের জন্য বারবার কপি-পেস্ট করলে কোড অনেক বড় ও হিজিবিজি হয়ে যেত। এই সমস্যা সমাধানের জন্য আমরা Partial View-এর সাহায্য নিয়েছি এবং শেষে কিছু ছোটখাটো Bug Fix করেছি।
চলো প্রথমে লেকচারটির একটি কুইক সামারি দেখে নিই।
📌 Quick Summary for Revision
- DRY Principle: একই Sorting লজিক বারবার না লিখে কোড রিইউজেবল করার সিদ্ধান্ত।
- Partial View Creation:
Views/Sharedফোল্ডারে_GridColumnHeader.cshtmlনামের একটি Partial View তৈরি করা। - Dynamic View Data: Main View থেকে Partial View-তে
ViewDataDictionaryব্যবহার করেColumnNameএবংDisplayNameপাস করা। - Razor Explicit Expression: HTML অ্যাট্রিবিউটের ভেতরে ভেরিয়েবল ঠিকমতো পার্স করার জন্য
@(...)সিনট্যাক্স ব্যবহার করা। - Invoking Partial View:
@await Html.PartialAsync()ব্যবহার করে প্রতিটি কলামের জন্য Partial View কল করা। - UI Jitter Fix: টেবিলের কলামগুলোর প্রস্থ ফিক্সড রাখার জন্য
<td>ট্যাগে inline CSS (width) ব্যবহার করা। - Data Binding Fix:
PersonsService-এ Country Name লোড না হওয়ার ইস্যুটি সমাধান করা।
🚀 Comprehensive Breakdown & The “Why”
নিচে লেকচারের প্রতিটি কনসেপ্ট বিস্তারিত এবং কারণসহ এক্সপ্লেইন করা হলো:
১. Partial View তৈরি করা [Priority: 10/10]
The “Why”: MVC-তে DRY (Don’t Repeat Yourself) প্রিন্সিপাল মেইনটেইন করার জন্য Partial View খুব জনপ্রিয়। আমাদের কলাম আছে ৮-৯টি। প্রতিটির জন্য যদি ১৫ লাইনের if-else ব্লক লিখি, তাহলে শুধু টেবিল হেডারের জন্যই ১০০+ লাইন কোড হয়ে যাবে। এর বদলে আমরা লজিকটি একটি আলাদা ফাইলে (_GridColumnHeader.cshtml) রাখবো এবং প্রয়োজন অনুযায়ী কল করবো।
- Convention অনুযায়ী Partial View-এর নামের আগে আন্ডারস্কোর (
_) দিতে হয়।
Implementation (Partial View - _GridColumnHeader.cshtml):
@* _GridColumnHeader.cshtml *@
<th>
@* ViewBag থেকে ডাইনামিক কলাম নেম এবং ডিসপ্লে নেম রিসিভ করা হচ্ছে *@
@if (ViewBag.CurrentSortBy == ViewBag.ColumnName && ViewBag.CurrentSortOrder == nameof(SortOrderOptions.ASC))
{
<a href="~/persons/index?searchBy=@ViewBag.CurrentSearchBy&searchString=@ViewBag.CurrentSearchString&sortBy=@(ViewBag.ColumnName)&sortOrder=@nameof(SortOrderOptions.DESC)">
@ViewBag.DisplayName <i class="fa-solid fa-sort-up"></i>
</a>
}
else if (ViewBag.CurrentSortBy == ViewBag.ColumnName && ViewBag.CurrentSortOrder == nameof(SortOrderOptions.DESC))
{
<a href="~/persons/index?searchBy=@ViewBag.CurrentSearchBy&searchString=@ViewBag.CurrentSearchString&sortBy=@(ViewBag.ColumnName)&sortOrder=@nameof(SortOrderOptions.ASC)">
@ViewBag.DisplayName <i class="fa-solid fa-sort-down"></i>
</a>
}
else
{
<a href="~/persons/index?searchBy=@ViewBag.CurrentSearchBy&searchString=@ViewBag.CurrentSearchString&sortBy=@(ViewBag.ColumnName)&sortOrder=@nameof(SortOrderOptions.ASC)">
@ViewBag.DisplayName
</a>
}
</th>
- Note: এখানে
@(ViewBag.ColumnName)ব্যবহার করা হয়েছে। ব্র্যাকেট দেওয়ার কারণ হলো, Razor ইঞ্জিন যেন ঠিকমতো বুঝতে পারে ভেরিয়েবলের নাম কোথায় শেষ হয়েছে এবং HTML স্ট্রিং কোথায় শুরু হয়েছে।
২. Partial View কল করা এবং Data পাস করা [Priority: 10/10]
The “Why”: Partial View নিজে থেকে জানে না সে কোন কলামের জন্য কাজ করছে (Email নাকি Age)। তাই মেইন ভিউ (Index.cshtml) থেকে কল করার সময় একটি ViewDataDictionary তৈরি করে তার ভেতর Key-Value পেয়ার হিসেবে ডাটা পাস করতে হয়।
Implementation (Main View - Index.cshtml):
<thead>
<tr>
@* Person Name কলাম *@
@await Html.PartialAsync("_GridColumnHeader", new ViewDataDictionary(ViewData) {
{ "ColumnName", nameof(PersonResponse.PersonName) },
{ "DisplayName", "Person Name" }
})
@* Email কলাম *@
@await Html.PartialAsync("_GridColumnHeader", new ViewDataDictionary(ViewData) {
{ "ColumnName", nameof(PersonResponse.Email) },
{ "DisplayName", "Email" }
})
@* এভাবে বাকি সব কলামের জন্য কল করতে হবে... *@
</tr>
</thead>
৩. Table Column Width ফিক্স করা [Priority: 5/10]
The “Why”: যখন আমরা আইকনে ক্লিক করে সর্ট করি, তখন আইকন অ্যাড/রিমুভ হওয়ার কারণে টেবিলের কলামগুলোর সাইজ ছোট-বড় হতে থাকে (Jumping effect)। এটি ইউজার এক্সপেরিয়েন্সের জন্য খারাপ। তাই <tbody> এর ভেতরের <td> গুলোতে পার্সেন্টেজ অনুযায়ী ফিক্সড উইডথ (width: 15%) দেওয়া হয়েছে।
৪. Service Layer Bug Fix (Country Name Missing) [Priority: 8/10]
The “Why”: UI-তে Country Name দেখা যাচ্ছিল না। কারণ, PersonsService-এর GetAllPersons() মেথডে সরাসরি .ToPersonResponse() কল করা হচ্ছিল, যা শুধু Person টেবিলের ডাটা ম্যাপ করে। কিন্তু Foreign Key থেকে Country Name আনার জন্য লেকচারার আগেই একটি প্রাইভেট মেথড ConvertPersonToPersonResponse() তৈরি করে রেখেছিলেন। মেথড কলিংটি আপডেট করতেই সমস্যার সমাধান হয়ে যায়।
Implementation (PersonsService.cs):
// আগে ছিল:
// return _persons.Select(temp => temp.ToPersonResponse()).ToList();
// আপডেট করা হলো:
return _persons.Select(temp => ConvertPersonToPersonResponse(temp)).ToList();
🆕 .NET 10 & Modern Approaches (Partial Tag Helper)
লেকচারে @await Html.PartialAsync() ব্যবহার করে Partial View কল করা হয়েছে। এটি ক্লাসিক অ্যাপ্রোচ। .NET Core 2.1 এর পর থেকে এবং .NET 10-এ সবচেয়ে মডার্ন এবং রিডেবল অ্যাপ্রোচ হলো Partial Tag Helper ব্যবহার করা। এটি দেখতে একদম HTML ট্যাগের মতো!
Modern Implementation (Index.cshtml):
<partial name="_GridColumnHeader" view-data='new ViewDataDictionary(ViewData) {
{ "ColumnName", nameof(PersonResponse.PersonName) },
{ "DisplayName", "Person Name" }
}' />
এটি দেখতে আগের চেয়ে অনেক বেশি ক্লিন এবং HTML-ফ্রেন্ডলি।
⌨️ IDE Shortcuts
-
Surround With (যেকোনো কোড ব্লক দ্রুত if/for/try-এর ভেতর ঢোকানো):
-
Visual Studio: কোড সিলেক্ট করে
Ctrl+K,Ctrl+S -
Visual Studio Code: কোড সিলেক্ট করে
Ctrl+Shift+P-> টাইপ করোSurround With -
Extract to Method (কোনো বড় কোড ব্লক কেটে আলাদা মেথড বানানো):
-
Visual Studio & VS Code: কোড সিলেক্ট করে
Ctrl+.চাপলে “Extract Method” অপশন আসবে।
💡 Best Practices (Partial Views & UI)
১. Avoid Inline CSS: লেকচারার নিজেই বলেছেন, style="width: 15%" এভাবে inline CSS লেখা গুড প্র্যাকটিস নয়। এর বদলে wwwroot/stylesheet.css ফাইলে .w-15 { width: 15%; } ক্লাস তৈরি করে সেটি HTML-এ ক্লাস হিসেবে (class="w-15") ব্যবহার করা উচিত।
২. Partial View vs View Component: যদি এমন কোনো রিইউজেবল UI অংশ থাকে, যার জন্য আলাদা করে ডাটাবেস কল বা কমপ্লেক্স লজিক প্রসেস করতে হয়, তবে Partial View এর বদলে View Component ব্যবহার করা বেস্ট প্র্যাকটিস। তবে এই লেকচারের মতো শুধু স্ট্রিং পাস করার জন্য Partial View-ই পারফেক্ট।
৩. Explicit Razor @(...): HTML এর ভেতর যখনই C# ভেরিয়েবল ব্যবহার করবে এবং তার ঠিক সাথেই কোনো স্ট্রিং বা ক্যারেক্টার থাকবে, তখন সবসময় @(Variable) ব্যবহার করবে। এতে রানটাইম এরর হওয়ার সম্ভাবনা শূন্য হয়ে যায়।
এই লেকচারের মাধ্যমে আমাদের Index পেজ বা গ্রিডের কাজ সম্পূর্ণ হলো! এর পরের লেকচারে আমরা Persons Create পেজ (Insert Operation) নিয়ে কাজ শুরু করতে পারি। এই অংশে কি আর কোনো প্রশ্ন আছে?