স্বাগতম! আমরা এখন আপনার কোর্সের Section 20 (Filters)-এর চতুর্থ লেকচারে (ViewData in Action Filter) চলে এসেছি। আগের লেকচারে আমরা দেখেছিলাম কীভাবে Action Filter-এর OnActionExecuting (Action Method-এর আগে) ব্যবহার করে প্যারামিটার ভ্যালিডেট করা যায়। আজ আমরা দেখব Action Filter-এর দ্বিতীয় মেথডটি—OnActionExecuted (Action Method-এর পরে) ব্যবহার করে কীভাবে Controller-এর কাজ কমিয়ে কোড ক্লিন করা যায়।
📝 Quick Revision Summary
- উদ্দেশ্য: Controller-এর Action Method থেকে
ViewBagবাViewDataঅ্যাসাইন করার বয়লারপ্লেট কোড (boilerplate code) সরিয়ে Action Filter-এ নিয়ে যাওয়া, যাতে Controller-এর কোড ক্লিন থাকে। - সমস্যা:
OnActionExecutedমেথডে সরাসরিActionArguments(প্যারামিটারের ভ্যালু) পাওয়া যায় না। - সমাধান:
OnActionExecutingমেথডে প্যারামিটারগুলোHttpContext.Items-এ সেভ করে রাখা এবং পরেOnActionExecutedমেথডে সেখান থেকে রিড করা। - ViewData অ্যাক্সেস:
context.Controller-কে আপনার স্পেসিফিক Controller ক্লাসে টাইপকাস্ট করে তারপরViewDataবাViewBagঅ্যাক্সেস করতে হয়।
🔍 Comprehensive Breakdown
১. Action Method-এর কোড সিম্প্লিফাই করার প্রয়োজনীয়তা [Priority: 10/10]
সমস্যা (The Why): সাধারণত একটি Index অ্যাকশন মেথডে সার্চিং বা সর্টিং করার পর, ইউজারের সিলেক্ট করা ড্রপডাউন বা টেক্সটবক্সের ভ্যালু ধরে রাখার জন্য (persist করার জন্য) আমরা ViewBag বা ViewData-তে ভ্যালু অ্যাসাইন করি। যেমন:
ViewBag.CurrentSearchBy = searchBy;
ViewBag.CurrentSearchString = searchString;
ViewBag.CurrentSortBy = sortBy;
ViewBag.CurrentSortOrder = sortOrder;
কিন্তু এটি মূল বিজনেস লজিক নয়; এটি শুধু ভিউ (UI) মেইনটেইন করার কোড। Action Method-এ এসব কোড রাখলে মূল লজিক ফোকাস হারায়। তাই আমরা এই কাজটি Controller থেকে সরিয়ে ActionFilter-এর OnActionExecuted মেথডে করব।
২. Data Transfer: OnActionExecuting থেকে OnActionExecuted [Priority: 9/10]
চ্যালেঞ্জ: OnActionExecuting (যেটি আগে কল হয়) মেথডে context.ActionArguments ব্যবহার করে আমরা সহজেই ইউজারের ইনপুট (যেমন searchBy) পেতে পারি। কিন্তু OnActionExecuted (যেটি Action Method-এর পরে কল হয়) মেথডে ActionArguments প্রপার্টিটি সরাসরি থাকে না।
সমাধান (HttpContext.Items): HttpContext.Items হলো একটি ডিকশনারি, যা একটি সিঙ্গেল রিকোয়েস্টের লাইফসাইকেলে ডেটা শেয়ার করতে ব্যবহৃত হয়।
তাই আমরা OnActionExecuting-এ প্যারামিটারগুলো Items-এ সেভ করে রাখব এবং OnActionExecuted-এ সেটি রিড করব।
// ১. OnActionExecuting (Action Method-এর আগে)
public void OnActionExecuting(ActionExecutingContext context)
{
// ActionArguments-কে HttpContext.Items-এ 'arguments' নামের একটি Key-তে সেভ করা হলো
context.HttpContext.Items["arguments"] = context.ActionArguments;
// (আগের লেকচারের Validation কোড এখানে থাকবে...)
}
৩. OnActionExecuted-এ ViewData ম্যানিপুলেশন [Priority: 10/10]
এখন Action Method এক্সিকিউট হওয়ার পর আমরা ফিল্টারে ফিরে আসব এবং ViewData আপডেট করব।
চ্যালেঞ্জ: OnActionExecuted-এর context-এ সরাসরি ViewData নেই।
সমাধান: context.Controller-কে আমাদের PersonsController-এ টাইপকাস্ট করতে হবে।
// ২. OnActionExecuted (Action Method-এর পরে)
public void OnActionExecuted(ActionExecutedContext context)
{
// ১. Controller-কে টাইপকাস্ট করে ViewData অ্যাক্সেস করার ব্যবস্থা করা
var personsController = (PersonsController)context.Controller;
// ২. HttpContext.Items থেকে ActionArguments রিড করা
if (context.HttpContext.Items.ContainsKey("arguments") &&
context.HttpContext.Items["arguments"] is IDictionary<string, object?> parameters)
{
// ৩. ডিকশনারি থেকে ভ্যালু নিয়ে ViewData-তে অ্যাসাইন করা
if (parameters.ContainsKey("searchBy"))
{
personsController.ViewData["CurrentSearchBy"] = Convert.ToString(parameters["searchBy"]);
}
if (parameters.ContainsKey("searchString"))
{
personsController.ViewData["CurrentSearchString"] = Convert.ToString(parameters["searchString"]);
}
if (parameters.ContainsKey("sortBy"))
{
personsController.ViewData["CurrentSortBy"] = Convert.ToString(parameters["sortBy"]);
}
if (parameters.ContainsKey("sortOrder"))
{
personsController.ViewData["CurrentSortOrder"] = Convert.ToString(parameters["sortOrder"]);
}
}
// ৪. Search Fields-এর ডিকশনারিও এখানে অ্যাড করা যায় (যাতে Controller আরও ক্লিন হয়)
personsController.ViewBag.SearchFields = new Dictionary<string, string>()
{
{ nameof(PersonResponse.PersonName), "Person Name" },
{ nameof(PersonResponse.Email), "Email" },
// ... other fields
};
}
৪. ফলাফল (The Result) [Priority: 8/10]
এর ফলে আপনার Controller-এর Index মেথডটি আগের চেয়ে অনেক ছোট এবং ক্লিন হয়ে যাবে। সেখানে শুধু ডেটাবেস/সার্ভিস থেকে ডেটা আনার কোড থাকবে, কোনো ViewBag বা ViewData অ্যাসাইনমেন্ট থাকবে না। ফিল্টারটি ব্যাকগ্রাউন্ডে ম্যাজিকের মতো UI মেইনটেইন করার জন্য ডেটা প্রোভাইড করবে।
🚀 Best Practices & .NET Updates
Best Practices for Controller Casting:
লেকচারে (PersonsController)context.Controller ব্যবহার করে হার্ড-কাস্টিং করা হয়েছে। এটি একটি নির্দিষ্ট কন্ট্রোলারের সাথে ফিল্টারটিকে শক্তভাবে যুক্ত (Tightly coupled) করে ফেলে।
এর চেয়ে ভালো প্র্যাকটিস হলো Controller বেস ক্লাসে কাস্ট করা, কারণ ViewData প্রপার্টিটি মূলত বেস Controller ক্লাসেই থাকে।
Better Approach:
if (context.Controller is Controller baseController)
{
baseController.ViewData["CurrentSearchBy"] = searchByValue;
baseController.ViewBag.SearchFields = searchFieldsDict;
}
এতে করে এই ফিল্টারটি শুধু PersonsController নয়, প্রজেক্টের যেকোনো Controller-এর সাথে কাজ করতে পারবে।
Minimal APIs Context (No ViewData):
মনে রাখবেন, .NET-এর Minimal APIs-এ কোনো View, ViewData বা ViewBag থাকে না। Minimal APIs সাধারণত SPA (Angular/React) বা মোবাইল অ্যাপের জন্য JSON রেসপন্স পাঠায়। তাই ఈ লেকচারের কনসেপ্টটি শুধুমাত্র ট্র্যাডিশনাল MVC (Model-View-Controller) আর্কিটেকচারের জন্য প্রযোজ্য।
পরবর্তী লেকচারে আমরা Serilog Structured Logging নিয়ে আলোচনা করব।