2
votes

I am trying to implement convention based registration with Castle Windsor, but I cannot figure out how to deal with swapping out default implementations of services.

In my framework, I might have an interface for a service and a default implementation:

public interface ILogger
{
     void Info(string message);
}

public class Logger : ILogger
{
     public void Info(string message)
     {
         Console.WriteLine(message);
     }
}

In the typical use case, I want Castle Windsor to register Logger with the ILogger service.

However, there are some use cases where the client code might provide an alternate implementation of ILogger:

public class CustomLogger : ILogger
{
    public void Info(string message)
    {
         Console.WriteLine("[LOG]: {0}", message);
    }
}

In this use case, I want Castle Windsor to register CustomLogger with the ILogger service instead of Logger.

So far, I've tried something like a tiered approach where I attempt to register all the default interfaces, followed by the non-default interfaces:

using (var container = new WindsorContainer())
{
    container.Register(
        Classes.FromAssemblyInDirectory(new AssemblyFilter(pluginDirectoryPath))
            .Where(type => true)
            .WithService.DefaultInterfaces(), 
        Classes.FromAssemblyInDirectory(new AssemblyFilter(pluginDirectoryPath))
            .Where(type => true)
            .WithService.AllInterfaces());

    var logger = container.Resolve<ILogger>();
    logger.Info("hello, world!");
}

However, this doesn't seem to work as both Logger and CustomLogger get registered to ILogger, but I still get back Logger when I resolve ILogger. Any tips for how to do this?

Also, I cannot register each component and service individually as the composition root is its own executable separate from the client code, so it has very limited knowledge of which services are available without doing some assembly scanning.

1
I've no idea how to do it with the 'auto scan' method but for the individual registration(s) this requires 1) setting IsFallback on the default component; or 2) setting a unique component name and using IsDefault for the overrides. See stackoverflow.com/questions/9253388/…user2864740
Ah yes, I had just seen that answer regarding IsFallback, but I couldn't figure out how to do the same thing with Classes.FromAssemblyInDirectoryuser1777663
In the absence of IsFallback and IsDefault, first registered will win. Couldn't you just register your non-default implementations first and then register your default implementations to fill the gaps?Phil Degenhardt
Aha! I think that's what I was doing wrong. I thought I had read somewhere that last registered wins. I modified the Where clause to look for non-default implementations based on my naming convention, then default implementations and that did the trick.user1777663

1 Answers

1
votes

First registered wins, so... just register your non-default implementations first and then register your default implementations to fill the gaps.