ASP.NET Core MVC 控制器创建与依赖注入
来源:互联网 发布:人工智能的英文缩写 编辑:程序博客网 时间:2024/05/20 22:29
在我最后一篇关于 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
.NET社区新闻,深度好文,微信中搜索dotNET跨平台或扫描二维码关注
- ASP.NET Core MVC 控制器创建与依赖注入
- ASP.NET Core 依赖注入
- ASP.Net Core的内置依赖注入
- ASP.NET Core 2.0 依赖注入
- ASP.NET Core 2.0 依赖注入
- asp.net mvc controller 依赖注入入门
- ASP.NET MVC 的依赖注入
- ASP.NET MVC中的依赖倒置IOC/依赖注入DI, (MircroSoft.Practices.Unity)与asp.net mvc 3的融合
- [.NET开发] ASP.NET Core 1.0基础之依赖注入
- Asp.net core 学习笔记--(1)依赖注入
- ASP.NET Core依赖注入解读&使用Autofac替代实现
- Asp.Net MVC 控制器
- Asp.Net MVC 控制器
- Asp.Net MVC 控制器
- Asp.Net MVC 控制器
- Asp.net mvc 控制器
- 【ASP.NET MVC】控制器
- ASP.NET MVC框架中的控制器与控制器行为
- 网页中下拉菜单的实现
- 面向 Visual Studio 开发者的 Git 内部源代码
- asp.net core新特性(1):TagHelper
- 拥抱.NET Core系列:依赖注入(2)
- 深入谈一谈String,StringBuilder,StringBuffer
- ASP.NET Core MVC 控制器创建与依赖注入
- Docker快速入门
- 基于.NET CORE微服务框架 -surging的介绍和简单示例 (开源)
- 使用领域事件
- .NET 跨平台界面框架和为什么你首先要考虑再三
- 如何利用.NET Core搭建跨平台的控制台应用程序
- DDD理论学习系列(9)-- 领域事件
- 一张图看懂小程序全生态
- 拥抱.NET Core系列:依赖注入(1)