Search code examples
inversion-of-controlcastle-windsorioc-containercastle

How to overwrite a component with castle windsor?


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


Solution

  • 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();
        }
    }