স্বাগতম! আমরা এখন আপনার কোর্সের Section 20 (Filters)-এর “IOrderedFilter” (লেকচার ২৯১) টপিকে আছি।

আগের লেকচারে আমরা দেখেছিলাম [TypeFilter(..., Order = 1)] ব্যবহার করে কীভাবে মেথড বা কন্ট্রোলার লেভেলে ফিল্টারের সিরিয়াল চেঞ্জ করা যায়। কিন্তু সমস্যাটি ছিল Global Filters নিয়ে। Program.cs-এ যখন আমরা কাস্টম আর্গুমেন্টসহ গ্লোবাল ফিল্টার অ্যাড করি, তখন সেখানে সরাসরি Order সেট করার কোনো অপশন থাকে না। আজ আমরা শিখব কীভাবে IOrderedFilter ইন্টারফেস ব্যবহার করে এই সমস্যার সমাধান করা যায় এবং ফিল্টারগুলো আরও ফ্লেক্সিবল করা যায়।


📝 Quick Revision Summary

ভবিষ্যতে দ্রুত রিভিশন দেওয়ার জন্য আজকের লেকচারের মূল পয়েন্টগুলো নিচে দেওয়া হলো:

  • The Problem: options.Filters.Add(new MyFilter(logger, "Key"))—এখানে Order সেট করার বিল্ট-ইন প্রপার্টি নেই।
  • The Solution (IOrderedFilter): ফিল্টার ক্লাসে IActionFilter-এর পাশাপাশি IOrderedFilter ইন্টারফেস ইমপ্লিমেন্ট করা।
  • Property Requirement: এই ইন্টারফেসে শুধুমাত্র একটি প্রপার্টি থাকে— int Order { get; set; }
  • How it works: ASP.NET Core রানটাইম নিজে থেকেই চেক করে যে ফিল্টারটি IOrderedFilter ইমপ্লিমেন্ট করেছে কি না। যদি করে থাকে, তবে এটি সেই Order ভ্যালুটি এক্সিকিউশন সিকোয়েন্সের জন্য ব্যবহার করে।
  • Constructor Parameter: ডাইনামিকভাবে Order সেট করার জন্য ফিল্টারের Constructor-এ একটি order প্যারামিটার রিসিভ করে সেটি প্রপার্টিতে অ্যাসাইন করা হয়।
  • TypeFilter Bug Note: যদিও ক্লাসে Order প্রপার্টি আছে, তবুও মেথড/কন্ট্রোলারে ব্যবহারের সময় [TypeFilter(..., Order = 1)] এবং আর্গুমেন্ট অ্যারে উভয় জায়গাতেই একই অর্ডার ভ্যালু দেওয়া সেফ প্র্যাকটিস।

🔍 Comprehensive Breakdown

১. Why IOrderedFilter? (প্রয়োজনীয়তা) [Priority: 10/10]

আমরা জানি, Program.cs-এ ফিল্টার অ্যাড করার দুটি উপায় আছে: ১. Generic Way:

// এখানে আপনি Order পাস করতে পারেন
options.Filters.Add<ResponseHeaderActionFilter>(order: 5); 
 

সমস্যা: এই পদ্ধতিতে ফিল্টারে কাস্টম আর্গুমেন্ট (যেমন আমাদের key এবং value) পাঠানো যায় না।

২. Instance Way:

// এখানে কাস্টম আর্গুমেন্ট পাঠানো যায়, কিন্তু Order প্রপার্টি পাস করার জায়গা নেই!
options.Filters.Add(new ResponseHeaderActionFilter(logger, "GlobalKey", "GlobalValue"));
 

এই দ্বিতীয় পদ্ধতিটি ব্যবহার করার সময় Order কন্ট্রোল করার জন্যই মূলত IOrderedFilter ইন্টারফেসটির প্রয়োজন হয়।

২. Implementing IOrderedFilter [Priority: 10/10]

আমাদের ফিল্টার ক্লাসটিকে এখন দুটি ইন্টারফেস ইমপ্লিমেন্ট করতে হবে।

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
 
// IActionFilter-এর সাথে IOrderedFilter যুক্ত করা হলো
public class ResponseHeaderActionFilter : IActionFilter, IOrderedFilter
{
    private readonly ILogger<ResponseHeaderActionFilter> _logger;
    private readonly string _key;
    private readonly string _value;
 
    // ১. IOrderedFilter-এর একমাত্র প্রপার্টি
    public int Order { get; set; }
 
    // ২. Constructor-এ order প্যারামিটার রিসিভ করে প্রপার্টিতে সেট করছি
    public ResponseHeaderActionFilter(ILogger<ResponseHeaderActionFilter> logger, string key, string value, int order)
    {
        _logger = logger;
        _key = key;
        _value = value;
        Order = order; // Assigning to the interface property
    }
 
