Reputation: 942
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
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
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
Reputation: 34947
If you look at the declaration:
public interface ILogger<out TCategoryName> : ILogger
it gives you two options:
Ilogger
because ILogger<T>
is an ILogger
,ILogger<BaseClass>
because ILogger<DerivedClass>
is also an ILogger<BaseClass>
.Upvotes: 1
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
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