Search code examples
.netdependency-injectionunity-container

During constructor injection can the Unity DI Container recognize the type that is being constructed, and use it when resolving a reference?


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)?


Solution

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