views:

623

answers:

5

I want to redefine an (default) implementation in a given windsor-container. Is that what OverWrite is for? Doesn't work, though.

container.Register(
                    Component.For<IServiceOperationAuthorization>()
                            .OverWrite()
                            .Instance(_authorization)
                    );

Any other Ideas?

cheers, Lars

A: 

Yes, it does redefine default implementation of a service. Why do you do this? Why not register this in the first place, instead of redefining it?

Could you provide more context?

Krzysztof Koźmic
I have a xml-file that overrides the fluent wirings. But I solved it now by reading the xml file first and not adding the configurations from the code, when the xml-file redefines them.
Lars Corneliussen
This is better approach
Krzysztof Koźmic
@Lars: Can you be more specific how you do that? I have the same problem here, I have my core wirings and want to switch one implementation with another WITHOUT have to modify the core wirings.
Marcus
See the new answer...
Lars Corneliussen
+4  A: 

I agree with Krzysztof that this is usually not a good idea... However as far as I can tell OverWrite() does not overwrite the default component, it just overwrites the lifestyle defined by attribute (i.e. [Singleton]).

If you want to replace a component, you can use container.Kernel.RemoveComponent(string key) followed by the registration of the new component.

Here's an example where this does make sense.

Mauricio Scheffer
It should be noted that RemoveComponent does not always work
George Mauer
@George: can you elaborate on that? it should work as long as no other components depend on the one trying to be removed (which makes sense)
Mauricio Scheffer
Ah I see, but how then to specify an alternate implementation? I don't want to remove the whole graph and re-add it.
George Mauer
IMO it's the only reasonable way to do it...
Mauricio Scheffer
@Mauricio: the problem is that RemoveComponent fails even if _no_ components have been instantiated yet. Why enforce this rule during the _building_ of the container, while it is by definition a "work in progress"?
Igor Brejc
@Igor: RemoveComponent is usually a bad idea, to the point that it's under consideration to be obsoleted/removed. On a related note, Krzysztof is planning to clearly separate building from resolving in a future version of Windsor, so RemoveComponent may make more sense then.
Mauricio Scheffer
@Mauricio: good to hear about this. Another suggestion: how about adding ReplaceComponent() method or, even more simple: providing a method for changing the order of resolving components of the same service. So instead of calling RemoveComponent() + RegisterComponent, user could simply say something like RegisterComponentAsFirstImplementation()
Igor Brejc
@Igor: you can probably pull it off with an IHandlerSelector or custom naming subsystem, I'd argue against adding more public methods to the container due to bloat, however you may suggest it for v3! https://castle.uservoice.com/forums/38955-windsor-v3
Mauricio Scheffer
+3  A: 

This would be the kind of problem that might be better solved by setting the container up with a decorator, where you can explicitly change the implementation the decorator is directing calls to ... especially if you want to replace the implementation that existing components (i.e. singletons) have been injected with which might exist for the lifetime of your application.

Really need more background on what you're trying to achieve.


Here is more information about registering Decorators with Windsor.

Bittercoder
+2  A: 

I needed to switch a component implementation when running a system in a integration test suite and failed to use container.Kernel.RemoveComponent(). So I ended up with a simple facility which takes care of this for me.

Johan Andersson
unfortunately all overrides have to be specified before the registration is done
George Mauer
A: 

I actually found a nice solution where I combine xml-files for overrides and use fluent registrations for defaults.

The fluent-API takes the fullname of a impl as the default key. On the fly I override the id's of the xml-config to imitate the key-conventions of the fluent-API.

Then i register the xml-config while I listen to Kernel.ComponentRegistered.

Afterwards I only add services from the code-config where the xml hasn't yet defined the service.

(it's a while ago and I just copy-pasted the code. hopefully you get it working. I'll do edits if you find any problems)

IList<Type> unnamedServices = new List<Type>();
IDictionary<string, Type> namedServices = new Dictionary<string, Type>();

ComponentDataDelegate registered = captureRegistrations(unnamedServices, namedServices);

container.Kernel.ComponentRegistered += registered;

// The method that captures the services
private static ComponentDataDelegate captureRegistrations(
    IList<Type> unnamedServices, IDictionary<string, Type> namedServices)
{
        return (key, handler) =>
               {
                   if (handler.ComponentModel.Name == handler.ComponentModel.Implementation.FullName)
                   {
                       unnamedServices.Add(handler.Service);
                   }
                   else
                   {
                       namedServices.Add(key, handler.Service);
                   }
               };
}

After that before I register services in code, I check if they already have been registered. I also created a base class that makes this more easy. This is an application configuration:

public class ApplicationConfiguration : WindsorConfigurationSkeleton
{
    internal static WindsorServiceLocator create()
    {
        var container = createWith(null, "components-config.xml", coreServices, caches, roles);
        return new WindsorServiceLocator(container);
    }

