1
votes

I want to inject a Microsoft.Extensions.Logging logger into the constructor of a class using the Unity DI container:

using Microsoft.Extensions.Logging;

namespace LoggingExample
{    
    public class MyClass
    {
        private readonly ILogger<MyClass> _logger;

        public MyClass(ILogger<MyClass> logger)
        {
            _logger = logger;
        }

        public void DoSomething()
        {
            _logger.LogInformation("Doing something.");
        }
    }
}

This Stackoverflow answer suggests it's a bad idea to inject ILogger<T> as it's too specific. Instead, the answer suggests injecting the non-generic base type, ILogger, which is simpler, easier to test and less error prone (from copy and paste errors mainly, I would guess).

In the class above we'd want to change the data type of the constructor parameter:

        public MyClass(ILogger logger)
        {
            _logger = logger;
        }

Then we'd need some way of resolving ILogger into Logger<MyClass> for this class, or Logger<T> in the more general case for any class, where T represents the class being injected into.

This is done in that Stackoverflow answer using the Simple Injector DI container and conditional registration:

container.RegisterConditional(
    typeof(ILogger),
    c => typeof(Logger<>).MakeGenericType(c.Consumer.ImplementationType),
    Lifestyle.Singleton,
    _ => true);

Is conditional registration possible in Unity? is there any other way Unity could recognize the class it's injecting ILogger into and resolve the reference to inject a Logger<T> object (where T is the class being injected into)?

1
You could just cast it back to Logger<MyClass> in your constructor. - howcheng
@howcheng: Unity will still be resolving ILogger to Logger<T> when it is injecting ILogger objects into the constructor, won't it? How will Unity know to use the appropriate type parameter when resolving ILogger if there are multiple classes ILogger is being injected into? For example, in one class it might have to inject a Logger<MyClass> object, in a second class a Logger<SecondClass> object. It looks like Simple Injector can do that. Can Unity? - Simon Tewsi

1 Answers

1
votes

You can define a name for each ILogger instance and use the [Dependency] attribute to specify which one you want. Example:

public static class Constants
{
    public const string LOGGER_MYCLASS_UNITY_KEY = "ILogger<MyClass>";
}

In your Unity registration:

IUnityContainer container = new UnityContainer();
container.RegisterType<ILogger, Logger<MyClass>>(Constants.LOGGER_MYCLASS_UNITY_KEY, 
  new ContainerControllerLifetimeManager());

In the constructor of the class that needs the logger:

public class MyClass
{
    private readonly ILogger<MyClass> _logger;

    public MyClass([Dependency(Constants.LOGGER_MYCLASS_UNITY_KEY)] ILogger logger)
    {
        _logger = (ILogger<MyClass>)logger;
    }
}

HTH.