    public void OnActionExecuting(ActionExecutingContext context)
    {
        // ... (Before Action Logic)
    }
 
    public void OnActionExecuted(ActionExecutedContext context)
    {
         // ... (After Action Logic)
    }
}
 

৩. Applying the Custom Order (সব জায়গায়) [Priority: 9/10]

এখন যেহেতু আমাদের ফিল্টার নিজে থেকেই তার Order ভ্যালু রিসিভ করতে পারে, তাই আমরা ৩টি লেভেলেই নিজেদের ইচ্ছেমতো সিকোয়েন্স বানাতে পারি।

১. Global Level (Program.cs): Order = 2

builder.Services.AddControllersWithViews(options => {
    // এখানে ৪র্থ আর্গুমেন্ট হিসেবে 2 পাস করছি যা Constructor-এর `order` প্যারামিটারে যাবে
    options.Filters.Add(new ResponseHeaderActionFilter(logger, "Key-Global", "Value-Global", 2));
});
 

২. Controller Level: Order = 3

// TypeFilter-এর Order প্রপার্টি এবং আর্গুমেন্ট অ্যারের শেষ ভ্যালু দুটোই 3 দেওয়া হলো
[TypeFilter(typeof(ResponseHeaderActionFilter), Arguments = new object[] { "Key-Controller", "Value-Controller", 3 }, Order = 3)]
public class PersonsController : Controller
{
    // ...
}
 

৩. Method Level: Order = 1

[TypeFilter(typeof(ResponseHeaderActionFilter), Arguments = new object[] { "Key-Action", "Value-Action", 1 }, Order = 1)]
public IActionResult Index()
{
    // ...
}
 

💡 The “Bug” Mentioned in Lecture: লেকচারার বলেছেন যে, যদিও আমরা Constructor-এ order = 1 পাস করছি, তবুও [TypeFilter(..., Order = 1)] প্রপার্টিতেও ভ্যালুটি সেট করা উচিত। কারণ ASP.NET Core অনেক সময় TypeFilter-এর নিজস্ব Order প্রপার্টিকে বেশি প্রায়োরিটি দেয়। দুটিতেই একই ভ্যালু রাখলে আন-এক্সপেক্টেড বিহেভিয়ার থেকে বাঁচা যায়।

৪. Verification (কীভাবে রান করছে?) [Priority: 8/10]

উপরের কনফিগারেশন অনুযায়ী:

  • OnActionExecuting (Ascending): Order = 1 (Method) -> Order = 2 (Global) -> Order = 3 (Controller)।
  • OnActionExecuted (Descending): Order = 3 (Controller) -> Order = 2 (Global) -> Order = 1 (Method)।

ব্রেকপয়েন্ট বসিয়ে ডিবাগ করলে ঠিক এই সিরিয়ালেই ভ্যালুগুলো দেখা যাবে।


🚀 Best Practices & .NET Modern Updates

Best Practices for IOrderedFilter:

  1. YAGNI (You Aren’t Gonna Need It): লেকচারার খুব সুন্দর একটি কথা বলেছেন— “Unless you want to access data returned from one filter to another… you don’t have any particular necessity to define the exclusive order.” অর্থাৎ, যদি একটি ফিল্টার অন্য ফিল্টারের ডেটার উপর নির্ভরশীল না হয়, তবে অযথাই Order নিয়ে ঘাঁটাঘাঁটি করবেন না। ডিফল্ট অর্ডারই বেস্ট।
  2. Warning Ignore (BuildServiceProvider): Program.cs-এ BuildServiceProvider() কল করার কারণে যে ওয়ার্নিং দেয়, লেকচারার বলেছেন তা ইগনোর করতে। তবে প্রোডাকশন লেভেল অ্যাপ্লিকেশনে এটি ইগনোর করা উচিত নয়।

.NET 10 Modern Code implementation: .NET 10-এ গ্লোবাল ফিল্টারগুলো আর্গুমেন্টসহ রেজিস্টার করার সবচেয়ে সেফ এবং প্রফেশনাল طریقہ হলো IFilterFactory ব্যবহার করা, যাতে BuildServiceProvider()-এর ওয়ার্নিং একেবারেই না আসে। (এটি হয়তো কোর্সের পরের দিকে বিস্তারিত দেখানো হবে, তবে এখনকার জন্য আপনার IOrderedFilter কনসেপ্টটিই সবচেয়ে জরুরি)।

পরবর্তী লেকচারে আমরা একটি অত্যন্ত গুরুত্বপূর্ণ টপিক—Async Filters (অ্যাসিন্ক্রোনাস ফিল্টার) নিয়ে আলোচনা করব, যা ডেটাবেস বা ফাইল সিস্টেমের সাথে কাজ করার সময় অপরিহার্য।