Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Cannot inject my own objects in Startup constructor in .NET Core 2.0

I'm trying to inject my own classes using dependency injection, like this:

// Startup.cs
public class Startup
{
    private readonly ILogger _log;
    private readonly IMainController _controller;
    public Startup(ILoggerFactory loggerFactory, IMainController controller)
    {
        _log = loggerFactory.CreateLogger("Logger");
        _controller = controller;
    }

    // This method gets called by the runtime. Use this method to add services to the container.
    // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped<IMainController, MainController>();
        // services.AddTransient<MainController, MainController>();
    }

and then MainController, the object to be injected

// MainController.cs
public interface IMainController
{
    Task Run(HttpContext context);
}
public class MainController : IMainController
{
    private readonly ILogger _log;

    public MainController(ILoggerFactory loggerFactory)
    {
        _log = loggerFactory.CreateLogger("Logger");
    }

At runtime, I get the following error:

Unhandled Exception: System.InvalidOperationException: Unable to resolve service for type 'mtss.ws.IMainController' while attempting to activate 'mtss.ws.Startup'. at Microsoft.Extensions.Internal.ActivatorUtilities.ConstructorMatcher.CreateInstance(IServiceProvider provider) at Microsoft.Extensions.Internal.ActivatorUtilities.CreateInstance(IServiceProvider provider, Type instanceType, Object[] parameters)

I want to inject an ILoggerFactory in MainController (as it's doing in Startup) and then inject a newly created MainController in Startup...

like image 884
opensas Avatar asked Sep 02 '17 06:09

opensas


4 Answers

Besides @nkosi answer at https://stackoverflow.com/a/46013224/47633, you can also add dependencies using the WebHostBuilder ConfigureServices method, like stated in the docs:

Any services added by the WebHostBuilder ConfigureServices method may be requested by the Startup class constructor or its Configure method. Use WebHostBuilder to provide any services you need during Startup methods.

It would be something like this

// ...other code removed for brevity
// in Program.cs
public static void Main(string[] args)
{
    BuildWebHost(args).Run();
}

public static IWebHost BuildWebHost(string[] args) =>
    WebHost.CreateDefaultBuilder(args)
        .ConfigureServices(services =>
            services.AddScoped<IMainController, MainController>()
        )
        .UseStartup<Startup>()
        .Build();

and then in Startup.cs I can do the following

private readonly ILogger _log;
private readonly IMainController _controller;

public Startup(ILoggerFactory loggerFactory, IMainController controller)
{
    _log = loggerFactory.CreateLogger("Logger");
    _controller = controller;
}

This gist helped me figure it out, it's got lot's of useful examples

like image 154
opensas Avatar answered Oct 12 '22 17:10

opensas


According to documentation, you should note the following

Services Available in Startup

ASP.NET Core dependency injection provides services during an application's startup. You can request these services by including the appropriate interface as a parameter on your Startup class's constructor or its Configure method. The ConfigureServices method only takes an IServiceCollection parameter (but any registered service can be retrieved from this collection, so additional parameters are not necessary).

Below are some of the services typically requested by Startup methods:

  • In the constructor: IHostingEnvironment, ILogger<Startup>
  • In the ConfigureServices method: IServiceCollection
  • In the Configure method: IApplicationBuilder, IHostingEnvironment, ILoggerFactory

Any services added by the WebHostBuilder ConfigureServices method may be requested by the Startup class constructor or its Configure method. Use WebHostBuilder to provide any services you need during Startup methods.

You are trying to resolve a service that is not available when the constructor of Startup is called. IMainController is not registered as yet when the constructor is called. however, It should be available by the time Configure is called, allowing an opportunity to inject your custom service because it is called after ConfigureServices and as of RTM, a scoped service provider will be created for the Configure method.

// Startup.cs
public class Startup {
    private ILogger _log;
    private IMainController _controller;

    public Startup() {

    }

    public void ConfigureServices(IServiceCollection services) {
        services.AddScoped<IMainController, MainController>();
        // services.AddTransient<MainController, MainController>();
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, 
               ILoggerFactory loggerFactory, IMainController controller) {
        _log = loggerFactory.CreateLogger("Logger");
        _controller = controller;
        //...other code removed for brevity
    }
}

The above should work in .net-core 2.0

like image 20
Nkosi Avatar answered Oct 12 '22 16:10

Nkosi


Your setup is impossible. The constructor of Startup will run before your ConfigureServices method, which means you are trying to inject IMainController before you register it for dependency injection.

like image 1
Kirk Larkin Avatar answered Oct 12 '22 17:10

Kirk Larkin


I will assume you are building an ASP.Net Core2.0 MVC Application (but your MainController does not inherit from Controller so I am suspicious about this) You do not need to register your controller in the DI container. The Constructor you have should be sufficient for ASP.Net to inject the ILogger concrete instance for you.

public MainController(ILoggerFactory loggerFactory)

If you wanted to also add your own service into the controller, the controllers constructor would change to be thus:-

Controller

public MainController(ILoggerFactory loggerFactory, IMyService myService)

Your service registration in Startup.cs may look like this:-

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddScoped<IMyService, MyService>();
}

MyService

public interface IMyService
{
    void DoSomethingPLEASE();
}
public class MyService : IMyService
{
    public void DoSomethingPLEASE()
    {
        // Do Something PLEASE, ANYTHING!
    }
}

HomeController

public class HomeController : Controller
{
    public HomeController(ILoggerFactory loggerFactory, IMyService myServce)
    {
        myServce.DoSomethingPLEASE();
    }
    public IActionResult Index()
    {
        return View();
    }
}
like image 1
Mr Slim Avatar answered Oct 12 '22 17:10

Mr Slim