It’s time for IoC Container Detente

I was perusing the session list for ALT.NET Canada this morning (sorry I couldn’t make it guys) and saw this session title that reflects something I’ve had quite a few conversations about this year.

Building extensible frameworks leveraging framework consumer selectable IoC containers.

As long as we’re going to work in static typed languages, it’s a major advantage to achieve extensibility and pluggability by utilizing an existing Inversion of Control container.  I’ll make the statement that starting with an existing IoC tool makes framework construction far simpler and cheaper than it was before.  The problem with that is that there are multiple IoC containers out there, and tying your framework to any of those containers is bound to irritate a big swath of people. 

When we were deciding between Ruby on Rails, the ASP.Net MVC, and MonoRail for our project architecture in June, I dismissed MonoRail in small part because I didn’t want to be forced to use Windsor as our IoC tool (for some reason). We’re perfectly content with the MVC framework at the moment*, but after reading through the MVC code and the way that they pass around dependencies through the myriad SomethingContext objects, I think the MVC team could streamline their own code quite a bit by using an IoC tool to resolve dependencies.  Now, that brings up a contentious issue, which container would they use?  The answer is that they can’t use any specific container.

When I was building the initial code that has became Fluent NHibernate there were a couple places where I needed to use an IoC container to resolve NHibernate objects and deal with configuration.  I obviously chose StructureMap and buried a couple hard references to ObjectFactory.GetInstance<T>() in the code.  The coupling to StructureMap doesn’t particularly concern me, but when the code first went public, Nate Kohari (author of Ninject) lamented on Twitter that the tool was coupled to StructureMap.

So, to make it safe for both Nate and myself to use the IoC of our choice, what I’d really like to see is a common interface to represent the very baseline functionality of an IoC container so that we could build and release frameworks that utilize an IoC container without forcing a particular choice of tool onto the development teams. A couple people are already going down the path of abstracting an IoC container with one off solutions.  Off the top of my head I can think of at least three:

  • MVCContrib
  • Caliburn
  • Whatever boring name that Prism is now called (and I owe P&P a reference implementation of Prism with StructureMap.  I’m getting there)

All of these frameworks have a more or less similar interface for the container, then an adapter for most of the major IoC tools.  I’d like to propose, and I’m not the first, that a new Service Locator abstraction be added to the .Net Framework itself as a first class citizen and publicized a little bit so framework builders can take advantage of a common interface.  I’d like to start a new petition to make this little, itty bitty work be a part of the MEF project within Microsoft.  All I think it needs to do is provide an interface with:

  1. GetInstance<T>()
  2. GetInstance<T>(name)
  3. GetAllInstances<T>()

and leave the registration completely alone because it’s so radically different from one tool to another.  Then, give us something like Thread.CurrentPrincipal for a wellknown place to put the container.

