Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why ServiceProvider is unable to create instance of controller in ASP.NET Core?

I develop integration tests in .NET Core. Because controllers have multiple dependencies it's inconvenient to create their instances manually. Here's how I create ServiceProvider instance to be able to access .NET Core native DI container:

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();

Startup class is taken from tested assembly.

To my surprise service provider is unable to create instances of controllers. It can instantiate registered services, but not the controllers. Does anybody have any idea why it is so?

like image 601
Arkadiusz Kałkus Avatar asked Oct 21 '25 01:10

Arkadiusz Kałkus


1 Answers

You should use TestServer, which bootstraps a complete test environment (optionally using a different startup class or environment variable, where you can replace data store with an in-memory store).

The ASP.NET Core documentation covers this case quite well.

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"));

In these alternative/environment-dependent methods you can put your integration test specific replacements/DI configuration.

Second, controllers (and tag helpers and view components) are not registered with the DI system by default. The controller factories will resolve the types. If you want that the controllers to get resolved via built-in or 3rd party IoC containers, you have to tell ASP.NET Core to register them explicitly (see this related question).

Here is an example:

services
    .AddMvc()
    .AddControllersAsServices()
    .AddViewComponentsAsServices()
    .AddTagHelpersAsServices();

I would recommend you to use the first approach, as it's more consistent and exactly the reason the Startup class with environment support is there.

Update

Important addition. If you use the TestServer, you can also access its DI Container (IServiceProvider instance).

var server = new TestServer(new WebHostBuilder()
        .UseStartup<Startup>()
        .UseEnvironment("IntegrationTest"));
var controller = server.Host.Services.GetService<MyController>();
like image 109
Tseng Avatar answered Oct 24 '25 08:10

Tseng



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!