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

আগের লেকচারগুলোতে আমরা IActionFilter ব্যবহার করে সিঙ্ক্রোনাস ফিল্টার তৈরি করেছিলাম। কিন্তু রিয়েল-লাইফ প্রজেক্টে ফিল্টারের ভেতর থেকে ডেটাবেস কল, নেটওয়ার্ক রিকোয়েস্ট বা ফাইল সিস্টেম রিড করার মতো ভারী কাজ করতে হতে পারে। সিঙ্ক্রোনাস মেথডে এই কাজগুলো করলে অ্যাপ্লিকেশন স্লো হয়ে যায় বা থ্রেড ব্লক হয়। এই সমস্যার সমাধানই হলো Asynchronous Action Filters। আজ আমরা শিখব কীভাবে একটি ফিল্টারকে অ্যাসিঙ্ক্রোনাস করতে হয়। চলুন শুরু করি!


📝 Quick Revision Summary

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

  • The Need: ফিল্টারে কোনো async অপারেশন (যেমন: ডেটাবেস কল, এপিআই রিকোয়েস্ট) করার জন্য।
  • Interface: IActionFilter-এর পরিবর্তে IAsyncActionFilter ইমপ্লিমেন্ট করতে হয়।
  • Single Method: দুটি মেথড (OnActionExecuting এবং OnActionExecuted)-এর বদলে এখানে শুধু একটি মেথড থাকে: OnActionExecutionAsync
  • The next Delegate: await next() কল করার মাধ্যমে পরবর্তী ফিল্টার বা অ্যাকশন মেথডকে কল করা হয়।
  • Execution Flow: await next()-এর আগের কোড হলো Before Logic (যা OnActionExecuting-এর সমতুল্য) এবং পরের কোড হলো After Logic (যা OnActionExecuted-এর সমতুল্য)।
  • Crucial Rule: কখনোই await next() কল করতে ভুলবেন না, নতুবা রিকোয়েস্ট শর্ট-সার্কিট (Short-circuit) হয়ে যাবে এবং অ্যাকশন মেথড আর রান করবে না।

🔍 Comprehensive Breakdown

১. Why Asynchronous Filters? [Priority: 10/10]

The “Why”: যখন আমরা IActionFilter ব্যবহার করি, তখন এর মেথডগুলো সিঙ্ক্রোনাস হয়। এর ভেতরে যদি আপনি কোনো ডেটাবেস কল (যেমন await _dbContext.Users.ToListAsync()) করতে যান, তবে থ্রেড ব্লক হয়ে যাবে, যা ASP.NET Core-এর পারফরম্যান্সের জন্য খুবই ক্ষতিকর। এই কারণে আধুনিক ওয়েব অ্যাপ্লিকেশনে ডেটাবেস বা এক্সটার্নাল সার্ভিস কলের জন্য আমরা IAsyncActionFilter ব্যবহার করি, যাতে মেইন থ্রেড ব্লক না হয়ে অ্যাসিন্ক্রোনাসলি কাজ হয়।

২. Syntax and Interface Changes [Priority: 10/10]

একটি সিঙ্ক্রোনাস ফিল্টারকে অ্যাসিন্ক্রোনাস ফিল্টারে রূপান্তর করা খুবই সহজ।

  • IActionFilter পরিবর্তন করে IAsyncActionFilter করতে হবে।
  • দুটি আলাদা মেথডের বদলে শুধু OnActionExecutionAsync মেথডটি লিখতে হবে।
  • মিডলওয়্যারের মতো এখানেও next ডেলিগেট ব্যবহার করতে হবে।

৩. Code Implementation: Converting the Filter [Priority: 10/10]

লেকচারে আমাদের আগের ResponseHeaderActionFilter-কে অ্যাসিন্ক্রোনাস করা হয়েছে। কোডটি নিচে দেওয়া হলো:

using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Logging;
using System.Threading.Tasks;
 
// IAsyncActionFilter ব্যবহার করা হচ্ছে (IOrderedFilter-এর লজিক আগের মতোই থাকবে)
public class ResponseHeaderActionFilter : IAsyncActionFilter, IOrderedFilter
{
    private readonly ILogger<ResponseHeaderActionFilter> _logger;
    private readonly string _key;
    private readonly string _value;
    
    public int Order { get; set; }
 
    public ResponseHeaderActionFilter(ILogger<ResponseHeaderActionFilter> logger, string key, string value, int order)
    {
        _logger = logger;
        _key = key;
        _value = value;
        Order = order;
    }
 
