Advanced StructureMap: Diagnosing problems


So you’ve set up StructureMap, got all your registries in a row, go to run the application, and you’re greeted by a rather unfortunate message:

StructureMap.StructureMapException : StructureMap Exception Code:  202
No Default Instance defined for PluginFamily

That’s just one of the issues we can run into.  If you’re doing more advanced things like nested containers, lifecycle management, external plugin folders for dynamic loading, you’re bound to run into binding and other bugs at runtime.  One of the things I like about StructureMap over other containers I’ve tried is the level of support for diagnosis, configuration validation, and testing.

When using any configuration tool, whether it’s IoC container configuration, Web.config or other persistent mechanism for application configuration, you will run into problems.  The first step to diagnosing issues is to write tests.

Testing StructureMap configuration

The easiest way to test your StructureMap configuration is to use the ObjectFactory.AssertConfigurationIsValid() method in a test:

ObjectFactory.Initialize(init =>
{
    init.AddRegistry<HandlerRegistry>();
});

ObjectFactory.AssertConfigurationIsValid();

Internally, StructureMap will loop through all of your registered instances and try to instantiate them.  This can catch 99% of your registration problems, as you might have forgotten a registry somewhere or an instance isn’t defined for something.  The key to remember is that StructureMap can only validate plugin types it knows about.  Just like AutoMapper, it can’t divine all the ways you’re using ObjectFactory.GetInstance() in your codebase.  So typically, AssertConfigurationIsValid() is our first line of defense.

The next way to test is for specific registries.  Suppose we’ve defined some custom registry:

public class HandlerRegistry : Registry
    {
        public HandlerRegistry()
        {
            Scan(cfg =>
            {
                // etc

If we have some custom registration logic (very likely), then we can then test that registry in isolation from the rest of our configuration.  This is what I do when I’m doing lots of meta-programming to hook up open generic types of things, and in general building highly de-coupled, SOLID stuff 😉

Side note: things like ConnectImplementationsToTypesClosing() should be built in to every IoC container registration.  It’s one of the things I can point to on our current project and say, “that enabled some of our success”.  If we had to register/build these things manually, we wouldn’t have tried to build the architecture we wound up doing, which let us scale our development very easily.

To test a single registry in isolation, you simply need a Container object:

[Test]
public void Should_connect_delete_handler_by_registry()
{
    var container = new Container(new HandlerRegistry());

    var handler = container.GetInstance<IHandler<DeleteEntityCommand<Customer>>>();

    handler.ShouldBeInstanceOf<DeleteEntityCommandHandler<Customer>>();
}

I instantiate a new Container object, but I pass in the instance of my custom registry.  From there, I can then use the Container directly to get an instance.  With registry-specific tests, I can ensure some of my more complex (and insanely powerful) registration is hooked up correctly.  If you’re using a container beyond just hooking up Foo to IFoo, I highly recommend doing registry-specific testing.  It’s much easier to test-drive a registry in isolation than it is to try and catch things at runtime.

Popping open the hood

Sometimes, you’ll get a “No default instance defined” exception, and in those cases, it’s best to just pop open the hood of StructureMap, and see what do I have?

ObjectFactory.Initialize(init =>
{
    init.AddRegistry<HandlerRegistry>();
});

Debug.WriteLine(ObjectFactory.WhatDoIHave());

Wow, there’s a method, “WhatDoIHave()”.  It’s on both ObjectFactory, and Container (ObjectFactory is merely a wrapper around a Container object).  This method returns a nicely formatting string showing everything in our container:

=======================================================================================
PluginType                                                                             
---------------------------------------------------------------------------------------
IHandler`1<DeleteEntityCommand`1<TEntity>> (IHandler`1<DeleteEntityCommand`1<TEntity>>)
Scoped as:  String

Well, there’s a lot more information here, but it basically lists out each registered plugin type and configured instances.  If you see a blank in the configured instances, that’s something that is registered, but doesn’t have a concrete instance.

Lifecycle issues

One of the major features of modern IoC containers is the ability to provide lifecycle management.  This means that we can use our container to control the lifetime of our instance, singleton, per-request, HttpContext, etc. etc.  That’s an extremely powerful tool, but one that cuts very, very deep if it’s not hooked up right.  Unfortunately, it’s also not one that’s easy or even possible to unit test in some cases.  Lifecycle configuration in your container is application behavior, and like things such as web.config, config stored in DB, external XML configuration etc. can only really be verified with the whole app up and going.

Before understanding lifecycle issues, it’s critical that you fully understand the lifecycle of the code calling ObjectFactory/Container.GetInstance().  Whenever I’ve had problems with lifecycle, it’s because I just misunderstood, didn’t understand, or was completely ignorant of the thread lifecycle of whatever was trying to build my lifecycle-configured instance.  My WCF understanding was wrong, and my hand-coded lifecycle management totally screwed up things that were found only after it went to production.  It’s really no different than if you wanted to start storing things in a static cache, but now need to deal with multiple threads, who gets what instance, and so on.

So back to debugging lifecycle issues.  In practice, when things don’t work, I do a healthy dose of logging + GetHashCode() to put out messages showing exactly what gets what instance.  About a year ago, I had to debug a gnarly lifecycle problem.  One thing I found is that it gets much, much harder if you’re instantiating things yourself and get away from DI however, as some old code we had put too many moving parts into play, making it that much harder to understand what came from where.  So my preference is to push as much instantiation into the container as you can.  That said, it’s not for everyone, so you’ll have to decide how much magic juju you’re comfortable with.

That aside, here’s an example of what I like to do:

public class DeleteCustomerCommandHandler : IHandler<DeleteCustomerCommand>
{
    private readonly ICustomerRepository _customerRepository;

    public DeleteCustomerCommandHandler(ICustomerRepository customerRepository)
    {
        Debug.WriteLine("Received instance of ICustomerRepository in DeleteCustomerCommandHandler: " 
            + customerRepository.GetHashCode());
        _customerRepository = customerRepository;
    }

It’s a primitive tool, but I use it to both build and debug lifecycle issues.  It could be Debug, or it could be any logger or whatever to be able to see basically the instance ID from GetHashCode().  I would see log messages of mismatched hash codes, things being instantiated more than I thought they should, and so on.  Whenever I had lifecycle issues, it was because the instantiator had one lifecycle, and the plugin type had another conflicting lifecycle configuration.

Now, this is just my way of debugging lifecycle issues.  If there’s some other ways of doing so, I’d love to hear about it.  I’m pretty sure this could be done also with AOP/profiling tools too.

Wrapping it up

IoC containers are magic juju boxes.  But they don’t have to be, if you have the right tools to test and diagnose problems.  If you do wind up drinking the container kool-aid, be prepared to have something go wrong at some time.  Like other sharp tools, it can really destroy a lot of time if you don’t have a good plan for diagnosing these issues.  And if nothing works, you can always go to the mailing list of your respected project (as long as your project is active and OSS for the most part) for some free help.

My picture of an MVC-WebForms marriage