R – Castle Windsor and auto-registration

castle-windsordependency-injectioninversion-of-control

I'm a Castle n00b, and am using the Fluent API in Castle Windsor to auto-register implementations to services based on a naming convention (ISomething is implemented by Something). One thing I wanted to support is for auto-registration to pick up dependencies that are in separate dlls, and auto-register those, as well.

So: IFoo is implemented by Foo (both in Foo.dll), and Foo depends on IBar, which is in Bar.dll.

I have the following code written to actually perform the auto-registration. It seems to work, but I'm wondering whether I did too much work myself. Is there a simpler way to accomplish the same thing?

private static void EnsureRegistered(Type t, IWindsorContainer container)
    {
        if (!container.Kernel.HasComponent(t) && (!(t.Assembly.FullName.StartsWith("System") || t.Assembly.FullName.StartsWith("mscorlib"))))
        {
            bool registeredAtLeastOneComponent = false;

            container.Register(AllTypes.Pick().
                   FromAssembly(t.Assembly).If(delegate(Type t2)
                       {
                           //auto-registers all types that implement an interface where
                           //the name of the interface is the same as the class, minus the 'I'
                           //ex: EchoService is auto-registered as handler of IEchoService
                           Type[] interfaces = t2.GetInterfaces();
                           bool shouldRegister = false;
                           foreach(Type interfaceType in interfaces)
                           {
                               shouldRegister = t2.Name.Equals(interfaceType.Name.Substring(1))
                                                     && (!container.Kernel.HasComponent(interfaceType));
                               registeredAtLeastOneComponent = registeredAtLeastOneComponent || shouldRegister;
                               if (shouldRegister)
                                   break;
                           }
                           return shouldRegister;

                       }).WithService.Select(delegate(Type type, Type baseType)
                         {
                             Type serviceType = null;
                             Type[] interfaces = type.GetInterfaces();
                             foreach(Type interfaceType in interfaces)
                             {
                                 if (!type.Name.Equals(interfaceType.Name.Substring(1))) continue;
                                 serviceType = interfaceType;
                                 break;
                             }

                             return new Type[] {serviceType};

                         }));

            if (!registeredAtLeastOneComponent)
                return;

            //for each of the items in the graph, if they have see if they have dependencies
            //in other assemblies that aren't registered, register those dependencies as well
            foreach(GraphNode registered in container.Kernel.GraphNodes)
            {
               if (!(registered is Castle.Core.ComponentModel))
                    continue;

               Castle.Core.ComponentModel registeredComponent = registered as Castle.Core.ComponentModel;

               foreach(ConstructorCandidate constructor in registeredComponent.Constructors)
               {
                   foreach(DependencyModel dep in constructor.Dependencies)
                   {
                       EnsureRegistered(dep.TargetType, container);
                   }
               }
            }
        }
    }

Best Answer

It really depends on how you structure and name your components... if you keep some simple conventions (i.e. all component services under the "MyApp.Services" namespaces and their implementations under "MyApp.Services.Impl") you could dramatically decrease this complexity (see for example ayende1, ayende2).

Also, IMO the usefulness of this is rather limited except for the most simple cases... for example some services you want to register without any service interface, some services need configuration, service overrides, etc. in which case this won't work.

Related Topic