3

I use S#arp Architecture which uses Windsor Castle for IoC. I got a new controller now that, unlike all other controllers in the project, need a different implementation of the same interfaces. I.e. all controllers use ProductsRepository: IProductsRepository as implementation, but the new one has to use SpecificProductsRepository.

How do I configure it to recognize and manage this automatically? Either in pure Windsor way, or with ASP.NET MVC help (e.g. in my custom controllers factory).

OK looks like I need subcontainers. Still searching.

queen3
  • 15,333
  • 8
  • 64
  • 119

2 Answers2

6

An easier and much simpler way would be to use Windsor's service overrides.

E.g. register your repos like so:

container.Register(Component.For<IProductsRepository>
                     .ImplementedBy<ProductsRepository>()
                     .Named("defaultProductsRepository"),
                   Component.For<IProductsRepository>
                     .ImplementedBy<SpecificProductsRepository>()
                     .Named("specificProductsRepository"));

which will ensure that the default implementation is ProductsRepository. Now, for your specific controller, add a service override like so:

container.Register(Component.For<NewController>()
     .ServiceOverrides(ServiceOverride
          .ForKey("productsRepository")
          .Eq("specificProductsRepository"));

You can read the docs here.

Edit: If you want to register your repositories with AllTypes, you can adjust the registration key e.g. like so:

container.Register(AllTypes.[how you used to].Configure(c => c.Named(GetKey(c)));

where GetKey e.g. could be something like:

public string GetKey(ComponentRegistration registration)
{
    return registration.Implementation.Name;
}
mookid8000
  • 18,258
  • 2
  • 39
  • 63
  • I register repositories using container.Register(AllTypes.Pick()...), how do I set key there? – queen3 Mar 15 '11 at 10:37
  • That is, I don't understand what value goes into ForKey. – queen3 Mar 15 '11 at 11:00
  • You can name the services with the `container.Register(...)` example I added to the answer... that's what you mean, right? – mookid8000 Mar 15 '11 at 11:19
  • 1
    Actually it doesn't have to be name. ServiceOverrides work via type as well when you don't name your components (which you shouldn't be most of the time anyway) – Krzysztof Kozmic Mar 15 '11 at 13:56
  • re: edit - I'd say `container.Register(AllTypes.[how you used to].Configure(c => c.ServiceOverrides(ServiceOverride.ForKey("productsRepository").Eq("specificProductsRepository")).Named(GetKey(c)));`, I think you forgot to mention you need to do service override as well. – Shagglez Sep 16 '11 at 12:47
  • Taken me hours to find a resolution for the same issue. I don't know why this isn't easier to find! Thanks @mookid8000 – dan richardson Jul 03 '13 at 14:52
0

OK, these days I tend to answer my own questions... so here it is for those who need it.

     // create subcontainer with specific implementation
     var mycontainer = new WindsorContainer();
     mycontainer.Register(AllTypes.Pick()
        .FromAssemblyNamed("My.Data")
        .WithService.FirstInterface()
        .Where(x => x.Namespace == "My.Data.Custom")
        .Configure(x => x.LifeStyle.Is(LifestyleType.PerWebRequest)));
     container.AddChildContainer(mycontainer);

     ControllerBuilder.Current.SetControllerFactory(new ExtendedControllerFactory(
        new Dictionary<string, IWindsorContainer> { {"", container}, {"Lm", mycontainer} }));

The controller factory chooses appropriate container based on name. The biggest challenge there is to call appropriate container's Release(controller) at the end of request, i.e. remember which container was used to instantiate controller. But this can be solved in several ways I suppose - remember in thread-specific (in HttpContext), remember in BaseController property, remember in internal dictionary, etc.

queen3
  • 15,333
  • 8
  • 64
  • 119