ASP.NET Core MVC 控制器创建与依赖注入

在我最后一篇关于 ASP.NET Core 释放IDsiposable对象的文章(中文、英文原文)中,Mark Rendle 指出,MVC 控制器在请求结束时也会释放资源。乍一看,此范围内的资源在请求结束时会释放似乎是显而易见的,但是 MVC 控制器的处理方式实际上与大多数服务略有不同。

在这篇文章中,我将介绍在ASP.NET Core MVC中IControllerActivator是如何创建控制器的,以及通过依赖注入创建控制器存在的差异。

默认的IControllerActivator

在 ASP.NET Core 中,当 MVC 中间件接收到请求时,通过路由选择要执行的控制器和操作方法。为了实际的执行操作, MVC 中间件必须创建所选控制器的实例。

创建控制器的过程依赖众多不同的提供者和工厂类,但最终是由实现IControllerActivator接口的实例来决定的。实现类只需要实现两个方法:

public interface IControllerActivator  {   
 object Create(ControllerContext context);  
  void Release(ControllerContext context, object controller); }

如您所见,该IControllerActivator.Create方法传递了用于创建控制器的ControllerContext实例。控制器的创建方式取决于具体的实现。

众所周知,ASP.NET Core 使用的是DefaultControllerActivator,它通过TypeActivatorCache来创建控制器。TypeActivatorCache通过调用类的构造函数,并试图从 DI 容器中解析构造函数所需参数的实例。

有一点很重要,DefaultControllerActivator 不会试图从 DI 容器中解析控制器的实例,只会解析控制器的依赖项。


DefaultControllerActivator 示例

为了演示这个行为,我创建了一个简单的 MVC 应用程序,包括一个单一的服务和一个控制器。服务实例有一个name属性,它通过构造函数来设置。默认情况下,它使用"default"作为默认值。

public class TestService  {   
 public TestService(string name = "default")    {Name = name;}    public string Name { get; } }

在应用程序中HomeController依赖于TestService,并返回Name属性的值:

public class HomeController : Controller  {  

 private readonly TestService _testService;  
 
 public HomeController(TestService testService)    {_testService = testService;}  
 
 public string Index()    {      
              return "TestService.Name: " + _testService.Name;} }

还有一块代码在Startup文件中。在这里我将TestService注册在 DI 容器中作为范围内服务,并设置 MVC 中间件和服务:

public class Startup  {   
 public void ConfigureServices(IServiceCollection services)    {services.AddMvc();services.AddScoped<TestService>();services.AddTransient(ctx =>      
      new HomeController(new TestService("Non-default value")));}    public void Configure(IApplicationBuilder app)    {app.UseMvcWithDefaultRoute();} }

您会注意到,我定义了一个工厂方法用于创建HomeController的实例。将HomeController类型注册到 DI 容器中,并且在TestService实例中传递自定义Name属性。

如果您运行应用程序,您会看到什么结果?

您可以看到,该TestService.Name属性使用的是默认值,表示TestService实例是直接从 DI 容器中获取的,直接忽略了创建HomeController的工厂方法。

这很容易理解,当您通过DefaultControllerActivator创建控制器时,它不会从DI容器中创建HomeController实例,只会解析构造函数的依赖项。

大多数情况下,使用DefaultControllerActivator是一个不错的选择,但有时您可能希望直接通过 DI 容器来创建控制器,比如您希望使用具有拦截器或装饰器等功能的第三方容器。

幸运的是,MVC 框架包含了一个这样的IControllerActivator实现,并提供了一种非常方便的扩展方法来启用它。


ServiceBasedControllerActivator

如您所见,DefaultControllerActivator使用TypeActivatorCache来创建控制器,MVC还包括另一个实现,称为ServiceBasedControllerActivator,它是直接从 DI 容器中获取控制器。它的实现非常简单:

public class ServiceBasedControllerActivator : IControllerActivator  {    public object Create(ControllerContext actionContext)    {  
     var controllerType = actionContext.ActionDescriptor.ControllerTypeInfo.AsType();  
           return actionContext.HttpContext.RequestServices.GetRequiredService(controllerType);}    public virtual void Release(ControllerContext context, object controller)    {} }

当您将 MVC 服务添加到应用程序时,可以使用AddControllersAsServices()扩展方法配置基于 DI 的激活器:

public class Startup  {  
 public void ConfigureServices(IServiceCollection services)    {services.AddMvc().AddControllersAsServices();services.AddScoped<TestService>();services.AddTransient(ctx =>      
       new HomeController(new TestService("Non-default value")));}    public void Configure(IApplicationBuilder app)    {app.UseMvcWithDefaultRoute();} }

