Base Class type for ILogger<T> using Dependency Injection

Viewed 6848

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?

4 Answers

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.

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

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>.

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 ILoggger 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.

Related