从.net core 5中的View访问控制器实例



我一直在通过以下行从视图访问基本控制器实例,ASP.NET

BaseController baseController = ViewContext.Controller as BaseController;

我在ASP.NET Core 5.0中有一个新项目。出于某种原因,我想访问基本控制器,但现在它似乎不像以前版本的MVC。

有什么解决方案或替代方案可以实现同样的目标吗?

注意:我想访问控制器的完全初始化的实例。我尝试使用GetService((方法通过依赖注入来获取实例。它提供了控制器的新实例,但尚未完全初始化,例如HttpContextUser等属性为空。

注意:基于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(。

实际上,CCD_ 10被传递到CCD_ 11。然而,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();

控制器视图中的用法与第一个解决方案中的用法完全相同。

最新更新