یک نکتهی تکمیلی: کار با اینترفیس IMiddleware جهت تعریف میانافزارهای سفارشی
اگر امروز قصد تعریف میانافزارهای سفارشی را دارید، بهتر است از روش باز public async Task Invoke(HttpContext context) که در این مطلب معرفی شد، دیگر استفاده نکنید؛ چون مهمترین محدودیتهای آن، داشتن طول عمر Singleton غیرقابل تغییر و همچنین عدم امکان پیاده سازی اینترفیس IDisposable در آن جهت پاکسازی خودکار منابع است. امروز روش توصیه شده، استفاده از اینترفیس IMiddleware است. در این حالت متد Task Invoke فوق، به متد مشخص و ثابت Task InvokeAsync(HttpContext context, RequestDelegate next) تغییر میکند. چون در این حالت دیگر نمیتوان پارامترهای این متد مشخص را مانند قبل که اینترفیسی را پیاده سازی نمیکرد، به صورت پویا کم و زیاد کرد، میتوان سرویسهای مدنظر را به سازندهی کلاس، تزریق کرد. به همین جهت نیاز است، آنرا به نحو زیر به سیستم تزریق وابستگیها معرفی کرد:
builder.Services.AddTransient<MyNewMiddleware>();
این الزام به تعریف آن به صورت یک سرویس رسمی، مزیتهای زیر را به همراه دارد:
الف) میتوان طول عمری، غیر از Singleton را هم در صورت نیاز، تعریف کرد (و مشکل کار با سرویسهایی با طول عمرهای غیر از Singleton کمتر میشود).
ب) چون طول عمر این میانافزار اکنون توسط سیستم تزریق وابستگیها مدیریت میشود، اگر این میانافزار اینترفیس IDisposable را پیاده سازی کند، کار پاکسازی منابع آن خودکار خواهد شد.
نکته 1: روش معرفی آن به سیستم تزریق وابستگیها، به صورت Concrete type است؛ یعنی اصل کلاس باید معرفی شود (مانند سطر فوق) و نه اینکه به صورت متداول زیر به همراه ذکر اینترفیس IMiddleware باشد:
builder.Services.AddTransient<IMiddleware, MyNewMiddleware>();
مابقی کار با آن، با میانافزارهای متداول، تفاوتی ندارد. یعنی قسمت UseMiddleware آن یکی است:
app.UseMiddleware<MyNewMiddleware>();
بنابراین این روش نسبت به روش متداول قبلی، دو تفاوت پیاده سازی اینترفیس مشخص IMiddleware و ثبت کلاس آن به صورت یک سرویس رسمی را دارد؛ مابقی نکات آن، مانند قبل است.
نکته 2: اگر از Scrutor برای ثبت خودکار سرویسهای برنامه استفاده میکنید، روش ثبت خودکار اینگونه سرویسها به صورت زیر و با استفاده از متد ()AsSelf است:
services.Scan(scan => scan.FromAssembliesOf(typeof(IDataSeedersRunner)) .AddClasses(classes => classes.Where(type => { var allInterfaces = type.GetInterfaces(); return allInterfaces.Contains(typeof(IMiddleware)) && allInterfaces.Contains(typeof(ISingletonService)); })) .AsSelf() .WithSingletonLifetime());
در این مثال، تمام IMiddleware هایی که با نشانگر ISingletonService هم مزین شدهاند، یافت شده و به صورت Concrete type هایی، با طول عمر Singleton، به سیستم تزریق وابستگیها اضافه میشوند.