    internal static IEnumerable<IRegistration> coreServices()
    {
        yield return Component.For<ISystemClock>()
            .ImplementedBy<PreciseSystemClock>()
            .Parameters(Parameter.ForKey("synchronizePeriodSeconds").Eq("10"))
            .LifeStyle.Singleton;

        yield return Component.For<IMailService>()
            .ImplementedBy<MailQueueService>()
            .LifeStyle.Singleton;
    }

    internal static IEnumerable<IRegistration> caches()
    {
        yield return Component.For<IDataCache<ServiceAttributes>>()
            .ImplementedBy<NoDataCache<ServiceAttributes>>()
            .LifeStyle.Singleton;

        // ....
    }
}

The base class that does the wiring: (Logging is from Commons.Logging)

public class WindsorConfigurationSkeleton
{
    private static readonly ILog _log = LogManager.GetLogger(
        typeof(WindsorConfigurationSkeleton));

    internal static IWindsorContainer createWith(
        IRegistration[] customs, string configFile, params Func<IEnumerable<IRegistration>>[] methods)
    {
        IWindsorContainer container = new WindsorContainer();
        BugFix.Kernel = container.Kernel;

        container.AddFacility("factory.support", new FactorySupportFacility());

        IList<Type> unnamedServices = new List<Type>();
        IDictionary<string, Type> namedServices = new Dictionary<string, Type>();

        ComponentDataDelegate registered = captureRegistrations(unnamedServices, namedServices);

        container.Kernel.ComponentRegistered += registered;

        if (customs != null)
        {
            container.Register(customs);
        }

        if (configFile != null)
        {
            tryAddXmlConfig(container, configFile);
        }

        container.Kernel.ComponentRegistered -= registered;

        if (methods != null && methods.Length > 0)
        {
            container.Register(union(unnamedServices, namedServices, methods));
        }

        return container;
    }

    private static ComponentDataDelegate captureRegistrations(
        IList<Type> unnamedServices, IDictionary<string, Type> namedServices)
    {
        return (key, handler) =>
               {
                   if (handler.ComponentModel.Name == handler.ComponentModel.Implementation.FullName)
                   {
                        var text = unnamedServices.Contains(handler.Service) ? "another" : "default";
                       _log.Info(
                           m => m(
                                    "Registered {2} service for {0} with {1}.",
                                    handler.Service.GetDisplayName(),
                                    handler.ComponentModel.Implementation.GetDisplayName(),
                                    text
                                    ));

                       unnamedServices.Add(handler.Service);
                   }
                   else
                   {
                        var text = namedServices.ContainsKey(key) ? "another" : "default";
                       _log.Info(
                           m => m(
                                    "Registered {3} service {0} with name '{1}' and {2}.",
                                    handler.ComponentModel.Service,
                                    handler.ComponentModel.Name,
                                    handler.ComponentModel.Implementation.GetDisplayName(),
                                    text
                                    ));
                       namedServices.Add(key, handler.Service);
                   }
               };
    }

    protected static void tryAddXmlConfig(IWindsorContainer container, string filename)
    {
        var fi = Resources.GetFileFromResourceHierarchy(typeof(ApplicationContext).Namespace, filename);
        if ( fi == null ) {
            return;
        }
        var configFile = fi.FullName;
        var xd = immitateFluentApiDefaultIdBehaviour(configFile);
        container.Install(Configuration.FromXml(new StaticContentResource(xd.OuterXml)));

    }

    private static XmlDocument immitateFluentApiDefaultIdBehaviour(string configFile)
    {
        var xd = new XmlDocument();
        xd.Load(configFile);

        foreach (
            XmlElement component in
                xd.SelectNodes("/configuration/components/component[@type and (not(@id) or @id = '')]"))
        {
            var type = Type.GetType(component.GetAttribute("type"), true);
            component.SetAttribute("id", type.FullName);
        }

        return xd;
    }

    private static IRegistration[] union(
        IList<Type> unnamed, IDictionary<string, Type> named, params Func<IEnumerable<IRegistration>>[] methods)
    {
        var all = new List<IRegistration>();
        foreach (var method in methods)
        {
            foreach (var registration in method())
            {
                var registrationType = registration.GetType();
                if (registrationType.IsGenericTypeOf(typeof(ComponentRegistration<>)))
                {
                    var componentType = registrationType.GetGenericArgumentsFor(typeof(ComponentRegistration<>))[0];

                    var name = (string)registrationType.GetProperty("Name").GetValue(registration, null);

                    if (name != null)
                    {
                        if (named.ContainsKey(name))
                        {
                            _log.Debug(
                                m => m("Skipped registering default named component {0}.", name));
                            continue;
                        }
                    }
                    else if (unnamed.Contains(componentType))
                    {
                        _log.Debug(
                            m => m("Skipped registering default component for type {0}.", componentType));
                        continue;
                    }

                    all.Add(registration);
                }
                else
                {
                    all.Add(registration);
                }
            }
        }

        return all.ToArray();
    }
}
Lars Corneliussen