Im working with .net core 2.2 and using dependency injection. I have some custom infrastructure dependencies that manage logging, tracing, etc. that I inject into classes. I'd like the dependencies to know what class they are in. I can do that if I instantiate the class like new Logger<Type>();
but is that possible to manage with dependency injection?
Ex:
public class Logger<Type> : ILogger
{
}
public class Foo
{
private Ilogger _logger;
public Foo(ILogger logger)
{
_logger = logger;
}
public void DoStuff()
{
_logger.Log(); //<= knows it's in the Foo class.
}
}
How do I inject ILogger into Foo, and have the Logger know what Type it was injected into?
You can specify the type like this
public class Foo
{
private Ilogger<Foo> _logger;
}
Then in the Logger, you can know the type of T by
public class Logger<T> : ILogger<T>
{
public Type WhatType() => typeof(T);
}
You will need to register the service like this
serviceCollection.AddSingleton(typeof(ILogger<>), typeof(Logger<>));
While more mature and feature rich DI Containers, such as Autofac and Simple Injector , do support context-based injection (which is the feature you are looking for), MS.DI does not support this feature.
It is for this very reason that all the Microsoft documentation describe the injection of a generic ILogger<T>
where T
equals the consuming type, as in:
public class C
{
public C(ILogger<C> logger) { ... }
}
Letting a consumer depend on the generic ILogger<T>
, instead of simply depending on a non-generic ILogger
, however, is more verbose and error prone. It makes you code harder to test and harder to maintain. This is likely the reason you are trying to inject a context-aware ILogger
instead, and I applaud you for that.
You can kinda 'hack' this feature into MS.DI by iterating through the ServiceCollection
, but there are practically too many limitations to it to be workable in your production application. For fun and entertainment, however, this is what you can try:
// Run just before finalizing the IServiceCollection
for (int i = 0; i < services.Count; i++)
{
ServiceDescriptor descriptor = services[i];
if (descriptor.ImplementationType != null)
{
var loggerParameters =
from ctor in descriptor.ImplementationType.GetConstructors()
from param in ctor.GetParameters()
where param.ParameterType == typeof(ILogger)
select param;
if (loggerParameters.Any())
{
// Replace registration
services[i] =
new ServiceDescriptor(
descriptor.ServiceType,
provider =>
ActivatorUtilities.CreateInstance(
provider,
descriptor.ImplementationType,
provider.GetRequiredService(
typeof(ILogger<>).MakeGenericType(
descriptor.ImplementationType))),
descriptor.Lifetime);
}
}
}
What this code does is:
ILogger
ActivatorUtilities
) while replacing the ILogger
dependency with an ILogger<T>
dependency.Limitations:
In practice, I would therefore not suggest using this approach but instead use a mature DI Container.
TIP: To get some inspiration, when using Simple Injector, such context-based registration can be made as follows:
container.RegisterConditional(
typeof(ILogger),
c => typeof(Logger<>).MakeGenericType(c.Consumer.ImplementationType),
Lifestyle.Singleton,
c => true);
When integrating with ASP.NET Core, Simple Injector even contains an extension method that simplifies this. Here 's the Simple Injector documentation on how to configure the container to inject ASP.NET Core's ILogger
, and the basic description on how to apply context-based injection in Simple Injector can be found here .
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.