通过上面的代码,点击主页将通过 DI 容器来创建一个控制器。由于我们已经注册了一个创建HomeController的工厂方法,我们自定义TestService配置将被保留,使用替换后的Name属性:

AddControllersAsServices方法实现了两件事情 - 它将您应用程序中的所有控制器注册到 DI 容器(如果尚未注册),并将IControllerActivator注册为ServiceBasedControllerActivator

public static IMvcBuilder AddControllersAsServices(this IMvcBuilder builder)  { 
   var feature = new ControllerFeature();builder.PartManager.PopulateFeature(feature);
   foreach (var controller in feature.Controllers.Select(c => c.AsType())){builder.Services.TryAddTransient(controller, controller);}builder.Services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());
      return builder; }

如果需要做一些更复杂的事情,您可以随时实现自己IControllerActivator;不过我找不到任何理由,这两点实现还不能满足您的需求!


总结

  • 默认情况下,在ASP.NET Core MVC 中IControllerActivator配置为DefaultControllerActivator

  • DefaultControllerActivator使用TypeActivatorCache来创建控制器。它从 DI 容器加载构造函数所需参数来创建控制器的实例。

  • 您也可以使用ServiceBasedControllerActivator作替代方法,它直接从 DI 容器加载控制器。您可以在Startup.ConfigureServices方法中使用MvcBuilderAddControllersAsServices()扩展方法来配置此激活方式。

原文地址:http://www.cnblogs.com/tdfblog/p/controller-activation-and-dependency-injection-in-asp-net-core-mvc.html


.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/324713.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

19级:一天一瞬间

点击上方蓝色关注我们&#xff01;3班的KTV项目周期为两周&#xff0c;目前时间将近一周&#xff0c;除李磊小组进度超出之外&#xff0c;其余四个小组的进度都属正常。上午大致的将每个小组的前台功能过了一遍&#xff0c;刘文瑞小组还剩几个点歌功能未实现&#xff0c;整体的…

拥抱.NET Core系列:依赖注入(2)

上一篇“拥抱.NET Core系列&#xff1a;依赖注入&#xff08;1&#xff09;大体介绍了服务注册、获取和生命周期&#xff0c;这一篇来做一些补充。由于内容跨度大&#xff08;.NET Core、ASP.NET Core&#xff09;&#xff0c;所以文章并不是非常的贯穿&#xff0c;大家可以挑着…

使用Docker 实现微服务并搭建博客,一文全掌握

转载自 使用Docker 实现微服务并搭建博客&#xff0c;一文全掌握 Docker 是一个容器工具&#xff0c;提供虚拟环境。很多人认为&#xff0c;它改变了我们对软件的认识。 本文&#xff0c;通过搭建一个博客的例子&#xff0c;来介绍如何使用Docker实现微服务。 站在 Docker …

失望至极……

点击上方蓝色关注我们&#xff01;

asp.net core新特性(1):TagHelper

进步&#xff0c;才是人应该有的现象。—— 雨果 今天开始&#xff0c;我就来说说asp.net core的新特性&#xff0c;今天就说说TagHelper标签助手。虽然学习.net&#xff0c;最有帮助的就是microsoft的官方说明文档了&#xff0c;里面把一些使用说明都写的非常清楚&#xff0c;…

Java中的事务——全局事务与本地事务

转载自 Java中的事务——全局事务与本地事务 在上一篇文章中说到过&#xff0c;Java事务的类型有三种&#xff1a;JDBC事务、JTA(Java Transaction API)事务、容器事务。 这是从事务的实现角度区分的&#xff0c;本文从另外一个角度来再次区分一下Java中的事务。站在事务管…

SSM整合简单登录案例

[1] 在数据库中创建用户信息表 [2] 搭建SSM开发环境 使用idea创建登录功能的web项目 在web-inf目录下创建lib文件夹&#xff0c;并导入SSM的jar包. 在src下创建MVC的包结构 在src下创建并配置SSM的xml文件 ① applicationcontext.xml <?xml version"1.0"…

19级、20级:班级日常分享,一天一瞬间

点击上方蓝色关注我们&#xff01;由于近期班级里面总是有或大或小的问题&#xff0c;所以就和丁老师商量的制定了长达29条的班规&#xff0c;昨天利用下午上课的时间在班内逐条讲解。因为班规是以安全、学习、道德以及身心健康为中心出发的&#xff0c;所以条条都合理&#xf…

