یک نمونه روش یافتن لیست تمام کنترلرها و اکشن متدهای یک برنامهی ASP.NET MVC 5.x را در مطلب «نحوه ایجاد یک نقشهی سایت پویا با استفاده از قابلیت Reflection» میتوانید ملاحظه کنید. استفادهی از این روش با ASP.NET Core الزاما به پاسخ مناسبی نخواهد رسید؛ چون در اینجا POCO controllers هم اضافه شدهاند. به علاوه میتوان اسمبلیهای دیگری را در زمان آغاز برنامه به تنظیمات AddMvc اضافه کرد و تمام آنها هم میتوانند حاوی کنترلرها و ویووها خاص خودشان باشند. روش بهتر این است که از خود ASP.NET Core سؤال کنیم چه مواردی را به عنوان کنترلر تشخیص دادهای؟ در ادامه این نکته را بیشتر بررسی خواهیم کرد.
معرفی سرویس IActionDescriptorCollectionProvider در ASP.NET Core
فرض کنید میخواهیم لیست تمام کنترلرهای یک برنامهی ASP.NET Core را با ساختار ذیل تهیه کنیم که شامل نام کنترلر، نام اکشن متد و نام ناحیهی متناظر با آن (در صورت تنظیم) میباشد:
یکی از سرویسهای از پیش ثبت شدهی ASP.NET Core که لیست تمام کنترلرها و اکشن متدهای تشخیص داده شدهی توسط آن را به همراه دارد، سرویس IActionDescriptorCollectionProvider میباشد. برای شروع به کار با آن، ابتدا این سرویس را به سازندهی یک کلاس دلخواه تزریق میکنیم:
توضیحات:
- در کلاس آغازین برنامه نیازی به ثبت سرویس IActionDescriptorCollectionProvider نیست و اینکار پیشتر توسط خود ASP.NET Core انجام شدهاست.
- این provider حاوی لیست اطلاعات تمام اکشن متدهای ثبت شدهی توسط ASP.NET Core است. در اینجا تنها کافی است حلقهای را بر روی لیست آیتمهای آن تشکیل داده و سپس مقادیر ControllerName و یا ActionName را بدست بیاوریم.
- اگر نیاز به اطلاعات بیشتری از کنترلر و اکشن متد جاری در حال بررسی توسط حلقهی تهیه شده بود، میتوان از ControllerTypeInfo و MethodInfo آن استفاده کرد. این TypeInfoها با استفاده از Reflection، امکان دسترسی به اطلاعاتی مانند ویژگیهای اعمال شدهی به کنترلر یا اکشنی خاص را میسر میکنند. برای مثال در اینجا توسط اطلاعات نوع یک کنترلر در حال بررسی توانستهایم متد GetCustomAttribute را فراخوانی کرده و سپس بررسی کنیم که آیا دارای ویژگی جدید Area هست یا خیر؟ و اگر بله، مقدار RouteValue آن را که در حقیقت مقدار یا نام Area آن کنترلر است، بازگشت میدهیم.
نحوهی استفاده از سرویس IMvcActionsDiscoveryService تهیه شده
اگر دقت کرده باشید اطلاعات لیست MvcActions، در سازندهی این کلاس مقدار دهی شدهاند. علت اینجا است که اگر این کلاس را به صورت singleton ثبت کنیم، تنها یکبار در طول عمر برنامه و در همان آغاز کار، این لیست پر شده و سپس کش خواهد شد. بنابراین دسترسیهای بعدی به MvcActions، شامل فراخوانی سازندهی این کلاس نخواهند بود:
پس از تعریف متد الحاقی کمکی فوق برای افزودن سرویس تهیه شده به صورت singleton، برای ثبت آن در برنامه و در کلاس آغازین آن، خواهیم داشت:
در ادامه هر کنترلری و یا سرویس دیگری که نیاز به اطلاعات تمامی اکشن متدهای برنامه داشت، میتواند سرویس IMvcActionsDiscoveryService را به سازندهی خود تزریق کرده و سپس از اطلاعات لیست MvcActions استفاده کند. این کاملترین لیستی که میتوان تهیه کرد؛ زیرا زیرساخت ASP.NET Core نیز از همین سرویس IActionDescriptorCollectionProvider استفاده میکند.
معرفی سرویس IActionDescriptorCollectionProvider در ASP.NET Core
فرض کنید میخواهیم لیست تمام کنترلرهای یک برنامهی ASP.NET Core را با ساختار ذیل تهیه کنیم که شامل نام کنترلر، نام اکشن متد و نام ناحیهی متناظر با آن (در صورت تنظیم) میباشد:
public class MvcActionViewModel { public string ControllerName { get; set; } public string ActionName { get; set; } public string AreaName { get; set; } }
public interface IMvcActionsDiscoveryService { ICollection<MvcActionViewModel> MvcActions { get; } } public class MvcActionsDiscoveryService : IMvcActionsDiscoveryService { public MvcActionsDiscoveryService(IActionDescriptorCollectionProvider actionDescriptorCollectionProvider) { var actionDescriptors = actionDescriptorCollectionProvider.ActionDescriptors.Items; foreach (var actionDescriptor in actionDescriptors) { var descriptor = actionDescriptor as ControllerActionDescriptor; if (descriptor == null) { continue; } var controllerTypeInfo = descriptor.ControllerTypeInfo; var actionMethodInfo = descriptor.MethodInfo; MvcActions.Add(new MvcActionViewModel { ControllerName = descriptor.ControllerName, ActionName = descriptor.ActionName, AreaName = controllerTypeInfo.GetCustomAttribute<AreaAttribute>()?.RouteValue }); } } public ICollection<MvcActionViewModel> MvcActions { get; } = new HashSet<MvcActionViewModel>(); }
- در کلاس آغازین برنامه نیازی به ثبت سرویس IActionDescriptorCollectionProvider نیست و اینکار پیشتر توسط خود ASP.NET Core انجام شدهاست.
- این provider حاوی لیست اطلاعات تمام اکشن متدهای ثبت شدهی توسط ASP.NET Core است. در اینجا تنها کافی است حلقهای را بر روی لیست آیتمهای آن تشکیل داده و سپس مقادیر ControllerName و یا ActionName را بدست بیاوریم.
- اگر نیاز به اطلاعات بیشتری از کنترلر و اکشن متد جاری در حال بررسی توسط حلقهی تهیه شده بود، میتوان از ControllerTypeInfo و MethodInfo آن استفاده کرد. این TypeInfoها با استفاده از Reflection، امکان دسترسی به اطلاعاتی مانند ویژگیهای اعمال شدهی به کنترلر یا اکشنی خاص را میسر میکنند. برای مثال در اینجا توسط اطلاعات نوع یک کنترلر در حال بررسی توانستهایم متد GetCustomAttribute را فراخوانی کرده و سپس بررسی کنیم که آیا دارای ویژگی جدید Area هست یا خیر؟ و اگر بله، مقدار RouteValue آن را که در حقیقت مقدار یا نام Area آن کنترلر است، بازگشت میدهیم.
نحوهی استفاده از سرویس IMvcActionsDiscoveryService تهیه شده
اگر دقت کرده باشید اطلاعات لیست MvcActions، در سازندهی این کلاس مقدار دهی شدهاند. علت اینجا است که اگر این کلاس را به صورت singleton ثبت کنیم، تنها یکبار در طول عمر برنامه و در همان آغاز کار، این لیست پر شده و سپس کش خواهد شد. بنابراین دسترسیهای بعدی به MvcActions، شامل فراخوانی سازندهی این کلاس نخواهند بود:
public static class MvcActionsDiscoveryServiceExtensions { public static IServiceCollection AddMvcActionsDiscoveryService(this IServiceCollection services) { services.AddSingleton<IMvcActionsDiscoveryService, MvcActionsDiscoveryService>(); return services; } }
public void ConfigureServices(IServiceCollection services) { services.AddMvcActionsDiscoveryService(); }