Overriding a component registration in Castle Windsor? Which container supports it?
Asked Answered
U

4

6

this is partially a duplication of the same question which has not been yet answered. See here: How can I override a component registered in Castle Windsor?

Since I cannot comment or post any answers to an existing questions I created this question again in the hope that somebody knows the answer to a seemingly basic and simple question.

Keep in mind that:

  1. I do not want to create a new container.
  2. I do not care if containers should not be used for Unit testing.
  3. I do not want to use derived containers.

If Castle Windsor is not able to provide this simple functionality, what other container implementation would you recommend?

Ulpian answered 13/6, 2016 at 16:8 Comment(3)
I believe your problem arises from two of your "limitations" - #1 and #2. If you are indeed unit testing, then a container will just get in your way - you have one piece of code under test and your dependencies should all be mocked. If you are integration testing, then it may make sense to use a container. But in that case, you should use a different container than your application and mock anything that is not relevant for the test. Reusing a container is a bit like reusing a .config file for a different app - why would you?Phatic
@NightOwl888: This is to be used for Integration tests. New container does not make sense as e.g. I would like to tests everything apart from the log4net component. Or everything apart from DB access layer. If I create a new container I am not testing the real subject but something else which is not the point. Copy/paste of the container mapping code is also a not the best practice (seen so often).Ulpian
Possible duplicate of How can I override a component registered in Castle Windsor?Here
G
9

Windsor works with convention that "first registration wins". But, if you have not SPECIFICALLY told it that this component will be overridden, it will throw an exception. So, there are 2 ways to allow existing component to be be overridden:

  1. Register component with .IsDefault(). This will override existing registration.
  2. Register component with .IsFallback(). This will allow component to be overridden later.
  3. Using unique name for component - .Named("NewComponentName").

I personally prefer .IsDefault() and use this shorthand extension in my integration tests:

    public static class WindsorContainerExtensions
    {            
        public static void Override<TService>(this IWindsorContainer container, TService instance) where TService : class
        {
            container.Register(Component.For<TService>().Instance(instance).IsDefault());
        }
    }
Ghat answered 15/6, 2016 at 10:1 Comment(6)
IsFallback and IsDefault seems like a kind of a solution. Bad thing about it that you need to change the subject being tested, that is you need to register your components in a specific way so they can be tested later. Eventually I went with Ninject and its Bind/Unbind functionality. Windsor seems to be lacking basic functionality so I had to made this decision.Ulpian
You don't need to change subject being tested. .IsDefault() is used in unit tests, when you wan't to replace component.Ghat
Windsor most definitely does not use a "last registration wins" convention. This answer led me down a rabbit hole because I took it as correct :/ link "In Windsor first one wins: In Castle, the default implementation for a service is the first registered implementation. This is different from AutoFac for example, where the default is the last registered implementation"Tattletale
The above answer was incorrect as stated; there are only two ways to to allow the overriding of components: 1) use IsFallback for the original registration and IsDefault for the new one (which can no longer be overridden); or 2) use named components. A dependency without IsFallback cannot be overridden (not even by using IsDefault), and a dependency with IsFallback can only be overridden by using IsDefault.Pregnable
Okay, there is a third way: Register a named dependency, and set that to IsDefault().Pregnable
Please change '2 ways' back to to '3 ways'. (I have suggested an edit, then found that it wasn't correct, tried to cancel it by undoing all changes and just editing the formatting (Stack Overflow doesn't allow directly canceling a suggested edit), and forgot to undo the change of number.Pregnable
B
1

I have no knowledge about other containers but Caslte so my answer is about Castle. If you want to replace what you can do is write an extension method to the IWindsorContainer that will remove and then add.

But I think you should rethink a bit your design:

  1. Why does your class need direct access to the container and try to resolve from it by itself?
  2. Why are you in need to change your source code for test code? If writing clean Dependency Injection code according to SOLID your tests will really "magically" flow.

Can you please explain more about the design and about the relevant classes?

Bucaramanga answered 13/6, 2016 at 17:12 Comment(0)
U
0

Answering to a second part of the question - what containers support registration overriding.

Ninject.

See Bind()/Unbind() methods.

I tried also Autofac but seems that the registration becomes frozen after it being built. So seems that it may not be also possible with Autofac.

Ulpian answered 13/6, 2016 at 16:11 Comment(0)
F
0

Register your service like this, and then the other registration in your tests will overwrite it.

Container.Register(Component.For<ISomething>().ImplementedBy<RealSomething>().IsFallback());

Finbar answered 2/7, 2021 at 7:51 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.