同步VS异步

同步异步 &#xff0c; 举个例子来说&#xff0c;一家餐厅吧来了5个客人&#xff0c;同步的意思就是说&#xff0c;来第一个点菜&#xff0c;点了个鱼&#xff0c;好&#xff0c; 厨师去捉鱼杀鱼&#xff0c;过了半小时鱼好了给第一位客人&#xff0c;开始下位一位客人&#xf…

面向 Visual Studio 开发者的 Git 内部源代码

在我撰写的 Git DevOps 文章 (msdn.com/magazine/mt767697) 中&#xff0c;我介绍了 Git 版本控制系统 (VCS) 与可能已经很熟悉的集中式 VCS 的区别。然后&#xff0c;我演示了如何在 Visual Studio 中使用 Git 工具完成一些 Git 任务。在本文中&#xff0c;我将汇总 Git 在新发…

Intellij IDEA神器居然还有这些小技巧

转载自 Intellij IDEA神器居然还有这些小技巧 Intellij IDEA真是越用越觉得它强大&#xff0c;它总是在我们写代码的时候&#xff0c;不时给我们来个小惊喜。出于对Intellij IDEA的喜爱&#xff0c;我决定写一个与其相关的专栏或者系列&#xff0c;把一些好用的Intellij IDE…

SpringMVC中使用作用域对象完成数据的流转

文章目录SpringMVC中使用作用域对象完成数据的流转[1] 作用域对象复习[2] SpringMVC中使用作用域对象流转数据[3] SpringMVC的Model对象的使用SpringMVC中使用作用域对象完成数据的流转 [1] 作用域对象复习 PageContext对象 作用域范围:当前jsp页面内有效request对象 作用域范…

为什么总是喜欢看我桌面呢?

点击上方蓝色关注我们&#xff01;每天上课&#xff0c;尤其是19级&#xff0c;在刚刚连接上投影仪的那一瞬间&#xff0c;差不多50双眼睛盯着我的桌面看&#xff0c;由于桌面上的字比较小&#xff0c;所以大家眼睛都不带的眨一下的。我的桌面也没啥好看的呀&#xff0c;不就是…

原码,反码,补码

原码就是符号位加上真值的绝对值&#xff0c; 即用第一位表示符号&#xff0c; 其余位表示值。 反码的表示方法是:正数的反码是其本身&#xff1b;负数的反码是在其原码的基础上&#xff0c; 符号位不变&#xff0c;其余各个位取反。 补码的表示方法是:正数的补码就是其本身&am…

【2018.3.10】模拟赛之三-ssl2576 平台

目录地址 前言 水题不想说&#xff0c;连自带快排都懒得用了 正题 有n个平台&#xff0c;每个平台头尾有两根柱子支撑到地板或另一个平台上&#xff0c;求支撑平台需要的柱子数 输入输出&#xff08;需要自取&#xff09; Input 输入文件platforme.in第一行包括1个整数N…

再谈消息队列技术

上周&#xff0c;我们举办了第二届技术沙龙&#xff0c;我这边主要演讲了消息队列技术的议题&#xff0c;现分享给大家&#xff1a; 在我们团队内部&#xff0c;随着消息应用中心&#xff08;任务中心&#xff09;的广泛应用&#xff0c;有时候我们感觉不到消息队列的存在&…

20级:班级日常分享,一天一瞬间

点击上方蓝色关注我们&#xff01;今天批改的作业不仅仅是卡个印有“优”字样式的章&#xff0c;当然也不仅仅是用红笔写个“SSSS9.28”字样的批语了。除了给每个学生的作业本上都卡了“优”的章之外&#xff0c;还送给每位同学一句或长或短的话&#xff0c;有表扬有批评&#…

SpringMVC的视图解析器

文章目录SpringMVC的自定义视图解析器[1] SpringMVC的视图解析器[2] SpringMVC的自定义视图解析器SpringMVC自定义视图解析器的使用[1] 目前项目资源的声明位置和访问中存在的问题[2] 使用自定义视图解析器优化资源跳转路径[3] 使用restful声明公共单元方法请求转发WEB-INF下的…

互联网级监控系统必备-时序数据库之Influxdb技术

时间序列数据库&#xff0c;简称时序数据库&#xff0c;Time Series Database&#xff0c;一个全新的领域&#xff0c;最大的特点就是每个条数据都带有Time列。 时序数据库到底能用到什么业务场景&#xff0c;答案是&#xff1a;监控系统。 Baidu一下&#xff0c;互联网监控系…