ПРИМЕЧАНИЕ. это первое решение, основанное на ControllerActionInvokerCache
, было протестировано в asp.net core 2.2
и работало нормально. Однако в более поздних версиях похоже, что этот класс сделан internal
и больше недоступен, это решение не поможет. Попробуйте второе решение, представленное позже.
Вызванный контроллер предварительно кэшируется в ControllerActionInvokerCache
(который доступен через DI). Ключ кеша — это экземпляр ControllerContext
, который не сравнивается одинаково по ссылке (поэтому вы можете создать новый экземпляр, пока обернутый ActionContext
является текущим). На самом деле ControllerActionInvokerCache
- это составной кеш.
Вы можете увидеть код ниже для деталей:
public static class RazorViewExtensions
{
public static Controller GetInvokedController(this RazorPage view)
{
var serviceProvider = view.Context.RequestServices;
var controllerCache = serviceProvider.GetRequiredService<ControllerActionInvokerCache>();
//ViewContext here is also an ActionContext
var controllerContext = new ControllerContext(view.ViewContext);
var cacheEntry = controllerCache.GetCachedResult(controllerContext).cacheEntry;
return cacheEntry == null ? null : cacheEntry.ControllerFactory(controllerContext) as Controller;
}
}
Для удобства мы объявляем метод расширения, как указано выше. Чтобы использовать его в представлении вашего контроллера:
var controller = this.GetInvokedController();
Вы можете основываться на этом, чтобы написать аналогичный метод расширения для использования внутри страницы Razor (базовая страница — Page
вместо RazorPage
).
На самом деле ControllerActionInvokerCacheEntry
передается ControllerActionDescriptor.CacheEntry
. Однако это свойство CacheEntry
является внутренним (и, конечно же, не задокументировано). Мы можем видеть это в исходном коде. Таким образом, вы можете использовать reflection
для получения этой записи в кэше. Однако это требует отражения, поэтому блок кода даже длиннее, чем первое решение, которое мы использовали выше.
Вот еще одно решение путем извлечения экземпляра контроллера из файла ActionExecutingContext.Controller
. Это может быть немного быстрее, чем первое решение, но нам нужен отдельный класс для пользовательского IActionFilter
, чтобы захватить экземпляр контроллера в функцию, совместно используемую через HttpContext.Features
. Код, конечно, немного длиннее, например:
//define the feature types
public interface IInvokedControllerFeature
{
Controller Controller { get; }
}
public class InvokedControllerFeature : IInvokedControllerFeature
{
public InvokedControllerFeature(Controller controller)
{
Controller = controller;
}
public Controller Controller { get; }
}
//an extension class to contain several convenient extension methods
//to setup the feature and get the controller instance later
public static class InvokedControllerFeatureExtensions
{
public static Controller GetInvokedController(this HttpContext httpContext)
{
return httpContext.Features.Get<IInvokedControllerFeature>()?.Controller;
}
public static Controller GetInvokedController(this RazorPage view)
{
return view.Context.GetInvokedController();
}
public static IServiceCollection AddInvokedControllerFeature(this IServiceCollection services)
{
return services.Configure<MvcOptions>(o => {
o.Filters.Add<InvokedControllerFeatureActionFilter>();
});
}
class InvokedControllerFeatureActionFilter : IActionFilter
{
public void OnActionExecuted(ActionExecutedContext context) {}
public void OnActionExecuting(ActionExecutingContext context)
{
//share the controller instance via a feature
context.HttpContext.Features.Set<IInvokedControllerFeature>(new InvokedControllerFeature(context.Controller as Controller));
}
}
}
//register the feature inside Startup.ConfigureServices
services.AddInvokedControllerFeature();
Использование внутри представления контроллера точно такое же, как и в первом решении.
person
King King
schedule
18.03.2021