views:

841

answers:

1

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);
                   }
               }
            }
        }
    }
+2  A: 

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.

Mauricio Scheffer
Thanks for the answer. What I'm doing is introducing a container into some legacy apps, and going for zero-friction when using the container (so long as they conform to the 'IBar is implemented by Bar' convention). And agreed re: complex scenarios. Trying to hit the 80% case here, though.
Craig Vermeer
In that case the code should serve its purpose very well!
Mauricio Scheffer
Thanks. Like I said, I was mainly looking to see if I was missing a simpler way. And as you pointed out, one way to make things simpler would be to simplify things around the container :-)
Craig Vermeer