Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/asp.net-core/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Dependency injection 为什么ServiceProvider无法在ASP.NET Core中创建控制器实例?_Dependency Injection_Asp.net Core_Asp.net Core Mvc - Fatal编程技术网

Dependency injection 为什么ServiceProvider无法在ASP.NET Core中创建控制器实例?

Dependency injection 为什么ServiceProvider无法在ASP.NET Core中创建控制器实例?,dependency-injection,asp.net-core,asp.net-core-mvc,Dependency Injection,Asp.net Core,Asp.net Core Mvc,我在.NETCore中开发集成测试。因为控制器有多个依赖项,所以手动创建它们的实例很不方便。以下是我如何创建ServiceProvider实例以访问.NET核心本机DI容器: HostingEnvironment env = new HostingEnvironment(); env.ContentRootPath = Directory.GetCurrentDirectory(); env.EnvironmentName = "Development"; Startup startup =

我在.NETCore中开发集成测试。因为控制器有多个依赖项,所以手动创建它们的实例很不方便。以下是我如何创建ServiceProvider实例以访问.NET核心本机DI容器:

HostingEnvironment env = new HostingEnvironment();
env.ContentRootPath = Directory.GetCurrentDirectory();
env.EnvironmentName = "Development";

Startup startup = new Startup(env);
ServiceCollection sc = new ServiceCollection();
startup.ConfigureServices(sc);
ServiceProvider = sc.BuildServiceProvider();
启动
类取自测试程序集

令我惊讶的是,服务提供商无法创建控制器的实例。它可以实例化已注册的服务,但不能实例化控制器。有人知道为什么会这样吗

因为控制器有多个依赖项,所以手动创建它们的实例很不方便

事实不应如此。在编写单元测试时,您不应该依赖于DI容器;您应该手动创建依赖项。在容器上添加依赖项只会使测试复杂化,并在库上添加不必要的依赖项

您可以应用一些有趣的模式来简化您的创建。其中之一是模式的一种变体,您在测试类中有一个私有工厂方法来构建SUT:

private static ShipOrderController CreateController(
    IShipOrderHandler handler = null,
    ILogger logger = null,
    IMailSender mailSender = null)
{
    return new ShipOrderController(
        handler ?? new ShipOrderHandlerStub(),
        logger ?? new LoggerStub(),
        mailSender ?? new MailSenderStub());
}
这样的SUT生成器使您的单元测试非常可读和可维护,因为向SUT添加依赖项只会影响工厂方法;没有任何现有的测试

测试可以如下所示:

[TestMethod]
public void Index_Always_Logs()
{
    // Arrange
    var logger = new FakeLogger();

    var sut = CreateController(logger: logger);

    // Act
    sut.Index();

    // Assert
    Assert.IsTrue(logger.Entries.Any());
}

您应该使用
TestServer
,它引导一个完整的测试环境(可以选择使用不同的启动类或环境变量,在这里您可以用内存存储替换数据存储)

ASP.NET内核很好地解决了这个问题

var server = new TestServer(new WebHostBuilder()
    .UseStartup<Startup>()
    // this would cause it to use StartupIntegrationTest or ConfigureServicesIntegrationTest / ConfigureIntegrationTest methods (if existing)
    // rather than Startup, ConfigureServices and Configure
    .UseEnvironment("IntegrationTest"));
我建议您使用第一种方法,因为它更为一致,而且正是有环境支持的
Startup
类存在的原因

更新 重要的补充。如果使用
TestServer
,还可以访问其DI容器(
IServiceProvider
实例)

var server=newtestserver(new WebHostBuilder()
.UseStartup()
.UseEnvironment(“集成测试”);
var controller=server.Host.Services.GetService();

编写单元测试时,不应依赖DI容器;您应该手动创建依赖项(请重新阅读问题;)他询问有关整合的问题tests@Tseng:谢谢你指出这一点。您是对的,对于集成测试来说,使用与生产中相同的基础结构(即容器)更为自然。您是否尝试过使用
TestServer
而不是自己布线?
services
    .AddMvc()
    .AddControllersAsServices()
    .AddViewComponentsAsServices()
    .AddTagHelpersAsServices();
var server = new TestServer(new WebHostBuilder()
        .UseStartup<Startup>()
        .UseEnvironment("IntegrationTest"));
var controller = server.Host.Services.GetService<MyController>();