2021-04-03 乐帮网
mvc netcore
原文:https://andrewlock.net/controller-activation-and-dependency-injection-in-asp-net-core-mvc/
在我的最后一篇有关IDsiposable
在ASP.NET Core中部署的文章中,马克·雷德尔(Mark Rendle)指出,在请求结束时也部署了MVC控制器。乍一看,考虑到范围资源是在请求末尾分配的,这似乎很明显,但是MVC控制器实际上以与大多数服务稍有不同的方式进行处理。
在本文中,我将介绍如何使用IControllerActivator
,可用的可用选项在ASP.NET Core MVC中创建控制器,以及它们在依赖项注入方面的区别。
IControllerActivator
在ASP.NET Core中,当接收到请求时MvcMiddleware
,使用路由(常规路由或属性路由)选择要执行的控制器和操作方法。为了实际执行该动作,MvcMiddleware
必须创建所选控制器的实例。
创建控制器的过程取决于许多不同的提供程序和工厂类,最终以的实例为准IControllerActivator
。此类仅实现两种方法:
public interface IControllerActivator
{
object Create(ControllerContext context);
void Release(ControllerContext context, object controller);
}
如您所见,该IControllerActivator.Create
方法传递了context,该方法ControllerContext
定义了要创建的控制器。如何创建控制器取决于特定的实现。
现成的,ASP.NET Core使用DefaultControllerActivator
,使用TypeActivatorCache
来创建控制器。在TypeActivatorCache
通过调用类的构造函数,并试图解决从DI容器所需的构造函数参数的依赖创建对象的实例。
这是重要的一点。在DefaultControllerActivator
不尝试从DI容器本身,只有控制器的依赖性解决控制器实例。
为了演示这种行为,我创建了一个简单的MVC应用程序,该应用程序由一个服务和一个控制器组成。服务实例具有一个名称属性,该名称属性是在构造函数中设置的。默认情况下,它将具有值"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容器中将其注册为范围服务,并设置MvcMiddleware
和服务:
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
正在创建控制器时,这很有意义。它不HomeController
向DI容器请求,而仅请求其构造函数依赖项。
在大多数情况下,使用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)
{
}
}
AddControllersAsServices()
将MVC服务添加到应用程序时,可以使用扩展方法配置基于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
方法有两件事-将应用程序中的所有Controller都注册到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
自己,但是我无法想到这两个实现不能满足您所有要求的任何原因!
DefaultControllerActivator
配置为IControllerActivator
。DefaultControllerActivator
用途TypeActivatorCache
创建控制器。这将创建控制器的实例,并从DI容器中加载构造函数参数。ServiceBasedControllerActivator
直接从DI容器加载控制器。您可以通过AddControllersAsServices()
在中的MvcBuilder
实例上使用扩展方法来配置此激活器Startup.ConfigureServices
。
关注我的微信公众号
在公众号里留言交流
投稿邮箱:1052839972@qq.com
庭院深深深几许?杨柳堆烟,帘幕无重数。
玉勒雕鞍游冶处,楼高不见章台路。
雨横风狂三月暮。门掩黄昏,无计留春住。
泪眼问花花不语,乱红飞过秋千去。
如果感觉对您有帮助
欢迎向作者提供捐赠
这将是创作的最大动力