Making frameworks container-aware
I’m currently knee-deep in NHibernate custom listeners, for the purpose of adding auditing to our application. Besides current documentation being plain wrong on the subject (I’ll update on the solution in the future), I hit a rather frustrating snag around the instantiation of my custom listener. One of the shouldn’t-be-frustrating-but-yet-it-is side-effects of committing fully to Dependency Injection and Inversion of Control Containers is all the code out there with “extension” points that don’t allow custom factory implementations. Sure, your framework allows for custom Floogle providers. But how does it create the IFloogleProvider implementations?
In the NHibernate code, I found this snippet that instantiates custom listeners, which are configured through an XML configuration file:
public void SetListeners(ListenerType type, string[] listenerClasses) { if (listenerClasses == null || listenerClasses.Length == 0) { ClearListeners(type); } else { var listeners = (object[]) Array.CreateInstance(eventListeners.GetListenerClassFor(type), listenerClasses.Length); for (int i = 0; i < listeners.Length; i++) { try { listeners[i] = Activator.CreateInstance(ReflectHelper.ClassForName(listenerClasses[i])); } catch (Exception e) { throw new MappingException( "Unable to instantiate specified event (" + type + ") listener class: " + listenerClasses[i], e); } } SetListeners(type, listeners); } }
One. glaring. problem. It uses Activator.CreateInstance to create the instance, which internally calls the default constructor. As a developer for custom listeners, this means that we need to define a no-argument constructor:
public class AuditPreInsertUpdateEventListener : IPreUpdateEventListener, IPreInsertEventListener { private readonly IUserSession _userSession; public AuditPreInsertUpdateEventListener(IUserSession userSession) { _userSession = userSession; } public AuditPreInsertUpdateEventListener() : this(ObjectFactory.GetInstance<IUserSession>()) { }
When doing constructor injection, you normally don’t create this constructor. Frameworks like ASP.NET MVC, WCF and others provide hooks for custom factories or instance providers. Containers receive a request to instantiate a component, and the container will create the dependencies, call the right constructor and pass those dependencies in.
From a developer’s perspective, it’s very helpful not to have to define any unnecessary no-arg constructors, as it muddies code. There is at least one solution out there, such as the Common Service Locator project. Now, NHibernate does allow programmatic configuration, so I do have the option to create the instances myself and pump them into NHibernate. All I’ve really done is eliminated the possibility of using XML configuration, which isn’t too fun if you’re doing things like XML manipulation as part of your deployments.
For framework developers, something like Activator.CreateInstance should be a red flag, that maybe we should provide alternate means of instantiation. As IoC containers become more mainstream, I think we’ll see more changes in major frameworks to support containers out-of-the-box. Until then, pointless dual constructors it is.