Carlos Garcia
Carlos Garcia

Reputation: 2970

C# ILogger dependency Injection error: There is no argument

I have a Docker DotNet Core console app that processes messages from a queue.

There is a class called SimulationEngine and I was expecting that ILogger is passed by Dependency Injection automatically

public class SimulationEngine
{
    ILogger<SimulationEngine> logger;

    public SimulationEngine(ILogger<SimulationEngine> logger)
    {
        this.logger = logger;
    }

This is how the instance is created:

   public class RunsQueueProcessor : ...
   {
        public RunsQueueProcessor(..., ILogger<RunsQueueProcessor> logger)
        {
        }

        protected override async Task ProcessMessage(...)
        {
            this.logger.LogInformation(...);

            // Here DI is not working
            var engine = new SimulationEngine();
        

DI works fine to inject logger into RunsQueueProcessor, but it fails when I try to new SimulationEngine() with the following error:

Error   CS7036  There is no argument given that corresponds to the required formal parameter 'logger' of 'SimulationEngine.Simui lationEngine(ILogger<SimulationEngine>)'

How can I tell DotNet to use DI for that constructor?

--- EDIT ---

I wrote this question because I am working on a PoC and I don't need Dependency Injection everywhere. I only need it in few places where I am doing Unit Testing to validate specific algorithms. A more concrete questions is if there is a way to configure DotNet DI framework to inject dependencies when using the new statement or manually instantiating objects.

Thanks to all comments and answers, Now I understand more about how DotNet DI works.

Upvotes: 2

Views: 1909

Answers (1)

Useme Alehosaini
Useme Alehosaini

Reputation: 3116

According to your last comment, you want more focus on explaining the Dependency Injection: The Other option, which is the traditional and the right way to do is:

  1. Define a marker Interface like ISimulationEngine.
  2. Implement the interface ISimulationEngine in SimulationEngine
  3. Register the interface in IServiceCollection (in startup.cs)
  4. Inject it in the constructor of RunsQueueProcessor class so the app understands you want to use SimulationEngine.

The marker interface

public interface ISimulationEngine
{
    void DoSomthing();
}

The Service that implements the interface

public class SimulationEngine : ISimulationEngine
{
    private readonly ILogger<SimulationEngine> _logger;

    public SimulationEngine(ILogger<SimulationEngine> logger)  //<-- here you are injecting the registered Singleton of ILogger
    {
        _logger = logger;
    }

    public async void DoSomthing()
    {
        throw new NotImplementedException();
    }
}

Suppose another service implements the interface

public class AnotherService: ISimulationEngine
{
          public async void DoSomthing()
        {
            throw new NotImplementedException();
        }
}

The registration of service in startup.cs

public void ConfigureServices(IServiceCollection services)
{
    //...
    // this means whenever ISimulationEngine injected that class can use
    // SimulationEngine service

    services.AddTransient<ISimulationEngine, SimulationEngine>();
    //...
} 

Note that you registered the interface ISimulationEngine for SimulationEngine but not for AnotherService which is also implementing the same marker but because of the registration in the container says whenever you see ISimulationEngine injected go and use SimulationEngine

Now inject the ISimulationEngine in the class that you want to use

public class RunsQueueProcessor
{
    private readonly ILogger<RunsQueueProcessor> _logger;
    private readonly ISimulationEngine _service;

    public RunsQueueProcessor(ILogger<RunsQueueProcessor> logger,  //<-- here AGAIN you are injecting the registered Singleton of ILogger
                              ISimulationEngine service            //<-- her you inject the Marker Interface so you can use any class implement this interface
                              )
    {
        _logger = logger;
        _service = service;
    }

    protected async Task ProcessMessage()
    {
        _logger.LogInformation("fdfdfd");

        await _service.DoSomthing();

        // Here DI is not working
        //var engine = new SimulationEngine();
    }
}

As a conclusion: You can imagine the process of injecting like the class say go and check the DI container, which service is defined for this interface?! let me use it.

you may ask what is the different between AdTransient and AddSingleton (there is AddScoped as well) check the link AddTransient, AddScoped and AddSingleton Services Differences

Upvotes: 4

Related Questions