    // শুধুমাত্র একটি অ্যাসিন্ক্রোনাস মেথড
    public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
    {
        // ----------------------------------------------------
        // BEFORE LOGIC (Equivalent to OnActionExecuting)
        // ----------------------------------------------------
        _logger.LogInformation("{FilterName}.{MethodName} executes - BEFORE", nameof(ResponseHeaderActionFilter), nameof(OnActionExecutionAsync));
        
        // (আপনি চাইলে এখানে await দিয়ে ডেটাবেস কল করতে পারেন)
 
        // ----------------------------------------------------
        // CALLING THE NEXT FILTER OR ACTION METHOD (CRITICAL)
        // ----------------------------------------------------
        // এই লাইনটি পরবর্তী ফিল্টার বা অ্যাকশন মেথডকে ট্রিগার করে
        await next(); 
 
        // ----------------------------------------------------
        // AFTER LOGIC (Equivalent to OnActionExecuted)
        // ----------------------------------------------------
        _logger.LogInformation("{FilterName}.{MethodName} executes - AFTER", nameof(ResponseHeaderActionFilter), nameof(OnActionExecutionAsync));
        
        // Response header অ্যাড করা হচ্ছে (আগের মতোই)
        context.HttpContext.Response.Headers[_key] = _value;
    }
}
 

৪. Understanding the Flow (কিভাবে কাজ করছে?) [Priority: 9/10]

লেকচারার বিষয়টিকে Middleware-এর কনসেপ্টের সাথে সুন্দরভাবে তুলনা করেছেন।

  • যখন রিকোয়েস্ট আসে, তখন OnActionExecutionAsync ফায়ার হয় এবং await next()-এর উপরের অংশ রান করে।
  • এরপর await next() কল হওয়ার সাথে সাথে কন্ট্রোল পরবর্তী ফিল্টারে চলে যায়। যদি আর কোনো ফিল্টার না থাকে, তবে কন্ট্রোল চলে যায় Action Method-এ।
  • Action Method তার কাজ শেষ করে রেজাল্ট রিটার্ন করলে, কন্ট্রোল আবার এই ফিল্টারে ফিরে আসে এবং await next()-এর নিচের অংশ রান করে।

এই কারণেই একটি মেথডের ভেতরেই Before এবং After লজিক লেখা সম্ভব হয়।

৫. What happens if you forget await next()? [Priority: 10/10]

এটি ডেভেলপারদের করা সবচেয়ে কমন একটি ভুল। যদি আপনি ভুল করে বা ইচ্ছে করে await next() কল না করেন, তবে এটি কোনো Compile-time error দেবে না। কিন্তু অ্যাপ্লিকেশন রান করলে আপনি দেখবেন যে আপনার Action Method কখনোই কল হচ্ছে না! কারণ next() কল না করার মানে হলো আপনি পাইপলাইনকে সেখানেই ব্রেক বা শর্ট-সার্কিট (Short-circuit) করে দিলেন। (কখন ইচ্ছে করে শর্ট-সার্কিট করতে হয়, তা আমরা পরের লেকচারে জানব)।


🚀 Best Practices & .NET Modern Updates

Best Practices for Async Filters:

  1. Prefer Async over Sync: আধুনিক .NET অ্যাপ্লিকেশনে সব সময় IAsyncActionFilter ব্যবহার করার চেষ্টা করুন, এমনকি যদি বর্তমানে কোনো await অপারেশন নাও থাকে। কারণ ভবিষ্যতে যদি লজিক অ্যাড করতে হয়, তবে কোড রিফ্যাক্টর করা সহজ হবে। তাছাড়া পারফরম্যান্সের দিক থেকেও অ্যাসিন্ক্রোনাস মডেলটি অনেক বেশি স্কেলেবল।
  2. Exception Handling: await next()-এর চারপাশে try-catch ব্লক ব্যবহার করে আপনি খুব সহজেই অ্যাকশন মেথডে ঘটে যাওয়া কোনো এরর ফিল্টার লেভেলে হ্যান্ডেল করতে পারেন।

.NET 10 Modern Code implementation (Endpoint Filters): .NET 10 বা Minimal APIs-এ এই একই কনসেপ্ট IEndpointFilter বা AddEndpointFilter দিয়ে করা হয়। সেখানেও অ্যাসিন্ক্রোনাস প্যাটার্ন এবং next ডেলিগেট ব্যবহার করা হয়:

// Minimal API .NET 10 Async Filter Concept
app.MapGet("/api/persons", async () => 
{
    return "Data from Action Method";
})
.AddEndpointFilter(async (context, next) =>
{
    Console.WriteLine("Before executing the logic..."); // BEFORE LOGIC
    
    // Call the endpoint handler
    var result = await next(context); 
    
    Console.WriteLine("After executing the logic...");  // AFTER LOGIC
    
    return result;
});
 

আশা করি অ্যাসিন্ক্রোনাস ফিল্টার সম্পর্কে আপনার পরিষ্কার ধারণা তৈরি হয়েছে। পরবর্তী লেকচারে আমরা শিখব কীভাবে এবং কেন ফিল্টারকে শর্ট-সার্কিট (Short circuit) করতে হয়।