How to register dependencies in Castle Windsor that work the same as MEF's [Export]

31 Views Asked by At

I have a number of dependencies that are created based on the values of certain types in my container, and I'm unsure of the best approach to register these dependencies in Windsor. In MEF, I would mark a property or method as an [Export], and then simply [Import] everything that provided that export.

For example:


// the same set of IKnownPositions are shared between various components
interface IKnownPosition { } 

// Lots of types share the same IKnownPosition implementations, and we want
// the container to automatically figure out that when they are resolved,
// any registrations for IKnownPositions need to be completed first
class KnownPositionConsumer
{
   // inject the positions in the constructor
   public KnownPositionConsumer(IKnownPosition[] positions) {}
}

// just a POCO registered by convention (and may not exist)
// and doesn't know anything about IKnownPosition
interface ILayout { IPosition[] Positions { get; } }

// we have a type that knows how to turn the POCO into something useful,
// but don't know how to register the IKnownPosition instances as instances
class LayoutToKnownPositions { IKnownPosition[] KnownPositions { get; } }

I have a set of IKnownPosition based type instances that are used throughout my application (shared state). I have an ILayout registered by convention that exposes a list of IPosition POCOs, and there's a separate component in a different assembly that knows how to make IKnownPosition's if an ILayout exists. What I don't know is how to tell Windsor to expect and resolve these things that may add additional IKnowPositions to the container.

So, the question is, how do I do delayed registration using Castle Windsor? In MEF, I'd use [Export]. I've also used an IProvider interface where each implementer provides a list of items and a singleton IManager to collects all those IProviders and aggregates their items together. Is this the only way to do what I need in Windsor?

public class KnownPositionManager : IKnownPositionManager
{
    public IKnownPosition[] AllKnownPositions { get; }

    public KnownPositionManager(IKnownPositionProvider[] providers)
        => AllKnownPositions = providers.SelectMany(p => p.KnownPositions);
}  

// registered in an IWindsorInstaller like:
container.Register(Component.For<IKnownPositionManager>
                            .ImplementedBy<KnownPositionManager>();

// elsewhere register a type to convert ILayout.Positions to IKnownPositions
container.Register(Component.For<IKnownPositionProvider>
                            .ImplementedBy<LayoutToKnownPosition>());

There's some resistance to the boiler-plate code of IProvider/Manager approach, since everyone has to deal with a manager instead of just the instances they care about. Plus the hassle in unit testing from having to mock the manager types. I might be able to mitigate some of that using generics (IProvider<IKnownPosition> and IManager<IKnownPosition>) but I'm not really sure how that would work in Windsor.

For background, I'm working in a code base where the problem has been solved in in three different ways:

  1. Registering the IWindsorContainer in itself and exposing a public static reference to the container.
  2. Forcing installers to run in a specific order so that components can be resolved during later IWindsorInstaller.Install() calls:
public void Install(IWindsorContainer container,...)
    => foreach (p in container.Resolve<ILayout>().Positions)
           container.Register(Component.For<IKnownPosition>()
                                       .Instance(new KnownPosition(p))
                                       .Named("somethingUnique"));
  1. Using interfaces that the bootstrapper resolves as parts of the application are resolved. This has to be done after Install, and then again after certain UI components have been instantiated, potentially ad-nauseum.
// register a type/delegate that accepts the container & adds registrations
container.Register(Component.For<IRegisterTypes>
                            .ImplementedBy<LayoutToKnownPosition>());

// in the bootstrapper, get all the registrars and execute them
container.Resolve<List<IRegisterTypes>>()
         .ForEach(p => p.RegisterStuff(container))

// do the same later on when the application is resolved
var myApp = container.Resolve<MyApp>();
container.Resolve<List<IRegisterAppDependency>>
         .ForEach(r => r.RegisterInApp(container, myApp))

How can I tell Windsor that whenever it goes to resolve a particular type, that there may be some registrations that need to be performed first? I'm assuming that some combination of the abstract factories and component selectors can make this work, but I'm at a loss.

0

There are 0 best solutions below