Search code examples
c#wpfdesign-patternsdependency-injectioninversion-of-control

Dependency injection and many implementations of interface


I have a small problem with using dependency injection in my project. To describe problem I will use simple example. Let's assume that I'm writing logger aplication and my end user is able to choose if log should be saved to file or written to the console. User control it by choosing checkboxes in running app. He can select both or only one. After selecting he clicks button "LOG" to perform action. Now what I understand from my knowledge of DI I should create interfaces :

public interface ILogger
{ 
   void log();
}

And two implementations

public class ConsoleLogger : ILogger
{
    public void log()
    {
      ...
    }
}

public class FileLogger : ILogger
{
    public void log()
    {
      ...
    }
}

I know that I can register both implementations in for example unity container and get them in constructor by using table but then I can't identify which implementations is FileLogger and which is ConsoleLogger (In case when user select only one checkbox)

Second options is use service locator pattern to resolve implementations from ioc by name. I dont know if it is a good approach

In my real application I will have more options than two and there will be a lot of shared interfaces by each option.

Maybe better is use MEF ?

Application will be written in WPF + PRISM.


Solution

  • The way I usually do this is to make your class depend on an ILoggerFactory, which can create ILogger instances given a name.

    The factory implementation, LoggerFactory, would hold the container's kernel and be able to resolve the component by name.

    Notice how the factory interface only tells you that it can create objects - it doesn't give you any hint about any underlying kernel, or DI framework - the factory implementation is the one that knows about those details.

    Something like this:

    public class MyLoggingClass
    {
        private readonly ILoggerFactory _loggerFactorty;
    
        public MyLoggingClass(ILoggerFactory factory)
        {
            _loggerFactorty = factory;
    
            var fileLogger = _loggerFactorty.Create("fileLogger");
            var consoleLogger = _loggerFactorty.Create("consoleLogger");
        }
    }
    
    
    public class LoggerFactory : ILoggerFactory
    {
        public ILogger Create(string key)
        {
            return kernel.Resolve<ILogger>(key);
        }
    }
    

    Frameworks like Castle Windsor even give you these factories for free: you don't even have to write its implementation.