Chad K
Chad K

Reputation: 942

Base Class type for ILogger<T> using Dependency Injection

I have a base class that does some work, including logging. I have an ILogger dependency injected into the constructor

public abstract class BaseClassExample
{
    protected readonly ILogger<BaseClassExample> logger;

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

And I want to have classes implement BaseClassExample, and do their own work too, also including logging.

public class DerivedClass : BaseClassExample
{
    protected readonly ILogger<DerivedClass> logger;

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

Is this the right way of doing things? Am I supposed to get the implementing class's type for logging for the base class? Should I have a separate instance of a logger (with the DerivedClass's type) or try and use the same one as the base class?

Upvotes: 36

Views: 15090

Answers (5)

Robert Synoradzki
Robert Synoradzki

Reputation: 2026

Fixing @F_IVI_L's answer.

To inject a logger to a base class while still allowing the child class to use (inherit) it, do this:

public abstract class BaseClass
{
    protected readonly ILogger<BaseClass> logger;

    protected BaseClass(ILogger<BaseClass> logger)
    {
        this.logger = logger;
    }
}
public class DerivedClass : BaseClass
{
    public DerivedClass(ILogger<DerivedClass> logger) :
        base(logger)
    { }
}

Even though Intellisense will show you that this.logger is of type ILogger<BaseClass>, it will actually be of type ILogger<DerivedClass> when used in DerivedClass (because of ILogger<out T> being covariant as explained here and here).

Upvotes: 6

Wouter
Wouter

Reputation: 2938

Because ILogger<T> is covariant you can use ILogger<BaseClass> in the constructor of the baseclass and call it with an ILogger<DerviedClass>.

In either approach using ILogger or ILogger<T> the injected logger starts to write to a different category than the intended one. Meaning your base class will log in the derived logger category.

Upvotes: 3

tmaj
tmaj

Reputation: 34947

If you look at the declaration:

public interface ILogger<out TCategoryName> : ILogger

it gives you two options:

  1. use Ilogger because ILogger<T> is an ILogger,
  2. use ILogger<BaseClass> because ILogger<DerivedClass> is also an ILogger<BaseClass>.

Upvotes: 1

Majid Shahabfar
Majid Shahabfar

Reputation: 4829

Use HttpContext.RequestServices.GetService<ILogger> if you are using ASP.NET Core like bellow:

public abstract class BaseController<T> : Controller
{
    private ILogger<T> _loggerInstance;
        protected ILogger<T> _logger => _loggerInstance ??= HttpContext.RequestServices.GetService<ILogger<T>>();
}

and if you are using Razor pages:

class BasePageModel<T> : PageModel where T : class
{
    private ILogger<T> _loggerInstance;
        protected ILogger<T> _logger => _loggerInstance ??= HttpContext.RequestServices.GetService<ILogger<T>>();
}

Upvotes: 3

F_IVI_L
F_IVI_L

Reputation: 1010

use a none generic ILogger in your base class, but ILogger<DerivedClass> in your derived class. This way you can simply pass the ILogger to your base class if needed:

public abstract class BaseClassExample
{
    private readonly ILogger logger;

    public class BaseClassExample(ILogger logger)
    {
        this.logger = logger;
    }
}

and

public class DerivedClass : BaseClassExample
{
    private readonly ILogger<DerivedClass> logger;

    public class BaseClassExample(ILogger<DerivedClass> logger)
                  :base(logger)
    {
        this.logger = logger;
    }
}

this way not only you can use it easier if you somehow end up with two derived class you can use ILogger in both of them.

Upvotes: 44

Related Questions