در یک پروژه wasm برای ساخت یک داشبورد که شامل تعدادی کامپوننت بوده و هر کامپوننت خود در هر ثانیه درخواستی را به سرور ارسال میکندو رفرش یا رندر مجدد این کامپوننتها در یک تایمر فراخوانی میشود چه روش پیاده سازی پیشنهاد داده میشودکه تعداد زیاد درخواستها باعث کندی یا بلاک شدن ui نشود.و اینکه اگر به جای ارسال درخواستها توسط httpclient ، درخواستها به هاب signalr ارسال شود در بلاک شدن ui تاثیر دارد یا خیر؟
الگوی decorator، امکان محصور کردن یک شیء مفروض را با لایهای بر فراز آن میسر میکند. برای مثال بجای اینکه در تمام متدهای سرویسی از try/catch استفاده کنیم، میتوانیم این متدها را با یک ExceptionHandlingDecorator مزین کنیم و یا از این دست اعمال تکراری میتوان به لاگ کردن ورودی و خروجیهای یک متد و یا کش کردن اطلاعات آنها نیز اشاره کرد. حتی عملیاتی مانند تشخیص خواص تغییر یافتهی یک شیء در Entity framework نیز به کمک همین مزین کنندهها که شیء اصلی در حال استفاده را با ایجاد لایهای بر روی آنها محصور میکنند، انجام میشود. به این عملیات Aspect oriented programming و یا AOP نیز میگویند؛ در اینجا واژهی Aspect به اعمال مشترک و متداول موجود در برنامه اشاره میکند. در این مطلب قصد داریم نمونهای از این تزئین کنندهها را به کمک سیستم تزریق وابستگیهای NET Core. پیاده سازی کنیم.
پیاده سازی الگوی Decorator به کمک سیستم تزریق وابستگیهای NET Core.
مثال زیر را در نظر بگیرید که در آن یک سرویس تعریف شدهاست و در این بین استثنائی رخ دادهاست.
میخواهیم بدون تغییری در کدهای این کلاس، به متدهای آن در حین اجرای نهایی، یک try/catch را به همراه logging، اضافه کنیم. به همین جهت نیاز خواهیم داشت تا یک محصور کننده (تزئین کننده یا decorator در اینجا) را برای آن طراحی کنیم:
این محصور کننده نیز دقیقا همان ITaskService را پیاده سازی میکند؛ اما در سازندهی آن یک ITaskService را نیز دریافت میکند. علت اینجا است که توسط آن بتوان متدهای ITaskService تزریقی را اجرا کرد و بر روی آن اعمالی مانند کش کردن، لاگ کردن و مدیریت استثناءها و غیره را انجام داد. برای مثال در متد Run آن مشاهده میکنید که متد Run همان وهلهی تزریقی اجرا شدهاست؛ اما درون یک try/catch به همراه لاگ کردن جزئیات استثنای رخ داده.
مزیت اینکار، پیاده سازی اصل DRY یا Don't repeat yourself است. کاری که برای رفع این مشکل قرار است انجام دهیم، استفاده از یک تزئین کننده (محصور کننده)، کپسوله سازی اعمال تکراری و سپس اتصال آن به قسمتهای مختلف برنامه است. همچنین در این حالت اصل open closed principle نیز بهتر رعایت خواهد شد. از این جهت که کدهای تکراری برنامه به یک لایهی دیگر منتقل شدهاند و دیگر نیازی نیست برای تغییر آنها، کدهای قسمتهای اصلی برنامه را تغییر داد (کدهای برنامه باز خواهند بود برای توسعه و بسته برای تغییر).
پس از طراحی این تزئین کننده، اکنون نوبت به معرفی آن به سیستم تزریق وابستگیهای NET Core. است:
روش انجام اینکار را نیز در «قسمت ششم - دخالت در مراحل وهله سازی اشیاء توسط IoC Container» بیشتر بررسی کردهایم.
در اینجا هم میتوان در صورت نیاز اصل کلاس MyTaskService را بدون هیچ نوع تزئین کنندهای از سیستم تزریق وابستگیها دریافت کرد و یا اگر وهلهای از سرویس ITaskService را از آن درخواست کردیم، ابتدا شیء MyTaskServiceDecorator وهله سازی شده و سپس توسط آن یک نمونهی محصور شده و تزئین شدهی MyTaskService به فراخوان بازگشت داده خواهد شد.
ساده سازی معرفی تزئین کنندهها به سیستم تزریق وابستگیهای NET Core. به کمک Scrutor
در «قسمت هشتم - ساده سازی معرفی سرویسها توسط Scrutor» با کتابخانهی Scrutor آشنا شدیم. یکی دیگر از قابلیتهای آن، امکان ساده سازی تعریف تزئین کنندها است:
در اینجا معادل کدهایی را که با روش factory خود NET Core. نوشتیم، ملاحظه میکنید. ابتدا نیاز است خود سرویس اصلی غیر تزئین شده، به نحو متداولی به سیستم معرفی شود. سپس متد الحاقی جدید <,>Decorate را با همان اینترفیس و اینبار با Decorator مدنظر معرفی میکنیم. کاری که Scrutor در اینجا انجام میدهد، یافتن سرویس ITaskService معرفی شدهی پیشین و تعویض آن با MyTaskServiceDecorator میباشد. بنابراین نیاز است تعریف services.AddTransient پیش از تعریف services.Decorate انجام شده باشد. این روش تمیزتر از روش قبلی به نظر میرسد و شامل وهله سازی مستقیم MyTaskServiceDecorator به همراه فراهم آوردن تمام پارامترهای سازندهی آن توسط ما نیست.
پیاده سازی الگوی Decorator به کمک سیستم تزریق وابستگیهای NET Core.
مثال زیر را در نظر بگیرید که در آن یک سرویس تعریف شدهاست و در این بین استثنائی رخ دادهاست.
public interface ITaskService { void Run(); } public class MyTaskService : ITaskService { public void Run() { throw new InvalidOperationException("An exception from the MyTaskService!"); } }
using System; using Microsoft.Extensions.Logging; namespace CoreIocServices { public class MyTaskServiceDecorator : ITaskService { private readonly ILogger<MyTaskServiceDecorator> _logger; private readonly ITaskService _decorated; public MyTaskServiceDecorator( ILogger<MyTaskServiceDecorator> logger, ITaskService decorated) { _logger = logger; _decorated = decorated; } public void Run() { try { _decorated.Run(); } catch (Exception ex) { _logger.LogCritical(ex, "An unhandled exception has been occurred."); } } } }
مزیت اینکار، پیاده سازی اصل DRY یا Don't repeat yourself است. کاری که برای رفع این مشکل قرار است انجام دهیم، استفاده از یک تزئین کننده (محصور کننده)، کپسوله سازی اعمال تکراری و سپس اتصال آن به قسمتهای مختلف برنامه است. همچنین در این حالت اصل open closed principle نیز بهتر رعایت خواهد شد. از این جهت که کدهای تکراری برنامه به یک لایهی دیگر منتقل شدهاند و دیگر نیازی نیست برای تغییر آنها، کدهای قسمتهای اصلی برنامه را تغییر داد (کدهای برنامه باز خواهند بود برای توسعه و بسته برای تغییر).
پس از طراحی این تزئین کننده، اکنون نوبت به معرفی آن به سیستم تزریق وابستگیهای NET Core. است:
namespace CoreIocSample02 { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<MyTaskService>(); services.AddTransient<ITaskService>(serviceProvider => new MyTaskServiceDecorator( serviceProvider.GetService<ILogger<MyTaskServiceDecorator>>(), serviceProvider.GetService<MyTaskService>()) );
در اینجا هم میتوان در صورت نیاز اصل کلاس MyTaskService را بدون هیچ نوع تزئین کنندهای از سیستم تزریق وابستگیها دریافت کرد و یا اگر وهلهای از سرویس ITaskService را از آن درخواست کردیم، ابتدا شیء MyTaskServiceDecorator وهله سازی شده و سپس توسط آن یک نمونهی محصور شده و تزئین شدهی MyTaskService به فراخوان بازگشت داده خواهد شد.
ساده سازی معرفی تزئین کنندهها به سیستم تزریق وابستگیهای NET Core. به کمک Scrutor
در «قسمت هشتم - ساده سازی معرفی سرویسها توسط Scrutor» با کتابخانهی Scrutor آشنا شدیم. یکی دیگر از قابلیتهای آن، امکان ساده سازی تعریف تزئین کنندها است:
namespace CoreIocSample02 { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddTransient<ITaskService, MyTaskService>(); services.Decorate<ITaskService, MyTaskServiceDecorator>();
ASP.NET Core به همراه یک فریم ورک توکار ثبت وقایع (Logging) ارائه شدهی توسط تزریق وابستگیها است که به صورت پیش فرض نیز فعال است.
این تصویر را پیشتر در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 6 - سرویسها و تزریق وابستگیها» مشاهده کردهاید. در اینجا لیست سرویسهایی را مشاهده میکنید که به صورت پیش فرض، ثبت شدهاند و فعال هستند و ILogger و ILoggerFactory نیز جزئی از آنها هستند. بنابراین نیازی به فعال سازی آنها نیست؛ اما برای استفادهی از آنها نیاز به انجام یک سری تنظیمات است.
پیاده سازی ثبت وقایع در ASP.NET Core
اولین قدم کار با فریم ورک ثبت وقایع ASP.NET Core، معرفی ILoggerFactory به متد Configure کلاس آغازین برنامه است:
متد Configure امضای مشخصی را ندارد و در اینجا به هر تعداد سرویسی که نیاز باشد، میتوان اینترفیسهای آنها را جهت تزریق وابستگیهای متناظر توسط IoC Containser توکار ASP.NET Core، معرفی کرد. در اینجا برای تنظیم ویژگیهای سرویس ثبت وقایع، تزریق وابستگی ILoggerFactory صورت گرفتهاست.
سطر اول متد، تنظیمات ثبت وقایع را از خاصیت Logging فایل appsettings.json برنامه میخواند (در مورد خاصیت Configuration، در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 7 - کار با فایلهای config» بیشتر بحث شد) و لاگ کردن ویژهی در کنسول NET Core. را فعال میکند:
در مورد Log Level و یا سطوح ثبت وقایع، در ادامهی مطلب بحث خواهد شد.
و سطر دوم سبب نمایش اطلاعات لاگ شده در کنسول دیباگ ویژوال استودیو میشود.
متد AddDebug برای شناسایی، نیاز به افزودن وابستگیهای ذیل در فایل project.json برنامه را دارد:
پس از این تنظیمات، برنامه را اجرا کنید.
در اینجا میتوانید ریز وقایعی را که توسط ASP.NET Core لاگ شدهاست، مشاهده کنید. برای مثال چه درخواستی صورت گرفتهاست و چقدر اجرای آن زمانبردهاست.
این فعال سازی مرتبط است به متد AddDebug که اضافه شد. اگر میخواهید خروجی AddConsole را هم مشاهده کنید، از طریق خط فرمان، به پوشهی اصلی پروژه وارد شده و سپس دستور dotnet run را اجرا کنید:
دستور dotnet run سبب راه اندازی وب سرور برنامه بر روی پورت 5000 شدهاست که در تصویر نیز مشخص است.
بنابراین اینبار برای دسترسی به برنامه باید مسیر http://localhost:5000 را در مرورگر خود طی کنید. در اینجا نیز میتوان حالتهای مختلف اطلاعات لاگ شده را مشاهده کرد و تمام اینها مرتبط هستند به ذکر متد AddConsole .
کار با سرویس ثبت وقایع ASP.NET Core از طریق تزریق وابستگیها
برای کار با سرویس ثبت وقایع توکار ASP.NET Core در قسمتهای مختلف برنامه، میتوان از ترزیق وابستگی ILogger آن استفاده کرد:
در این کنترلر، وابستگی اینترفیس ILogger با پارامتری از نوع کنترلر جاری به سازندهی کلاس تزریق شدهاست. علت ذکر این پارامتر جنریک این است که ILoggerFactory بداند چگونه باید متد CreateLogger خود را در پشت صحنه وهله سازی کند.
سپس با توجه به اینکه این سرویس جزو سرویسهای از پیش ثبت شدهی ASP.NET Core است، امکانات آن بدون نیاز به تنظیمات بیشتری در دسترس است. برای مثال از متد LogInformation آن در اکشن متد Hello استفاده شدهاست و خروجی عبارت لاگ شدهی آنرا در اینجا میتوانید مشاهده کنید:
سطوح مختلف ثبت وقایع
اینترفیس ILogger به همراه متدهای مختلفی است؛ مانند LogError، LogDebug و غیره. معانی آنها به شرح زیر هستند:
Debug (1): ثبت واقعهای است با بیشترین حد جزئیات ممکن که عموما شامل اطلاعات حساسی نیز میباشد. بنابراین نباید در حالت ارائهی نهایی برنامه فعال شود.
(2) Verbose: ثبت وقایعی مفصل، جهت بررسی مشکلات در حین توسعهی برنامه. تنها باید حاوی اطلاعاتی برای دیباگ برنامه باشند.
(3) Information: عموما برای ردیابی قسمتهای مختلف برنامه مورد استفاده قرار میگیرند.
(4) Warning: جهت ثبت واقعهای نامطلوب در سیستم بکار میرود و سبب قطع اجرای برنامه نمیشود.
(5) Errors: مشکلات برنامه را که سبب قطع سرویس دهی آن شدهاند را ثبت میکند. هدف آن ثبت مشکلات واحد جاری است و نه کل برنامه.
Critical (6): هدف آن ثبت مشکلات بحرانی کل سیستم است که سبب از کار افتادن آن شدهاند.
برای مثال در حین تنظیم متد AddDebug که سبب نمایش اطلاعات لاگ شده در کنسول دیباگ ویژوال استودیو میشود، میتوان حداقل سطح ثبت وقایع را نیز ذکر کرد:
این حداقل مرتبط است با اعدادی که در کنار سطوح فوق ملاحظه میکنید. برای مثال اگر حداقل سطح ثبت وقایع به Information تنظیم شود، چون سطح آن 3 است، دیگر سطوح پایینتر از آن لاگ نخواهند شد. اهمیت این مساله در اینجا است که اگر صرفا نیاز به اطلاعات Critical داشتیم، نیازی نیست تا با انبوهی از اطلاعات لاگ شده سر و کار داشته باشیم و به این ترتیب میتوان حجم اطلاعات نمایش داده شده را کاهش داد.
البته ترتیب واقعی این سطوح را در enum مرتبط با آنها بهتر میتوان مشاهده کرد:
یک نکته: زمانیکه متد AddDebug را بدون پارامتر فراخوانی میکنید، حداقل سطح ثبت وقایع آن به Information تنظیم شدهاست. یعنی در این لاگ، خبری از اطلاعات Debug نخواهد بود (چون سطح دیباگ پایینتر است از Information). بنابراین اگر میخواهید این اطلاعات را هم مشاهده کنید باید پارامتر minLevel آنرا به LogLevel.Debug تنظیم نمائید.
امکان استفادهی از پروایدرهای ثبت وقایع ثالث
تا اینجا، دو نمونه از پروایدرهای توکار ثبت وقایع ASP.NET Core را بررسی کردیم. اگر نیاز به ثبت این اطلاعات با فرمتهای مختلف و یا در بانک اطلاعاتی وجود دارد، میتوان به تامین کنندههای ثالثی که قابلیت کار با ILoggerFactory را دارند نیز مراجعه کرد. برای مثال:
- elmah.io - provider for the elmah.io service
- Loggr - provider for the Loggr service
- NLog - provider for the NLog library
- Serilog - provider for the Serilog library
این تصویر را پیشتر در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 6 - سرویسها و تزریق وابستگیها» مشاهده کردهاید. در اینجا لیست سرویسهایی را مشاهده میکنید که به صورت پیش فرض، ثبت شدهاند و فعال هستند و ILogger و ILoggerFactory نیز جزئی از آنها هستند. بنابراین نیازی به فعال سازی آنها نیست؛ اما برای استفادهی از آنها نیاز به انجام یک سری تنظیمات است.
پیاده سازی ثبت وقایع در ASP.NET Core
اولین قدم کار با فریم ورک ثبت وقایع ASP.NET Core، معرفی ILoggerFactory به متد Configure کلاس آغازین برنامه است:
public void Configure(ILoggerFactory loggerFactory, IApplicationBuilder app, IHostingEnvironment env) { loggerFactory.AddConsole(Configuration.GetSection("Logging")); loggerFactory.AddDebug();
سطر اول متد، تنظیمات ثبت وقایع را از خاصیت Logging فایل appsettings.json برنامه میخواند (در مورد خاصیت Configuration، در مطلب «ارتقاء به ASP.NET Core 1.0 - قسمت 7 - کار با فایلهای config» بیشتر بحث شد) و لاگ کردن ویژهی در کنسول NET Core. را فعال میکند:
{ "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } }
و سطر دوم سبب نمایش اطلاعات لاگ شده در کنسول دیباگ ویژوال استودیو میشود.
متد AddDebug برای شناسایی، نیاز به افزودن وابستگیهای ذیل در فایل project.json برنامه را دارد:
{ "dependencies": { //same as before "Microsoft.Extensions.Logging": "1.0.0", "Microsoft.Extensions.Logging.Console": "1.0.0", "Microsoft.Extensions.Logging.Debug": "1.0.0" } }
در اینجا میتوانید ریز وقایعی را که توسط ASP.NET Core لاگ شدهاست، مشاهده کنید. برای مثال چه درخواستی صورت گرفتهاست و چقدر اجرای آن زمانبردهاست.
این فعال سازی مرتبط است به متد AddDebug که اضافه شد. اگر میخواهید خروجی AddConsole را هم مشاهده کنید، از طریق خط فرمان، به پوشهی اصلی پروژه وارد شده و سپس دستور dotnet run را اجرا کنید:
دستور dotnet run سبب راه اندازی وب سرور برنامه بر روی پورت 5000 شدهاست که در تصویر نیز مشخص است.
بنابراین اینبار برای دسترسی به برنامه باید مسیر http://localhost:5000 را در مرورگر خود طی کنید. در اینجا نیز میتوان حالتهای مختلف اطلاعات لاگ شده را مشاهده کرد و تمام اینها مرتبط هستند به ذکر متد AddConsole .
کار با سرویس ثبت وقایع ASP.NET Core از طریق تزریق وابستگیها
برای کار با سرویس ثبت وقایع توکار ASP.NET Core در قسمتهای مختلف برنامه، میتوان از ترزیق وابستگی ILogger آن استفاده کرد:
[Route("[controller]")] public class AboutController : Controller { private readonly ILogger<AboutController> _logger; public AboutController(ILogger<AboutController> logger) { _logger = logger; } [Route("")] public ActionResult Hello() { _logger.LogInformation("Running Hello"); return Content("Hello from DNT!"); }
سپس با توجه به اینکه این سرویس جزو سرویسهای از پیش ثبت شدهی ASP.NET Core است، امکانات آن بدون نیاز به تنظیمات بیشتری در دسترس است. برای مثال از متد LogInformation آن در اکشن متد Hello استفاده شدهاست و خروجی عبارت لاگ شدهی آنرا در اینجا میتوانید مشاهده کنید:
سطوح مختلف ثبت وقایع
اینترفیس ILogger به همراه متدهای مختلفی است؛ مانند LogError، LogDebug و غیره. معانی آنها به شرح زیر هستند:
Debug (1): ثبت واقعهای است با بیشترین حد جزئیات ممکن که عموما شامل اطلاعات حساسی نیز میباشد. بنابراین نباید در حالت ارائهی نهایی برنامه فعال شود.
(2) Verbose: ثبت وقایعی مفصل، جهت بررسی مشکلات در حین توسعهی برنامه. تنها باید حاوی اطلاعاتی برای دیباگ برنامه باشند.
(3) Information: عموما برای ردیابی قسمتهای مختلف برنامه مورد استفاده قرار میگیرند.
(4) Warning: جهت ثبت واقعهای نامطلوب در سیستم بکار میرود و سبب قطع اجرای برنامه نمیشود.
(5) Errors: مشکلات برنامه را که سبب قطع سرویس دهی آن شدهاند را ثبت میکند. هدف آن ثبت مشکلات واحد جاری است و نه کل برنامه.
Critical (6): هدف آن ثبت مشکلات بحرانی کل سیستم است که سبب از کار افتادن آن شدهاند.
برای مثال در حین تنظیم متد AddDebug که سبب نمایش اطلاعات لاگ شده در کنسول دیباگ ویژوال استودیو میشود، میتوان حداقل سطح ثبت وقایع را نیز ذکر کرد:
loggerFactory.AddDebug(minLevel: LogLevel.Information);
البته ترتیب واقعی این سطوح را در enum مرتبط با آنها بهتر میتوان مشاهده کرد:
public enum LogLevel { Trace, Debug, Information, Warning, Error, Critical, None, }
یک نکته: زمانیکه متد AddDebug را بدون پارامتر فراخوانی میکنید، حداقل سطح ثبت وقایع آن به Information تنظیم شدهاست. یعنی در این لاگ، خبری از اطلاعات Debug نخواهد بود (چون سطح دیباگ پایینتر است از Information). بنابراین اگر میخواهید این اطلاعات را هم مشاهده کنید باید پارامتر minLevel آنرا به LogLevel.Debug تنظیم نمائید.
امکان استفادهی از پروایدرهای ثبت وقایع ثالث
تا اینجا، دو نمونه از پروایدرهای توکار ثبت وقایع ASP.NET Core را بررسی کردیم. اگر نیاز به ثبت این اطلاعات با فرمتهای مختلف و یا در بانک اطلاعاتی وجود دارد، میتوان به تامین کنندههای ثالثی که قابلیت کار با ILoggerFactory را دارند نیز مراجعه کرد. برای مثال:
- elmah.io - provider for the elmah.io service
- Loggr - provider for the Loggr service
- NLog - provider for the NLog library
- Serilog - provider for the Serilog library
سلام
و در کلاس Startup کد زیر رو نوشتم
در متد ConfigureServices نحوه Register رو مشخص کردم (مپ کردن IMessagesService به MessagesService ) و در متد Configure انتظار دارم چون کلاس MessagesService و MessagesService2 کنار هم هستند این نگاشت به درستی انجام شود و خطا میده که نگاشت I MessagesService تعریف نشده است. در صورتی که هدف ما اسکن کل اسمبلی هست. ممنون میشم راهنمایی کنید.
من میخوستم از Scrutor استفاده کنم
کد سرویس من به صورت زیر است
namespace FirstProjectServices { public interface IMessagesService { string GetSiteName(); } public class MessagesService : IMessagesService { public string GetSiteName() { return "DNT"; } } } namespace FirstProjectServices { public interface IMessagesService2 { string GetSiteName(); } public class MessagesService2 : IMessagesService2 { public string GetSiteName() { return "DNT"; } } }
public class Startup { public void ConfigureServices(IServiceCollection services) { var collection = new ServiceCollection(); services.Scan(scan => scan // We start out with all types in the assembly of ITransientService .FromAssemblyOf<IMessagesService>() .AddClasses(classes => classes.AssignableTo<MessagesService>()) .AsImplementedInterfaces() .WithTransientLifetime() ); } public void Configure(IApplicationBuilder app, IHostingEnvironment env, IMessagesService2 _messagesService) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } // app.UseDefaultFiles(); // app.UseStaticFiles(); app.Run(async (context) => { string siteName = _messagesService.GetSiteName(); await context.Response.WriteAsync($"Site Name {siteName}"); }); } }
نظرات مطالب
طرحبندی صفحات وب با بوت استرپ 4 - قسمت سوم
یک نکتهی تکمیلی: معادلهای pull-left و pull-right بوت استرپ 3 در نگارش 4 آن
بجای pull-right میتوان از float-right و بجای pull-left از float-left استفاده کرد.
<div class="clearfix"> <span class="float-left">Float left</span> <span class="float-right">Float right</span> </div>
<div class="col-lg-6 d-flex justify-content-end">
<ul class="nav"> <li><a href class="nav-link">Link</a></li> <li><a href class="nav-link">Link</a></li> <li class="ml-auto"><a href class="nav-link">Right</a></li> </ul>
مطالب دورهها
تزریق خودکار وابستگیها در SignalR
فرض کنید لایه سرویس برنامه دارای اینترفیس و کلاسهای زیر است:
قصد داریم از این لایه، توسط تزریق وابستگیها در Hub برنامه استفاده کنیم:
برنامه، همان برنامهای است که در دوره جاری تکمیل گردیده است. فقط در اینجا سازنده کلاس اضافه شده و سپس اینترفیس ITestService به عنوان پارامتر آن تعریف گردیده است. در ادامه میخواهیم کار وهله سازی و تزریق نمونه مرتبط را توسط StructureMap به صورت خودکار انجام دهیم.
برای این منظور یک کلاس جدید را به نام StructureMapDependencyResolver به برنامه اضافه کنید:
کار این کلاس، تعویض DefaultDependencyResolver توکار SignalR با StructureMap است. از این جهت که برای مثال در سراسر برنامه از StructureMap جهت تزریق وابستگیها استفاده شده است و قصد داریم در قسمت Hub آن نیز یکپارچگی کار حفظ گردد.
برای استفاده از این کلاس تعریف شده فقط کافی است Application_Start فایل Global.asax.cs برنامه هاب را به نحو ذیل تغییر دهیم:
در اینجا در ابتدای کار IDependencyResolver توکار StructureMap با کلاس StructureMapDependencyResolver وهله سازی میگردد. سپس تعاریف متداول تنظیمات کلاسها و اینترفیسهای لایه سرویس برنامه اضافه میشوند. همچنین نیاز است GlobalHost.DependencyResolver توکار SignalR نیز به نحوی که ملاحظه میکنید مقدار دهی گردد.
اینبار اگر برنامه را اجرا کنید و سپس یکی از کلاینتهای آنرا فراخوانی نمائید، میتوان مشاهده کرد که کار وهله سازی و تزریق وابستگی سرویس مورد استفاده به صورت خودکار انجام گردیده است:
namespace SignalR02.Services { public interface ITestService { int GetRecordsCount(); } }
namespace SignalR02.Services { public class TestService : ITestService { public int GetRecordsCount() { return 10; // It's just a sample to test IOC's. } } }
[HubName("chat")] public class ChatHub : Hub { //جهت آزمایش تزریق خودکار وابستگیها private readonly ITestService _testService; public ChatHub(ITestService testService) { _testService = testService; } public void SendMessage(string message) { var msg = string.Format("{0}:{1}", Context.ConnectionId, message); Clients.All.hello(msg); Clients.All.hello(string.Format("RecordsCount: {0}", _testService.GetRecordsCount()));
برای این منظور یک کلاس جدید را به نام StructureMapDependencyResolver به برنامه اضافه کنید:
using System; using System.Collections.Generic; using System.Linq; using Microsoft.AspNet.SignalR; using StructureMap; namespace SignalR02.Utils { public class StructureMapDependencyResolver : DefaultDependencyResolver { private readonly IContainer _container; public StructureMapDependencyResolver(IContainer container) { if (container == null) { throw new ArgumentNullException("container"); } _container = container; } public override object GetService(Type serviceType) { return !serviceType.IsAbstract && !serviceType.IsInterface && serviceType.IsClass ? _container.GetInstance(serviceType) : (_container.TryGetInstance(serviceType) ?? base.GetService(serviceType)); } public override IEnumerable<object> GetServices(Type serviceType) { return _container.GetAllInstances(serviceType).Cast<object>().Concat(base.GetServices(serviceType)); } } }
برای استفاده از این کلاس تعریف شده فقط کافی است Application_Start فایل Global.asax.cs برنامه هاب را به نحو ذیل تغییر دهیم:
using System; using System.Web; using System.Web.Routing; using Microsoft.AspNet.SignalR; using SignalR02.Services; using SignalR02.Utils; using StructureMap; namespace SignalR02 { public class Global : HttpApplication { protected void Application_Start(object sender, EventArgs e) { ObjectFactory.Initialize(cfg => { cfg.For<IDependencyResolver>().Singleton().Add<StructureMapDependencyResolver>(); // the rest ... cfg.For<ITestService>().Use<TestService>(); }); GlobalHost.DependencyResolver = ObjectFactory.GetInstance<IDependencyResolver>(); // Register the default hubs route: ~/signalr RouteTable.Routes.MapHubs(new HubConfiguration { EnableCrossDomain = true }); } } }
اینبار اگر برنامه را اجرا کنید و سپس یکی از کلاینتهای آنرا فراخوانی نمائید، میتوان مشاهده کرد که کار وهله سازی و تزریق وابستگی سرویس مورد استفاده به صورت خودکار انجام گردیده است:
اشتراکها
مدیریت Keyboard در AngularJs
در بخش قبل با نصب فریم ورک vuetify و بخشی از کامپوننتهای آن آشنا شدیم .
نتیجعه قطعه کد بالا به این صورت است.
نتیجه قطعه کد بالا بدین ترتیب است :
نتیجه قطعه کد بالا :
Order :
بهوسیلهی order میتوان ترتیب قرارگیری آیتمهای Grid را مشخص کرد. فرض کنید یک ردیف سه ستون داریم و میخواهیم زمانیکه این آیتمها در مروگر نمایش داده میشوند، ترتیب قرارگیری آنها متناسب با نیاز ما باشد که با استفاده از دستور order این کار صورت میپذیرد.
نکته: این کار برای بحث seo مورد استفاده قرار میگیرد.
در پایین با یک مثال، چگونگی انجام اینکار شرح داده شدهاست:در توضیح کد پایین اینگونه میتوان گفت که درون کامپوننت <v-layout>، در خط چهارم برنامه، سه کامپوننت <v-flex> قرار داده شدهاند که به وسیله order، ترتیب نمایش آنها را مشخص کردهایم . در <v-flex> اول order با مقدار md2 مشخص شدهاست. بدین معنا که محتویات این <v-flex>، درون خانه دوم نمایش داده شود و به همین ترتیب برای <v-flex>های بعدی نیز این مقادیر تنظیم شدهاند.
<div id="app"> <v-app id="inspire"> <v-container fluid> <v-flex xs4 order-md2> <v-card dark tile flat color="red lighten-1"> <v-card-text>#1</v-card-text> </v-card> </v-flex> <v-flex xs4 order-md3> <v-card dark tile flat color="red lighten-2"> <v-card-text>#2</v-card-text> </v-card> </v-flex> <v-flex xs4 order-md1> <v-card dark tile flat color="red darken-1"> <v-card-text>#3</v-card-text> </v-card> </v-flex> </v-layout> <v-layout row wrap> <v-flex xs12 sm6 md3 order-md4 order-sm2> <v-card dark tile flat color="red darken-2"> <v-card-text>#1</v-card-text> </v-card> </v-flex> <v-flex xs12 sm6 md3 order-md3 order-sm1> <v-card dark tile flat color="deep-orange lighten-1"> <v-card-text>#2</v-card-text> </v-card> </v-flex> <v-flex xs12 sm6 md3 order-md2 order-sm4> <v-card dark tile flat color="deep-orange darken-3"> <v-card-text>#3</v-card-text> </v-card> </v-flex> <v-flex xs12 sm6 md3 order-md1 order-sm3> <v-card dark tile flat color="deep-orange"> <v-card-text>#4</v-card-text> </v-card> </v-flex> </v-layout> </v-container> </v-app> </div>
Direction & Align :
به وسیله تنظیم مقادیر Direction & align برای کامپوننت <v-flex> میتوان برای ایجاد فواصل بین سطرها و ستونها و همچنین نحوه چینش آنها استفاده کرد. اگر به قطعه کد زیر توجه داشته باشید، این تنظیمات شامل justify-space-between , justify-space-around , justify-center هستند که عملکرد هر کدام به صورت جداگانه نمایش داده شده است.
<div id="app"> <v-app id="inspire"> <v-container fluid grid-list-xl> <v-layout row justify-space-between> <v-flex xs2> <v-card dark color="primary"> <v-card-text>one</v-card-text> </v-card> </v-flex> <v-flex xs2> <v-card dark color="secondary"> <v-card-text>two</v-card-text> </v-card> </v-flex> <v-flex xs2> <v-card dark color="accent"> <v-card-text>three</v-card-text> </v-card> </v-flex> </v-layout> <v-layout row justify-space-around> <v-flex xs2> <v-card dark color="primary"> <v-card-text>one</v-card-text> </v-card> </v-flex> <v-flex xs2> <v-card dark color="secondary"> <v-card-text>two</v-card-text> </v-card> </v-flex> <v-flex xs2> <v-card dark color="accent"> <v-card-text>three</v-card-text> </v-card> </v-flex> </v-layout> <v-layout row justify-center> <v-flex xs2> <v-card dark color="primary"> <v-card-text>one</v-card-text> </v-card> </v-flex> <v-flex xs2> <v-card dark color="secondary"> <v-card-text>two</v-card-text> </v-card> </v-flex> <v-flex xs2> <v-card dark color="accent"> <v-card-text>three</v-card-text> </v-card> </v-flex> </v-layout> </v-container> </v-app> </div>
Grow & Shrink :
در لغت، Grow به معنی رشد و توسعه دادن و Shrink به معنی کوچک کردن میباشد که در اینجا نیز دقیقا همین عملکرد را دنبال میکنند. هرگاه برای یک کامپوننت <v-flex> مقدار Grow را تنظیم نماییم، آن <v-flex> تمامی قسمت خالی عرض صفحه را به خود اختصاص میدهد. اما اگر برای کامپوننت <v-flex> مقدار Shrink را تنظیم نماییم، به میزان محتویات درونی خود جا اشغال مینماید.
نکته: pa-1 در اینجا به معنی padding میباشد که به وسیله pa-1 در چهار جهت بالا، پایین، چپ و راست، padding اعمال میشود.
<div id="app"> <v-app id="inspire"> <v-container fluid> <v-layout row> <v-flex grow pa-1> <v-card dark color="green darken-3"> <v-card-text>#1 Im a Grow Flex</v-card-text> </v-card> </v-flex> <v-flex shrink pa-1> <v-card dark color="green darken-1"> <v-card-text>#2 Im a Shrink Flex</v-card-text> </v-card> </v-flex> </v-layout> <v-layout row> <v-flex grow pa-1> <v-card dark color="green darken-1"> <v-card-text>#1 Im a Grow Flex</v-card-text> </v-card> </v-flex> <v-flex shrink pa-1> <v-card dark color="green lighten-1"> <v-card-text>#2 Im a Shrink Flex</v-card-text> </v-card> </v-flex> <v-flex grow pa-1> <v-card dark color="green darken-4"> <v-card-text>#3 Im a Grow Flex</v-card-text> </v-card> </v-flex> </v-layout> <v-layout row> <v-flex shrink pa-1> <v-card dark color="green darken-3"> <v-card-text>#1 Im a Shrink Flex</v-card-text> </v-card> </v-flex> <v-flex grow pa-1> <v-card dark color="green"> <v-card-text>#2 Im a Grow Flex</v-card-text> </v-card> </v-flex> <v-flex shrink pa-1> <v-card dark color="green lighten-1"> <v-card-text>#3 Im a Shrink Flex</v-card-text> </v-card> </v-flex> </v-layout> </v-container> </v-app> </div>
نتیجه قطعه کد بالا:
Nested grid :
در این فریم ورک قابلیت تعریف nested grid همانند سایر CSS فریم ورکها مقدور است. nested gridها در واقع ردیفهایی هستند که درون ستونها قرار میگیرند. بدین وسیله میتوان هر ستون را به 12 قسمت، تقسیم بندی کرد.
<div id="app"> <v-app id="inspire"> <v-container fluid grid-list-md> <v-layout row wrap> <v-flex d-flex xs12 sm6 md4> <v-card color="purple" dark> <v-card-title primary>Lorem</v-card-title> <v-card-text>{{ lorem }}</v-card-text> </v-card> </v-flex> <v-flex d-flex xs12 sm6 md3> <v-layout row wrap> <v-flex d-flex> <v-card color="indigo" dark> <v-card-text>{{ lorem.slice(0, 70) }}</v-card-text> </v-card> </v-flex> <v-flex d-flex> <v-layout row wrap> <v-flex v-for="n in 2":key="n" d-flex xs12 > <v-card color="red lighten-2" dark > <v-card-text>{{ lorem.slice(0, 40) }}</v-card-text> </v-card> </v-flex> </v-layout> </v-flex> </v-layout> </v-flex> <v-flex d-flex xs12 sm6 md2 child-flex> <v-card color="green lighten-2" dark> <v-card-text>{{ lorem.slice(0, 90) }}</v-card-text> </v-card> </v-flex> <v-flex d-flex xs12 sm6 md3> <v-card color="blue lighten-2" dark> <v-card-text>{{ lorem.slice(0, 100) }}</v-card-text> </v-card> </v-flex> </v-layout> </v-container> </v-app> </div>
اگر فایل html زیر را بذارید بصورت divهای تو در تو، هیچی نمیده. میشه بفرمایید مشکل چیه؟
"<div><div>سلام</div></div>"
پیشنیازها
- فیلترها در MVC
- ASP.NET MVC #15
فیلترها در ASP.NET MVC، امکان اجرای کدهایی را پیش و یا پس از مرحلهی خاصی از طول اجرای pipeline آن فراهم میکنند. کلیات فیلترها در ASP.NET Core با نگارشهای قبلی ASP.NET MVC (پیشنیازهای فوق) تفاوت چندانی را ندارد و بیشتر تغییراتی مانند نحوهی معرفی سراسری آنها، اکشن فیلترهای Async و یا تزریق وابستگیها در آنها، جدید هستند.
امکان تعریف فیلترهای Async در ASP.NET Core
حالت کلی تعریف یک فیلتر در ASP.NET MVC که در ASP.NET Core نیز همچنان معتبر است، پیاده سازی اینترفیس کلی IActionFilter میباشد که توسط آن میتوان به مراحل پیش و پس از اجرای قطعهای از کدهای برنامه دسترسی پیدا کرد:
در اینجا اینترفیس IAsyncActionFilter نیز معرفی شدهاست که توسط آن میتوان فراخوانیهای غیرهمزمان و async را نیز مدیریت کرد:
به کامنتهای نوشته شدهی در بدنهی متد OnActionExecutionAsync دقت کنید. در اینجا کدهای پیش از await next معادل OnActionExecuting و کدهای پس از await next معادل OnActionExecuted حالت همزمان و یا همان حالت متداول هستند. بنابراین جایی که اکشن متد اجرا میشود، همان await next است.
یک نکته: توصیه شدهاست که تنها یکی از حالتهای همزمان و یا غیرهمزمان را پیاده سازی کنید و نه هر دوی آنها را. اگر هر دوی اینها را در طی یک کلاس پیاده سازی کنید (تک کلاسی که هر دوی اینترفیسهای IActionFilter و IAsyncActionFilter را با هم پیاده سازی میکند)، تنها نگارش Async آن توسط ASP.NET Core فراخوانی و استفاده خواهد شد. همچنین مهم نیست که اکشن متد شما Async هست یا خیر؛ برای هر دو حالت میتوان از فیلترهای async نیز استفاده کرد.
ساده سازی تعریف فیلترها
اگر مدتی با ASP.NET MVC کار کرده باشید، میدانید که عموما کسی از این اینترفیسهای کلی برای پیاده سازی فیلترها استفاده نمیکند. روش کار با ارث بری از یکی از فیلترهای از پیش تعریف شدهی ASP.NET MVC صورت میگیرد؛ از این جهت که این فیلترها که در اصل همین اینترفیسها را پیاده سازی کردهاند، یک سری جزئیات توکار protected را نیز به همراه دارند که با ارث بری از آنها میتوان به امکانات بیشتری دسترسی پیدا کرد و کدهای سادهتر و کم حجمتری را تولید نمود:
ActionFilterAttribute
ExceptionFilterAttribute
ResultFilterAttribute
FormatFilterAttribute
ServiceFilterAttribute
TypeFilterAttribute
برای مثال در اینجا فیلتری را مشاهده میکنید که با ارث بری از فیلتر توکار ResultFilterAttribute، سعی در تغییر Response برنامه و افزودن هدری به آن کردهاست:
و برای استفادهی از این فیلتر جدید خواهیم داشت:
نحوهی تعریف میدان دید فیلترها
نحوهی دید فیلترها در اینجا نیز همانند سابق، سه حالت را میتواند داشته باشد:
الف) اعمال شدهی به یک اکشن متد.
ب) اعمال شدهی به یک کنترلر که به تمام اکشن متدهای آن کنترلر اعمال خواهد شد.
ج) حالت تعریف سراسری و این مورد محل تعریف آن به کلاس آغازین برنامه منتقل شدهاست:
در اینجا دو روش معرفی فیلترهای سراسری را در متد ConfigureServices کلاس آغازین برنامه مشاهده میکنید:
الف) اگر توسط ارائهی new ClassName معرفی شوند، یعنی وهله سازی را خودتان قرار است مدیریت کنید و در این حالت تزریق وابستگیهایی صورت نخواهند گرفت.
ب) اگر توسط typeof معرفی شوند، یعنی این وهله سازی توسط IoC Container توکار ASP.NET Core انجام خواهد شد و طول عمر آن Transient است. یعنی به ازای هربار نیاز به آن، یکبار وهله سازی خواهد شد.
ترتیب اجرای فیلترها
توسط خاصیت Order میتوان ترتیب اجرای چندین فیلتر اجرا شدهی به یک اکشن متد را مشخص کرد. اگر این مقدار منفی وارد شود:
این فیلتر پیش از فیلترهای سراسری و همچنین فیلترهای اعمال شدهی در سطح کلاس اجرا میشود.
تزریق وابستگیها در فیلترها
فیلترهایی که به صورت ویژگیها یا Attributes تعریف میشوند و قرار است به کنترلرها و یا اکشن متدها به صورت مستقیم اعمال شوند، نمیتوانند دارای وابستگیهای تزریق شدهی در سازندهی خود باشند. این محدودیتی است که توسط زبانهای برنامه نویسی اعمال میشود و نه ASP.NET Core. اگر ویژگی قرار است پارامتری در سازندهی خود داشته باشد، هنگام تعریف و اعمال آن، این پارامترها باید مشخص بوده و تعریف شوند. به همین جهت آنچنان با تزریق وابستگیهای از طریق سازندهی کلاس قابل مدیریت نیستند. برای رفع این نقصیه، راهحلهای متفاوتی در ASP.NET Core پیشنهاد و طراحی شدهاند:
الف) استفادهی از ServiceFilterAttribute
ویژگی جدید ServiceFilter، نوع کلاس فیلتر را دریافت میکند و سپس هر زمانیکه نیاز به اجرای این فیلتر خاص بود، کار وهله سازیهای وابستگیهای آن، در پشت صحنه توسط IoC Container توکار ASP.NET Core انجام خواهد شد.
همچنین باید دقت داشت که در این حالت ثبت کلاس فیلتر در متد ConfigureServices کلاس آغازین برنامه الزامی است.
در غیراینصورت استثنای ذیل را دریافت خواهید کرد:
ب) استفاده از TypeFilterAttribute
فیلتر و ویژگی TypeFilter بسیار شبیه است به عملکرد ServiceFilter، با این تفاوت که:
- نیازی نیست تا وابستگی آنرا در متد ConfigureServices ثبت کرد (هرچند وابستگیهای خود را از DI Container دریافت میکنند).
- امکان دریافت پارامترهای اضافی سازندهی کلاس مدنظر را نیز دارند.
یک مثال تکمیلی: لاگ کردن تمام استثناءهای مدیریت نشدهی یک برنامهی ASP.NET Core 1.0
میتوان با سفارشی سازی فیلتر توکار ExceptionFilterAttribute، امکان ثبت وقایع را توسط فریم ورک توکار Logging اضافه کرد:
و برای ثبت سراسری آن در کلاس آغازین برنامه خواهیم داشت:
در اینجا از typeof استفاده شدهاست تا کار تزریق وابستگیهای این فیلتر به صورت خودکار انجام شود.
در ادامه با این فرض که پیشتر تنظیمات ثبت وقایع صورت گرفتهاست:
اکنون اگر یک چنین اکشن متدی فراخوانی شود:
در پنجرهی دیباگ ویژوال استودیو، این استثناء قابل مشاهده خواهد بود:
- فیلترها در MVC
- ASP.NET MVC #15
فیلترها در ASP.NET MVC، امکان اجرای کدهایی را پیش و یا پس از مرحلهی خاصی از طول اجرای pipeline آن فراهم میکنند. کلیات فیلترها در ASP.NET Core با نگارشهای قبلی ASP.NET MVC (پیشنیازهای فوق) تفاوت چندانی را ندارد و بیشتر تغییراتی مانند نحوهی معرفی سراسری آنها، اکشن فیلترهای Async و یا تزریق وابستگیها در آنها، جدید هستند.
امکان تعریف فیلترهای Async در ASP.NET Core
حالت کلی تعریف یک فیلتر در ASP.NET MVC که در ASP.NET Core نیز همچنان معتبر است، پیاده سازی اینترفیس کلی IActionFilter میباشد که توسط آن میتوان به مراحل پیش و پس از اجرای قطعهای از کدهای برنامه دسترسی پیدا کرد:
namespace FiltersSample.Filters { public class SampleActionFilter : IActionFilter { public void OnActionExecuting(ActionExecutingContext context) { // انجام کاری پیش از اجرای اکشن متد } public void OnActionExecuted(ActionExecutedContext context) { // انجام کاری پس از اجرای اکشن متد } } }
namespace FiltersSample.Filters { public class SampleAsyncActionFilter : IAsyncActionFilter { public async Task OnActionExecutionAsync( ActionExecutingContext context, ActionExecutionDelegate next) { // انجام کاری پیش از اجرای اکشن متد await next(); // انجام کاری پس از اجرای اکشن متد } } }
یک نکته: توصیه شدهاست که تنها یکی از حالتهای همزمان و یا غیرهمزمان را پیاده سازی کنید و نه هر دوی آنها را. اگر هر دوی اینها را در طی یک کلاس پیاده سازی کنید (تک کلاسی که هر دوی اینترفیسهای IActionFilter و IAsyncActionFilter را با هم پیاده سازی میکند)، تنها نگارش Async آن توسط ASP.NET Core فراخوانی و استفاده خواهد شد. همچنین مهم نیست که اکشن متد شما Async هست یا خیر؛ برای هر دو حالت میتوان از فیلترهای async نیز استفاده کرد.
ساده سازی تعریف فیلترها
اگر مدتی با ASP.NET MVC کار کرده باشید، میدانید که عموما کسی از این اینترفیسهای کلی برای پیاده سازی فیلترها استفاده نمیکند. روش کار با ارث بری از یکی از فیلترهای از پیش تعریف شدهی ASP.NET MVC صورت میگیرد؛ از این جهت که این فیلترها که در اصل همین اینترفیسها را پیاده سازی کردهاند، یک سری جزئیات توکار protected را نیز به همراه دارند که با ارث بری از آنها میتوان به امکانات بیشتری دسترسی پیدا کرد و کدهای سادهتر و کم حجمتری را تولید نمود:
ActionFilterAttribute
ExceptionFilterAttribute
ResultFilterAttribute
FormatFilterAttribute
ServiceFilterAttribute
TypeFilterAttribute
برای مثال در اینجا فیلتری را مشاهده میکنید که با ارث بری از فیلتر توکار ResultFilterAttribute، سعی در تغییر Response برنامه و افزودن هدری به آن کردهاست:
using Microsoft.AspNetCore.Mvc.Filters; namespace FiltersSample.Filters { public class AddHeaderAttribute : ResultFilterAttribute { private readonly string _name; private readonly string _value; public AddHeaderAttribute(string name, string value) { _name = name; _value = value; } public override void OnResultExecuting(ResultExecutingContext context) { context.HttpContext.Response.Headers.Add( _name, new string[] { _value }); base.OnResultExecuting(context); } } }
[AddHeader("Author", "DNT")] public class SampleController : Controller { public IActionResult Index() { return Content("با فایرباگ هدر خروجی را بررسی کنید"); } }
نحوهی تعریف میدان دید فیلترها
نحوهی دید فیلترها در اینجا نیز همانند سابق، سه حالت را میتواند داشته باشد:
الف) اعمال شدهی به یک اکشن متد.
ب) اعمال شدهی به یک کنترلر که به تمام اکشن متدهای آن کنترلر اعمال خواهد شد.
ج) حالت تعریف سراسری و این مورد محل تعریف آن به کلاس آغازین برنامه منتقل شدهاست:
public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => { options.Filters.Add(typeof(SampleActionFilter)); // by type options.Filters.Add(new SampleGlobalActionFilter()); // an instance }); }
الف) اگر توسط ارائهی new ClassName معرفی شوند، یعنی وهله سازی را خودتان قرار است مدیریت کنید و در این حالت تزریق وابستگیهایی صورت نخواهند گرفت.
ب) اگر توسط typeof معرفی شوند، یعنی این وهله سازی توسط IoC Container توکار ASP.NET Core انجام خواهد شد و طول عمر آن Transient است. یعنی به ازای هربار نیاز به آن، یکبار وهله سازی خواهد شد.
ترتیب اجرای فیلترها
توسط خاصیت Order میتوان ترتیب اجرای چندین فیلتر اجرا شدهی به یک اکشن متد را مشخص کرد. اگر این مقدار منفی وارد شود:
[MyFilter(Name = "Method Level Attribute", Order=-1)]
تزریق وابستگیها در فیلترها
فیلترهایی که به صورت ویژگیها یا Attributes تعریف میشوند و قرار است به کنترلرها و یا اکشن متدها به صورت مستقیم اعمال شوند، نمیتوانند دارای وابستگیهای تزریق شدهی در سازندهی خود باشند. این محدودیتی است که توسط زبانهای برنامه نویسی اعمال میشود و نه ASP.NET Core. اگر ویژگی قرار است پارامتری در سازندهی خود داشته باشد، هنگام تعریف و اعمال آن، این پارامترها باید مشخص بوده و تعریف شوند. به همین جهت آنچنان با تزریق وابستگیهای از طریق سازندهی کلاس قابل مدیریت نیستند. برای رفع این نقصیه، راهحلهای متفاوتی در ASP.NET Core پیشنهاد و طراحی شدهاند:
الف) استفادهی از ServiceFilterAttribute
[ServiceFilter(typeof(AddHeaderFilterWithDi))] public IActionResult Index() { return View(); }
همچنین باید دقت داشت که در این حالت ثبت کلاس فیلتر در متد ConfigureServices کلاس آغازین برنامه الزامی است.
services.AddScoped<AddHeaderFilterWithDi>();
System.InvalidOperationException: No service for type 'FiltersSample.Filters.AddHeaderFilterWithDI' has been registered.
ب) استفاده از TypeFilterAttribute
[TypeFilter(typeof(AddHeaderAttribute), Arguments = new object[] { "Author", "DNT" })] public IActionResult Hi(string name) { return Content($"Hi {name}"); }
- نیازی نیست تا وابستگی آنرا در متد ConfigureServices ثبت کرد (هرچند وابستگیهای خود را از DI Container دریافت میکنند).
- امکان دریافت پارامترهای اضافی سازندهی کلاس مدنظر را نیز دارند.
یک مثال تکمیلی: لاگ کردن تمام استثناءهای مدیریت نشدهی یک برنامهی ASP.NET Core 1.0
میتوان با سفارشی سازی فیلتر توکار ExceptionFilterAttribute، امکان ثبت وقایع را توسط فریم ورک توکار Logging اضافه کرد:
using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Logging; namespace Core1RtmEmptyTest.StartupCustomizations { public class CustomExceptionLoggingFilterAttribute : ExceptionFilterAttribute { private readonly ILogger<CustomExceptionLoggingFilterAttribute> _logger; public CustomExceptionLoggingFilterAttribute(ILogger<CustomExceptionLoggingFilterAttribute> logger) { _logger = logger; } public override void OnException(ExceptionContext context) { _logger.LogInformation($"OnException: {context.Exception}"); base.OnException(context); } } }
public void ConfigureServices(IServiceCollection services) { services.AddMvc(options => { options.Filters.Add(typeof(CustomExceptionLoggingFilterAttribute));
در ادامه با این فرض که پیشتر تنظیمات ثبت وقایع صورت گرفتهاست:
public void Configure(ILoggerFactory loggerFactory) { loggerFactory.AddDebug(minLevel: LogLevel.Debug);
public IActionResult GetData() { throw new Exception("throwing an exception!"); }