*Honestly, we’ve replaced a fair amount of the MVC framework with our own stuff, but that’s a post for another day.  The best thing about the MVC is that it’s so small and fairly modular, so it’s relatively easy to swap in your own “special sauce” to embed conventions and more opinionated structures.

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Jeff,

    David Mohundro is doing one, but I don’t know where it’s at. I might do something January-ish for my own purposes that I will release.

    I betcha that my recommendation is going to end up being to bypass the Prism bootstrapping a bit though.

  • http://www.agilification.com Jeff Doolittle

    You said: “I owe P&P a reference implementation of Prism with StructureMap. I’m getting there”

    Jeremy, I need to either get what you have, or build this myself. I’d much rather get what you have! Any idea when this may be completed?

  • http://plaureano.blogspot.com Philip Laureano

    Hi Jeremy, I couldn’t agree more myself! I’m currently in the process of rewriting LinFu’s own IoC container, so this is of a huge interest to me as well.

    FWIW, I think the basic service locator interface should support the basic functions:

    -Type Registration

    -Determining whether or not the locator can instantiate the service type

    -A GetService method of some sort that can instantiate the particular service type in question

    I think the lowest common denominator approach would be best since no two IoC frameworks are exactly alike, and from the end user standpoint, a common service locator interface would make it easy for people to try different IoC frameworks without having to do a massive rewrite of their code.

    It is by no means perfect, of course, but I’ll certainly agree to implementing a common interface once all the other IoC container devs agree to do the same thing.

  • http://wekempf.spaces.live.com wekempf

    I don’t believe the interface should use generics AT ALL. As pointed out, there’s times you must supply the type at runtime, not compile time. So you must have versions that take a Type parameter. So why not have both? Because it’s not a very DRY solution. Far better to leave the generic versions out of the interface and provide extension methods for this. I’d also consider using IServiceProvider here.

    public interface INamedServiceProvider : IServiceProvider
    {
    GetService(Type type, string name);
    GetAllIServices(string name);
    }

    public static class ServiceProviderExtensions
    {
    public static T GetService(this IServiceProvider self)
    {
    return (T)self.GetService(typeof(T));
    }
    // not showing all extensions for brevity
    }

  • http://www.codebetter.com/blogs/glenn.block/ Glenn Block

    I forget to say, i think this is a great idea! From a naming perspective I like IServiceLocator in that there is nothing particularly DIish about the suggested interface. The main thing is providing a clean pluggable mechanism for accessing services. If that mechanism happens to be a DI container great, but as a user, why do I care.

  • http://www.codebetter.com/blogs/glenn.block/ Glenn Block

    No petitions! Just to update the world, we’re listening and talking about options.

  • Tim Scott

    It might help your cause that the author of Windsor has joined the MEF team. http://hammett.castleproject.org/?p=312

  • Daniel Fernandes

    Jeremy D: My mistake, I misunderstood your post which, for a good reason, doesn’t mention registration.
    Having had nasty surprises with the multiple overloads of component registration with CastleWindsor I was here hoping for something better.
    Next time I will use StructureMap I swear.

  • Jeremy Gray

    @Jeremy & Sebastien – In my specific case, I expect we’ll be imposing convention scanner requirements instead of required registration mechanism support but both options are certainly be appropriate in different situations.

    This is why I’m tempted to agree that the absolute lowest common denominator is on the instance retrieval side of things and would not include any particular style or set of registration support requirements. This would still leave the possibility of each framework implementer layering on an additional set of requirements around registration (and/or convention scanner) functionality depending on the needs and style of the framework (and consuming applications) in question.

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    Sebastien,

    I’d make the registration abstraction be something coarse grained like:

    IBootstrapper.Bootstrap() and let users use the native registration.

    In your solution you’re barely scratching the surface of what the tools provide for registration capabilities. I personally wouldn’t be willing to accept that as a consumer of your framework.

    I’m not that wild about the “some configuration over here in form A” and some over there in form B.

  • http://serialseb.blogspot.com Sebastien Lambla

    Jeremy,

    how do you handle the case where your framework requires registration of components for its internal use?

    I automatically enroll all the default components of OpenRasta when the framework is loaded, so that whatever IoC is in use they’ll receive a registration for those components to resolve them when needed. The behavior is transparent to the user but lets them override this at any time.

    I’ve found that for my needs, covering registration was done through a couple of methods that were indeed the lowest common denominator of all containers, but that is only used for my framework. Users are still encouraged to do their registrations using whatever container-specific API they are used to.

    Is that so different from the resolve mechanism for framework writers?

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Nate & @Haacked,

    StructureMap wouldn’t allow that either (always have to know the service type upfront), but you can do that very thing with Spring.Net. I always thought it was a design flaw, but hey, whatever floats your boat.

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Daniel,

    I couldn’t disagree more, and I argued a long time with the Prism guys about doing just that.

    Going with a least common denominator approach for service location is low hanging fruit and would provide enough swappability for the framework things we mentioned above. After configuration time the only things you generally use are the 2-3 “give me this” methods.

    Coming up with a least common denominator approach for registering services effectively castrates all of the IoC containers, because this is where all of them have individual capabilities that set them apart.

  • Daniel Fernandes

    Rather than agreeing on a common base interface wouldn’t be better instead of agreeing on a common fluent IoC API containing enough extensibiliy points so that we can cater for all situations ?

    WhenResolvingType().ResolveWith().AsSingleton();
    WhenResolvingType().ResolveWith().WhenCallingAssemblyIs(assembly)..
    etc etc

    I have probably absolutely no idea as to what I am talking about (honest) but I don’t think Jeremy’s approach to agree on a very basic IoC interface will allow us to go anywhere (no offense!).

  • http://kohari.org/ Nate Kohari

    Phil:

    I understand what you’re talking about re: the DLR and GetInstance(string), but I don’t know if it has an actual implementation in any of the popular DI frameworks. I know for certain that Ninject wouldn’t know the first thing about how to resolve that, since it doesn’t use string keys in the first place. :)

    I’m willing to add functionality to Ninject to support this abstraction layer, but I think we should just focus on making it the lowest common denominator. Then again, if Ninject is the only framework that doesn’t support it, I’ll bring it up to par. :)

  • Greg M

    “As long as we’re going to work in static typed languages, it’s a major advantage to achieve extensibility and pluggability by utilizing an existing Inversion of Control container. ”

    Is it really static-typed languages that demand containers for IOC? Isn’t it just languages without first-class/higher-order functions?

  • Jeremy Gray

    @Haacked – I hear ya. Exposing both flavors is completely sensible.

  • http://haacked.com/ Haacked

    @Jeremy in a dynamic scenario, I might duck-type the returned object to a type I do know about. I can’t statically pass the type I know about because the object I want doesn’t implement it.

    Or, I might have information about the type I want from somewhere else and want to call methods on it dynamically (for example, if I pass the object to an IronRuby layer and call it from there.

  • http://aussiebloke.blogspot.com Stuart Carnie

    I think it’s great that we standardise a basic interface for an IoC container; however I do have a couple of points.

    1. I second the need for the following non-generic equivalents:

    object GetInstance(Type t)
    object GetInstance(Type t, string name)
    object GetAllInstances(Type t)

    2. What are the semantics for GetAllInstances()? Does it return all components registered as ‘T’ or all components that are a subclass of ‘T” or implement ‘T’ (if T is an interface)? I think it needs to be well-defined, or it may not make it that easy to plug in different containers.

    3. I think it is worth considering exposing a minimum set of Register* methods. Example:

    I’m writing a simple command-line utility that uses the IoC container interface, and based on certain command-line options, allows me to configure which implementation of the INotificationService is used. I want to programatically register either EmailNotification or ConsoleNotification with the container depending on the option specified. I also don’t want to have a separate / external configuration file with my utility.

    4. Attributes… I know this is a point of contention, and many will argue they do not want to litter their objects with [Dependency], etc; however, to standardize on a few might be worth considering. They allow the developer to declare their intent in the same code as the class, making it easier to understand the external dependencies from the perspective of a 3rd party.

  • Jeremy Gray

    @Chad again – to be clear, I am in general agreement with the rest of your comment. :)

  • Jeremy Gray

    @Chad – I’m afraid I have to disagree.

    First of all, at the very least one needs a way to bootstrap their way into the application and some kind of Service Locator is almost surely going to find its way in there at some point. Also, you’ll need Service Locator for those times where you need need a clean way to select, at runtime, from multiple available implementations of a service interface. Finally, there will always be times that you want access to something without the overhead of injecting it up front (e.g. cases where instantiated object count is higher but where a particular service or set of services are used through those objects less frequently.)

    While I definitely think it is great to minimize the number of points in your application where you work with a Service Locator, there are too many scenarios where it is needed to be able to do away with it entirely.

  • http://udidahan.weblogs.us Udi Dahan: The Software Simplist

    I know that nServiceBus has an abstract over a container (IBuilder) and then an adapter to Spring and Windsor.

    I think that in the Rhino Repository there is something similar.

    I’d suggest that, similar to Common.Logging we all just pick one and go with it. I mean, it’s one project with one interface in it.

    And to Chad, opening up new windows in a smart client does require service locator.

  • http://chadmyers.lostechies.com Chad Myers

    @Jeremy M, Jeremy G, et al:

    I would push strongly for the whole “if you follow DI seriously, you won’t need service locator stuff”

    At least in Fluent NHibernate, the ObjectFactory.GetInstance<> calls were all unnecessary and served only to keep some of the c’tors cleaner for speedier development and use (primarily for testing utility stuff where DIP may not have been as important).

    As soon as we inverted all the dependencies, pulling StructureMap out was a breeze.

    Likewise, for ASP.NET MVC, all the *Context classes can be done away with easily or with minor work.

    Locating the controller would be tricky without service location, but I think I’d rather just have an IControllerLocator implementation registered in the cloud rather than the current mechanism of subclassing and overriding methods.

    I’d rather ASP.NET MVC say: “You must have implementations of the following 10 interfaces registered in the container cloud in order for dependency injection to work”. By default, ASP.NET might use Unity, or even a static container of some sort that has everything hard coded.

    MVCContrib could ship with a standard StructrueMap Registry files, Windsor Facilities (or whatever the equivalent is) and whatever the Ninject and Unity equivalents are.

    These Registries/Facilities/etc would be pre-configured with all the required ASP.NET MVC dependencies.

    If @Haacked/Gu/MSFT were serious about this and interested, I’d be willing to do a spike test on this to prove it but I won’t waste my time if it’s not in the cards for MSFT to consider.

  • Jeremy Gray

    @Haacked – We do need a get that is by some kind of name key, and that one is already the list I posted as “Service Locator, with support for resolution of both default and named instances…”

    As for passing the type as an argument, I’m not entirely against your suggestion but do really wonder how it is you would expect to interact with the located service if you didn’t have at least know some type information available. I tend to work entirely in static typing territory, however, so I would certainly understand that the requirements over in dynamic territory might be more than a bit different. ;)

  • http://haacked.com/ Haacked

    Shouldn’t at least one of those signatures be
    object GetInstance(Type t, string name);

    There are many situations when I don’t know the type at compile time. Also, we might need…

    object GetInstance(string name)?

    For instance, when doing interop with a DLR language, I might not have a type *at all* that I can pass.

  • Jeremy Gray

    * As for addressing the core subject during the session, the lowest common denominator (at least, the one that this framework needs) is captured in the notes (I think ;) but summarized thusly:
    - Service Locator, with support for resolution of both default and named instances of a given type
    - ctor DI with auto-wiring
    - a convention-based scanner, either built in or created while integrating the container with the application framework, that supports the conventions we will define. (It will be a basic pattern along the lines of “If IFoo refines IComponent, a discovered class Foo that implements IFoo is registered as the default implementation of IFoo, where any IFoo-implementing XFoo, YFoo, etc. are registered as named IFoo implementations called “X”, “Y”, etc. respectively.

  • Jeremy Gray

    Thanks for taking notice of the session!

    The session started out small, with just three of us, and finished with seven or eight. We didn’t have anyone on-hand with direct experience with developing a framework with this kind of IoC dependency-and-yet-abstraction, so the core nugget for the session was reasonably addressed* and out of the way relatively early. That did, however, afford us the opportunity to expand the discussions out into a number of interesting directions more or less related to the original subject, in the end touching on everything from AOP to introducing IoC to teams to selling TDD and BDD to management above and team members below.

    You can check out my rough notes from the session, written up from memory during the two sessions since the IoC session this morning, here:

    http://www.altnetpedia.com/Calgary200808FrameworkIoC.ashx

    If anyone has any comments or questions, I’ll try to keep an eye on this post for a while, but if I miss anything feel free to mail me at jeremy@jeremygray.ca.

  • http://jimmybogard.lostechies.com Jimmy Bogard

    That was my next note, we just completed a project that used StructureMap w/ MonoRail, just fine. When you’re back in town, I can show you what we came up with.

    @Ayende

    Ha, that was *supposed* to be a joke. I had a colleague that would bring IServiceProvider up constantly as some kind of locator panacea. I think he works at Arby’s now…

  • http://www.ayende.com/Blog/ Ayende Rahien

    Note, MonoRail has no dependency on Windsor

  • http://www.ayende.com/Blog/ Ayende Rahien

    @Jimmy,
    Not good enough, not by half.
    IServiceProvider only gives you a tiny bit. There is a lot that we need to create first.

  • http://jimmybogard.lostechies.com Jimmy Bogard

    Isn’t someone going to make the ridiculous “we already have a locator, and it’s IServiceProvider” claim?

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @All,

    I think I should have said that the MEF team is aware of the issue and is seriously considering doing just this. I just want to say it out loud and get some support for the idea so that it’s a high enough priority for them to do it.

  • http://kohari.org/ Nate Kohari

    Absolutely, Jeremy, well said. While I naturally want to use Ninject, I’m happy using other DI frameworks as well. I just don’t think that infrastructure libraries should be opinionated, because you might end up being forced to put two DI frameworks in the same project.

    If this doesn’t end up in MEF, maybe we should consider something like the AOP Alliance? I’d be willing to add it to Ninject, even if it means creating an external dependency. As long as the dependency was tiny, it would be worth it. That way framework developers could work against the common abstraction rather than creating their own custom abstractions each time.

  • http://www.thefreakparade.com Nathan

    Amen to that. If MS rejects the idea, or drags their feet, would a neutral open source project or organization providing a set of interfaces serve as a stand-in? Any chance something like that could gain critical mass? I think that in addition to IoC there are a small handful of other interfaces that could provide framework or infrastructure tool developers with the same benefit, like logging.

  • http://www.bluespire.com Rob

    Hear, hear!!!