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)
        var listeners = (object[]) Array.CreateInstance(eventListeners.GetListenerClassFor(type), listenerClasses.Length);
        for (int i = 0; i < listeners.Length; i++)
                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.

About Jimmy Bogard

I'm a technical architect with Headspring in Austin, TX. I focus on DDD, distributed systems, and any other acronym-centric design/architecture/methodology. I created AutoMapper and am a co-author of the ASP.NET MVC in Action books.
This entry was posted in C#. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • We’ve tried to avoid listeners for object creation because things can get complicated quickly – as you have discovered.

    NHibernate isn’t the most IoC friendly framework. What we ended up doing was creating a private field in our repositories that looked like this:

    private Action initialize;

    This private field served as a callback that populated our object instance – typically through setter injection (ugh!)

    We used setter injection rather than constructor injection for several reasons, but the most important was to allow NHibernate to create a proxy object for us so that we gained the benefits of deferred query execution.

    Here is our “load” method on our repository:

    public virtual T FindById(int id)
    var loaded = (T)this.session.Load(this.type, id);
    return loaded;

    In this instance “T” would be something like IContact, IAddress, IUser, etc. whereas “this.type” would be a concrete implementation for the respective interface.

    We are actually performing dependency injection on our repositories to inject the session, type, and initialize objects.

    In this manner we make NHibernate a bit more IoC friendly.

  • @bogardj
    use this method
    public void SetListeners(ListenerType type, object[] listeners)
    if you want initialize NH with instance of your listeners