Castle Windsor - multiple implementation of an interface
Asked Answered
R

4

27

While registering components in Castle Windsor, how do we bind specific implementation of an interface to a component that has a dependency on that interface. I know in advance which implementation needs to be used by the component.

For example i created a sample console application based on code from several blogs and tutorials.

Following is the code.

public interface IReport
{
    void LogReport();
}

public interface ILogger
{
    string Log();
}

public class FileLogger : ILogger
{
    public string Log()
    {
        return "Logged data to a file";
    }
}

public class DatabaseLogger : ILogger
{
    public string Log()
    {
        return "Logged data to a database";
    }
}

public class McAfeeService : IReport
{
    private readonly ILogger _logger;

    public McAfeeService(ILogger logger)
    {
        this._logger = logger;
    }

    public void LogReport()
    {
        string getLogResult = this._logger.Log();

        Console.WriteLine("McAfee Scan has " + getLogResult);
    }        
}

public class NortonService : IReport
{
    private readonly ILogger _logger;

    public NortonService(ILogger logger)
    {
        this._logger = logger;
    }

    public void LogReport()
    {
        string getLogResult = this._logger.Log();

        Console.WriteLine("Norton Scan has " + getLogResult);
    }
}

class Program
{
    private static IWindsorContainer container;

    static void Main(string[] args)
    {
        // Register components
        container = new WindsorContainer();

        container.Register(Component.For<IReport>().ImplementedBy<NortonService>());
        container.Register(Component.For<ILogger>().ImplementedBy<FileLogger>());

        IReport service = container.Resolve<IReport>();
        service.LogReport();

        Console.ReadLine();
    }
}

I would like NortonService to always use a Filelogger and McAfeeService to use a Database Logger.

In the above program i am unable to bind NortonService to FileLogger.

How to do it?

Renown answered 2/8, 2013 at 3:57 Comment(0)
R
31

The above answers lead me to inline dependencies and the feature service override

Here is the registration code:

container.Register(Component.For<IReport>().ImplementedBy<NortonService>().Named("nortonService"));

container.Register(Component.For<ILogger>().ImplementedBy<FileLogger>());
container.Register(Component.For<ILogger>().ImplementedBy<DatabaseLogger>());

container.Register(
    Component.For<IReport>().ImplementedBy<McAfeeService>().Named("mcafeeService")
        .DependsOn(Dependency.OnComponent<ILogger, DatabaseLogger>())
);

IReport mcafeescan = container.Resolve<IReport>("mcafeeService");
mcafeescan.LogReport();

IReport nortonscan = container.Resolve<IReport>("nortonService");
nortonscan.LogReport();

Output:

McAfee Scan has Logged data to a database
Norton Scan has Logged data to a file
Renown answered 3/8, 2013 at 2:49 Comment(0)
H
10

I had a problem very like this, two implementation of one interface and two implementation of another interface. I wanted to force usage of particular implementations of those interfaces.

My class structure looked like this -

enter image description here

I looked at the naming convention, but didn't really like it. Instead I used the following -

    public void Install(IWindsorContainer container, IConfigurationStore store)
    {
    container.Register(
         Component.For<IMessageLoader>().ImplementedBy<MessageLoaderDatabase>()
        ,Component.For<IMessageLoader>().ImplementedBy<MessageLoaderFile>()

        ,Component.For<IMessageOfTheDayService>().ImplementedBy<MessageOfTheDayServiceDatabase>()
            .DependsOn(Dependency.OnComponent<IMessageLoader, MessageLoaderDatabase>())

        ,Component.For<IMessageOfTheDayService>().ImplementedBy<MessageOfTheDayServiceFile>()
            .DependsOn(Dependency.OnComponent<IMessageLoader, MessageLoaderFile>())

        ,Component.For<MessageOfTheDayController>().LifestyleTransient()
            .DependsOn(Dependency.OnComponent<IMessageOfTheDayService, MessageOfTheDayServiceFile>())
    );

Full info about this approach is here. In the source code provided with that post I show two other ways of achieving the same result.

Homans answered 21/9, 2014 at 3:12 Comment(0)
T
3

If you want to do it at runtime, This can be acheived through IHandlerSelector. Write a class that implements IHandlerSelector. It provides a method SelectHandler which will let you define the condition for binding conditionally at runtime. A Handler in this case is a component in Windsor that participates in instance construction. Refer here for more details.

Taegu answered 23/6, 2016 at 9:59 Comment(0)
C
2

My answer maybe not the best one, you can use naming method to resolve multi implementation:

 container.Register(Component.For(typeof(ILogger))
          .ImplementedBy(typeof(FileLogger))
          .Named("FileLoggerIoC")
          .LifestylePerWebRequest() ,
          Component.For(typeof(ILogger))
          .ImplementedBy(typeof(DatabaseLogger))
          .Named("DatabaseLoggerIoC")
          .LifestylePerWebRequest());

In your calling functions, you need to resolve it by name :-

var fileLog = container.Resolve("FileLoggerIoC", typeof(ILogger));
var DbLog = container.Resolve("DatabaseLoggerIoC", typeof(ILogger));

Mine method maybe not the best one as people don't like service locator to get the components, you can use this as temporary solution.

Calla answered 2/8, 2013 at 6:58 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.