ASP.NET Core MVC 控制器创建与依赖注入
本文翻译自《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
方法中使用MvcBuilder
的AddControllersAsServices()
扩展方法来配置此激活方式。
转载请注明出处,原文链接:http://www.cnblogs.com/tdfblog/p/controller-activation-and-dependency-injection-in-asp-net-core-mvc.html。
ASP.NET Core MVC 控制器创建与依赖注入的更多相关文章
- ASP.NET Core 入门教程 4、ASP.NET Core MVC控制器入门
一.前言 1.本教程主要内容 ASP.NET Core MVC控制器简介 ASP.NET Core MVC控制器操作简介 ASP.NET Core MVC控制器操作简介返回类型简介 ASP.NET C ...
- ASP.NET Core 入门笔记5,ASP.NET Core MVC控制器入门
摘抄自https://www.cnblogs.com/ken-io/p/aspnet-core-tutorial-mvc-controller-action.html 一.前言 1.本教程主要内容 A ...
- ASP.NET Core - 在ActionFilter中使用依赖注入
上次ActionFilter引发的一个EF异常,本质上是对Core版本的ActionFilter的知识掌握不够牢固造成的,所以花了点时间仔细阅读了微软的官方文档.发现除了IActionFilter.I ...
- 扒一扒asp.net core mvc控制器的寻找流程
不太会排版,大家将就看吧. asp.net core mvc和asp.net mvc中都有一个比较有意思的而又被大家容易忽略的功能,控制器可以写在非Web程序集中,比如Web程序集:"MyW ...
- asp.net core 系列之Dependency injection(依赖注入)
这篇文章主要讲解asp.net core 依赖注入的一些内容. ASP.NET Core支持依赖注入.这是一种在类和其依赖之间实现控制反转的一种技术(IOC). 一.依赖注入概述 1.原始的代码 依赖 ...
- ASP.NET Core应用的7种依赖注入方式
ASP.NET Core框架中的很多核心对象都是通过依赖注入方式提供的,如用来对应用进行初始化的Startup对象.中间件对象,以及ASP.NET Core MVC应用中的Controller对象和V ...
- 使用Rotativa在ASP.NET Core MVC中创建PDF
在本文中,我们将学习如何使用Rotativa.AspNetCore工具从ASP.NET Core中的视图创建PDF.如果您使用ASP.NET MVC,那么Rotativa工具已经可用,我们可以使用 ...
- Asp.Net Core MVC控制器和视图之间传值
一.Core MVC中控制器和视图之间传值方式和Asp.Net中非常类似 1.弱类型数据:ViewData,ViewBag 2.强类型数据:@model 二.代码 实例 1.ViewData pub ...
- asp.net core MVC 控制器,接收参数,数据绑定
1.参数 HttpRequest HttpRequest 是用户请求对象 QueryString Form Cookie Session Header 实例: public IActionResult ...
随机推荐
- 【T-SQL进阶】02.理解SQL查询的底层原理
本系列[T-SQL]主要是针对T-SQL的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式 ...
- 如何同时完成多个ajax之后再执行某个方法 ? 使用$.when().done();
jQuery中的$.when()方法比较复杂,这里不作全面讲解,只写一个同时完成多个ajax请求后执行操作的方法. 有时候我们需要等待多个ajax执行完以后,再执行某个操作. 写法如下: $.when ...
- 脑洞大开--一条项目中常用的linux命令引发的经典算法题
小时候家里定了<读者>的月刊,里面记录一个故事:说有有个偏僻的乡村一日突然来了一个美女,她携着万贯家财子女在当地安家落户,成了当地的乡绅.她让她的子女世世代代的保守这个秘密,直到这个秘密不 ...
- JavaScript 特效三大系列总结
一. offset系列 1. offset系列的5个属性 1. offsetLeft : 用于获取元素到最近的定位父盒子的左侧距离 * 计算方式: 当前元素的左边框的左侧到定位父盒子的左边框右侧 * ...
- 将某个日期字符串转换为java.sql.Date的类型
import java.text.ParseException; import java.text.SimpleDateFormat; public class date { /** * @param ...
- 关于Myeclipse不能加载已有项目的问题
如果缺少.project文件,你可以新建一个同名项目,把Use default location 去掉,选择要加载的项目,完成
- 网络接口配置--Bonding
Bonding 就是讲到快网卡绑定到同一IP地址对外服务,可以实现高可用或者负载均衡.当然,直接给两块网卡设置同一IP地址是不可能的.通过bonding,虚拟一块网卡对外提供连接,物理网卡被修改为同一 ...
- C#与Java对比学习
Eclipse开发环境与VS开发环境的调试对比 数据类型.集合类.栈与队列.迭达.可变参数.枚举 类型判断.类与接口继承.代码规范与编码习惯.常量定义
- Hibernate入门(四)
一 Hibernate缓存 缓存是介于应用程序和数据库之间,对数据库中的数据复制一份到缓存中,其作用就是为了减少应用程序对数据库的访问,访问数据库时先从缓存中取,提高了程序的性能.Hibernate缓 ...
- 在项目中利用TX Text Control进行WORD文档的编辑显示处理
在很多文档管理的功能模块里面,我们往往需要对WORD稳定进行展示.编辑等处理,而如果使用微软word控件进行处理,需要安装WORD组件,而且接口使用也不见得简单易用,因此如果有第三方且不用安装Offi ...