本文翻译自《Controller activation and dependency injection in 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

ASP.NET Core MVC 控制器创建与依赖注入的更多相关文章

  1. ASP.NET Core 入门教程 4、ASP.NET Core MVC控制器入门

    一.前言 1.本教程主要内容 ASP.NET Core MVC控制器简介 ASP.NET Core MVC控制器操作简介 ASP.NET Core MVC控制器操作简介返回类型简介 ASP.NET C ...

  2. ASP.NET Core 入门笔记5,ASP.NET Core MVC控制器入门

    摘抄自https://www.cnblogs.com/ken-io/p/aspnet-core-tutorial-mvc-controller-action.html 一.前言 1.本教程主要内容 A ...

  3. ASP.NET Core - 在ActionFilter中使用依赖注入

    上次ActionFilter引发的一个EF异常,本质上是对Core版本的ActionFilter的知识掌握不够牢固造成的,所以花了点时间仔细阅读了微软的官方文档.发现除了IActionFilter.I ...

  4. 扒一扒asp.net core mvc控制器的寻找流程

    不太会排版,大家将就看吧. asp.net core mvc和asp.net mvc中都有一个比较有意思的而又被大家容易忽略的功能,控制器可以写在非Web程序集中,比如Web程序集:"MyW ...

  5. asp.net core 系列之Dependency injection(依赖注入)

    这篇文章主要讲解asp.net core 依赖注入的一些内容. ASP.NET Core支持依赖注入.这是一种在类和其依赖之间实现控制反转的一种技术(IOC). 一.依赖注入概述 1.原始的代码 依赖 ...

  6. ASP.NET Core应用的7种依赖注入方式

    ASP.NET Core框架中的很多核心对象都是通过依赖注入方式提供的,如用来对应用进行初始化的Startup对象.中间件对象,以及ASP.NET Core MVC应用中的Controller对象和V ...

  7. 使用Rotativa在ASP.NET Core MVC中创建PDF

    在本文中,我们将学习如何使用Rotativa.AspNetCore工具从ASP.NET Core中的视图创建PDF.如果您使用ASP.NET MVC,那么Rot​​ativa工具已经可用,我们可以使用 ...

  8. Asp.Net Core MVC控制器和视图之间传值

    一.Core MVC中控制器和视图之间传值方式和Asp.Net中非常类似 1.弱类型数据:ViewData,ViewBag 2.强类型数据:@model 二.代码 实例  1.ViewData pub ...

  9. asp.net core MVC 控制器,接收参数,数据绑定

    1.参数 HttpRequest HttpRequest 是用户请求对象 QueryString Form Cookie Session Header 实例: public IActionResult ...

随机推荐

  1. 把ipad作为电脑的第二显示器

    需要:1,iPad          2,ios端需要软件idisplay(在pp助手里搜索idisplay下载即可)       3,windows需要软件iDisplay(链接http://www ...

  2. MySQL优化总结,百万级数据库优化方案

    1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引. 2.应尽量避免在 where 子句中对字段进行 null 值判断,否则将导致引擎放弃使用索 ...

  3. 从零开始理解JAVA事件处理机制(2)

    第一节中的示例过于简单<从零开始理解JAVA事件处理机制(1)>,简单到让大家觉得这样的代码简直毫无用处.但是没办法,我们要继续写这毫无用处的代码,然后引出下一阶段真正有益的代码. 一:事 ...

  4. vue-cli项目中怎么mock数据

    在vue项目中, mock数据可以使用 node 的 express模块搭建服务 1. 在根目录下创建 test 目录, 用来存放模拟的 json 数据, 在 test 目录下创建模拟的数据 data ...

  5. iOS地理围栏技术的应用

    遇到一个需求,要求监测若干区域,设备进入这些区域则要上传数据,且可以后台监测,甚至app被杀死也要监测.发现oc的地理围栏技术完美匹配这个需求,任务做完了,把遇到的坑记录下来,也许能帮到你呢. 要做这 ...

  6. go服务端----使用dotweb框架搭建简易服务

    使用dotweb框架搭建简易服务 go语言web框架挺多的,所谓琳琅满目,里面也有很多优秀的,比如echo.beego等,但体验下来,总是觉得哪里有点小疙瘩,后来才明白过来,echo太简单,很多日常使 ...

  7. C++构造函数(二)

    本篇是介绍C++的构造函数的第二篇(共二篇),属于读书笔记,对C++进行一个系统的复习. 复制构造函数 复制构造函数是构造函数的一种,也被称为拷贝构造函数,他只有一个参数,参数类型是本类的引用.默认构 ...

  8. zookeeper 新手安装指南

    Zookeeper集群的角色:  Leader 和  follower  (Observer) zk集群最好配成奇数个节点 只要集群中有半数以上节点存活,集群就能提供服务 本事例采用版本:zookee ...

  9. PHP中的抽象类与抽象方法/静态属性和静态方法/PHP中的单利模式(单态模式)/串行化与反串行化(序列化与反序列化)/约束类型/魔术方法小结

      前  言  OOP  学习了好久的PHP,今天来总结一下PHP中的抽象类与抽象方法/静态属性和静态方法/PHP中的单利模式(单态模式)/串行化与反串行化(序列化与反序列化). 1  PHP中的抽象 ...

  10. 如何让.Net线程支持超时后并自动销毁!

    如何让.Net线程支持超时后并自动销毁! 1.使用CancellationTokenSource之基于Task实现方式 CancellationTokenSource source = new Can ...