Dependency Injection (DI) & Registration Madness

I recently saw an application that has many services being registered with a DI container as below.

container.RegisterType<ICustomerSearchService, CustomerSearchService>();
container.RegisterType(typeof(IWriteRepository<>), typeof(WriteRepository<>));
container.RegisterType(typeof(IReadRepositories), typeof(ReadRepositories));
container.RegisterType(typeof(ICustomerSearchRepository),
typeof(CustomerSearchRepository));

This is just a few but there are 100s of registrations and it is continue to grow…

registration_page

One of the developers has taken a new approach to split registration across multiple classes. But again there are still loads of type registrations.

This is the registration madness.

This is where the Convention Based Auto Registrations comes into the picture. Basically the team agree on a naming convention and use that convention to auto-wire the registrations as below.

public static void AutoWire(IUnityContainer container)
{
     var assemblies = AppDomain.CurrentDomain.GetAssemblies()
         .Where(s =>
     s.FullName.StartsWith("YouAssemblyNameStartingWith"))
     .ToList();

     var types = from assembly in assemblies
                 from exptype in assembly.ExportedTypes
                 where exptype.Name.StartsWith("TypeNameStartWith")
                 select exptype;

     container.RegisterTypes(types, t => t.GetInterfaces(),
               WithName.Default);
}

There are 2 conventions here – The assembly name and the type that need to be registered.
Both must start with an agreed naming convention. Once the convention is agreed and well defined, the container is almost invisible and importantly registration code is much more maintainable.

The above example uses Microsoft Unity Container. However similar techniques can be applied for other DI libraries.
Note that RegisterTypes only available in Unity version 3 and higher.

Let’s look at some practicality of the convention based approach
While it provides a great way to reduce configuration verbosity, it might not fit for all purposes. There might be situations where that we might still need a non-convention based approaches such as programmatic registrations or even configuration based registrations (in rare cases to support late binding). There is no rule to say that we can only use one approach. I think it is best to employ, first convention based, then programmatic registrations if required.

What if someone forget to use the naming convention?
It would result in a dependency resolution exception. Let’s assume there is no code reviews, and someone in the team had no idea about the convention based registration. Then included a non-convention based/explicit registrations as below..

container.RegisterType<TFrom, TTo>();

Again this is just a start of the same problem we saw before and we wanted to avoid it in the first place.

So how can we avoid it?
I thought of it would be nice to create a custom extension method on the container to verify the conventions are registered accordingly.

container.VerifyRegistrationConventions();

This extension method will look for the types that are already registered and check for the convention that we expect within each registered type.

public static class UnityExt
{
      public static void VerifyRegistrationConventions
                         (this IUnityContainer continer)
      {
           var registrations = continer.Registrations
              .Where(x => !x.MappedToType.Name.StartsWith("Reg") &
              !x.MappedToType.Name
              .StartsWith("IUnityContainer")).ToList();

           if (registrations.Any())
           {
               var builder = new StringBuilder();
               builder.AppendLine("Registrations Convention Mismatch : ");
               foreach (var registration in registrations)
               {
                   builder.AppendLine(registration.MappedToType.Name);
               }
               builder.AppendLine("Registrations with the container
                         expects types name to be start with 'App': ");

               throw new ContainerConventionViolationException
                        (builder.ToString());
           }
      }
}

This extension also assumes all registrations follow convention based registration.

The thrown exception message will contain the types that are not adhere to the specified naming convention.

Where we invoke this extension?
There are 2 possible places..
a. Last possible place that your application uses the DI container for object composition. In other words this extension can be used just after the last possible execution routine within the composition root.
b. First possible place just after the execution exited the composition root. This place may not be obvious as ‘a’, therefor I prefer ‘a’ over ‘b’

In both cases, the developer had every opportunity to construct the object graph and the above extension method would verify the conventions.

If we are making the DI container accessible outside the composition root, then we cannot guarantee that all types are composed within the Composition Root. In that case, we cannot utilise this extension reliably and importantly we are not using the DI container correctly.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s