好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

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

默认的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() 扩展方法来配置此激活方式。

 到此这篇关于ASP.NET Core MVC创建控制器与依赖注入的文章就介绍到这了。希望对大家的学习有所帮助,也希望大家多多支持。

查看更多关于ASP.NET Core MVC创建控制器与依赖注入讲解的详细内容...

  阅读:51次