【问题标题】:Accessing Controller instance from View in .net core 5从.net core 5中的视图访问控制器实例
【发布时间】:2021-03-18 10:52:02
【问题描述】:

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

BaseController baseController = ViewContext.Controller as BaseController;

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

是否有任何解决方案或替代方案来实现相同的目标?

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

【问题讨论】:

  • 您如何通过 ViewModel 将控制器基础从控制器发送到 View ?这样可以节省大量的铸造过程和东西
  • 可能值得注意的是,虽然@King-King’s answer 为您的问题提供了解决方案,但需要从视图访问控制器通常是一种代码味道,并且几乎肯定违反了 MVC 的核心设计原则图案。更好的解决方案是从控制器中获取您需要的任何信息,并将其注入您要返回到视图的视图模型中。

标签: asp.net-core asp.net-core-5.0


【解决方案1】:

注意:第一个基于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();

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

【讨论】:

  • 这第一部分,Razor 视图扩展,(公共静态控制器 GetInvokedController(此 RazorPage 视图))没有工作,但后来工作。非常感谢
  • @AftabAhmedKalhoro 不客气,实际上我已经测试了这两种解决方案并且都在我身边工作。您能否分享更多关于它如何不适用于第一个解决方案的信息?是否有任何例外或只是您获得了控制器的null 实例?
  • @AftabAhmedKalhoro 我相信您在第一个解决方案中遇到的错误与您未注册的IActionContextAccessor 有关,但是我刚刚发现ViewContext 也是@ 987654343@,所以我们可以方便地使用它而不是使用IActionContextAccessor,它也缩短了代码:)
  • 出现错误:ControllerActionInvokerCache 由于其保护级别而无法访问“ControllerActionInvokerCache.GetCachedResult(ControllerContext)”由于其保护级别而无法访问“ControllerActionInvokerCacheEntry.ControllerFactory”由于其保护级别而无法访问
  • 正如我所提到的,我使用的是 .NET Core 5.0。所以可能框架被改变了。无论如何,非常感谢您的支持。
猜你喜欢
  • 2010-11-27
  • 2014-01-10
  • 2013-01-08
  • 2014-08-27
  • 2018-11-28
  • 2017-02-18
  • 1970-01-01
  • 2021-06-13
  • 1970-01-01
相关资源
最近更新 更多