Search code examples
c#asp.net-core.net-corestartup

Is there a difference between method injection or constructor injection of services in the Configure() method of the Startup class?


According to the docs we can inject the following services into the startup class:

  • IWebHostEnvironment
  • IHostEnvironment
  • IConfiguration

But we can also use method injection to resolve these services in the Configure() method:

Additional services, such as IWebHostEnvironment, ILoggerFactory, or anything defined in ConfigureServices, can be specified in the Configure method signature. These services are injected if they're available.

Does it make a difference which injection variant I use?

To be specific, is there a difference resolving the IWebHostEnvironment in the constructor and then access it in the Configure() method via private field vs. injecting it as a method parameter?

public class Startup
{
    private readonly IWebHostEnvironment env;

    public Startup(IWebHostEnvironment env)
    {
        this.env = env;
    }

    public void Configure(IApplicationBuilder app)
    {
        if (this.env.IsDevelopment())
        [...]
    }
}

vs.

public class Startup
{
    public Startup() {}

    public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
    {
        if (env.IsDevelopment())
        [...]
    }
}

Solution

  • Generally speaking, constructor & method DI can be a preference or a requirement based on some framework that uses this but not the other. In asp.net core (as well as .net core generally), it's recommended to use constructor injection because it states clearly the dependencies. That makes more sense than method injection. However in some cases you have to use method injection because the dependencies can only be available at the time of invoking the method or at least not available at the time of constructing the classes/services. Sometimes the injected services are used only for a specific method call, in such cases method injection will be used. In some cases, the service instance is singleton but its method call requires some scoped services so method injection will be used instead. One example for that scenario is when using convention-based middleware. Convention-based middlewares are singleton, created once at the app startup, not for each request. So all scoped services must be injected into the Invoke or InvokeAsync method. Factory-based middlewares however can have its scoped services injected into the constructor because they can be created per request (registered as scoped or transient).

    As in the case of Startup class. You can inject some built-in services into the Startups constructor such as IConfiguration, IWebHostEnvironment, ... Usually many services available (registered before) before ConfigureServices can be injected into Startup's constructor. That means all services registered in ConfigureServices are NOT available for injection into the Startup's constructor. You can however inject all registered services (including built-in ones and yours) into the method Configure. Note that the services injected into Configure should be singleton & transient, for scoped services there may be issues when using them. That really depends on the specific services you use.

    Here is an example showing that injecting a service of your own (registered in ConfigureServices) cannot be done into the Startup's constructor:

     public interface ISomeService {}
     public class SomeService : ISomeService {}
    
    
     public class Startup {
          //will not work because at this time, the ISomeService has not been registered yet
          public Startup(ISomeService someService){
              //an error will be thrown complaining about not being able to 
              //resolve the ISomeService
          }
    
          public void ConfigureServices(IServiceCollection services){
              //...
              services.AddSingleton<ISomeService,SomeService>();
              //...
          }
          //this works because at this time the ConfigureServices was called
          //the services container has been built with your registered ISomeService
          public void Configure(IApplicationBuilder app, ISomeService someService){
              //...
          }
     }