CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Jeremy D. Miller -- The Shade Tree Developer

Under the hood and working with .Net, TDD, Software Design, and Agile Stuff

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.


Comments

Rob said:

Hear, hear!!!

# August 16, 2008 1:00 PM

Nathan said:

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.

# August 16, 2008 1:08 PM

Nate Kohari said:

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.

# August 16, 2008 1:19 PM

Jeremy D. Miller said:

@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.

# August 16, 2008 2:36 PM

Jimmy Bogard said:

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

# August 16, 2008 2:39 PM

Ayende Rahien said:

@Jimmy,

Not good enough, not by half.

IServiceProvider only gives you a tiny bit. There is a lot that we need to create first.

# August 16, 2008 2:49 PM

Ayende Rahien said:

Note, MonoRail has no dependency on Windsor

# August 16, 2008 2:49 PM

Jimmy Bogard said:

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...

# August 16, 2008 3:12 PM

Jeremy Gray said:

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:

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.

# August 16, 2008 5:59 PM

Jeremy Gray said:

* 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.

# August 16, 2008 6:05 PM

Haacked said:

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.

# August 16, 2008 6:57 PM

Jeremy Gray said:

@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. ;)

# August 16, 2008 7:11 PM

ASP.NET MVC Archived Blog Posts, Page 1 said:

Pingback from  ASP.NET MVC Archived Blog Posts, Page 1

# August 16, 2008 11:05 PM

Chad Myers said:

@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.

# August 16, 2008 11:36 PM

Udi Dahan: The Software Simplist said:

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.

# August 17, 2008 2:21 AM

Jeremy Gray said:

@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.

# August 17, 2008 11:53 AM

Jeremy Gray said:

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

# August 17, 2008 11:54 AM

Stuart Carnie said:

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<T>()?  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.

# August 17, 2008 11:57 AM

Haacked said:

@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.

# August 17, 2008 12:18 PM

Jeremy Gray said:

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

# August 17, 2008 1:30 PM

Dew Drop - August 17, 2008 | Alvin Ashcraft's Morning Dew said:

Pingback from  Dew Drop - August 17, 2008 | Alvin Ashcraft's Morning Dew

# August 17, 2008 2:39 PM

Greg M said:

"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?

# August 17, 2008 11:48 PM

Nate Kohari said:

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. :)

# August 18, 2008 7:16 AM

Daniel Fernandes said:

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<IFoo>().ResolveWith<Foo>().AsSingleton();

WhenResolvingType<IBar>().ResolveWith<Bar>().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!).

# August 18, 2008 8:19 AM

Jeremy D. Miller said:

@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.  

# August 18, 2008 9:11 AM

Jeremy D. Miller said:

@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.

# August 18, 2008 9:13 AM

Sebastien Lambla said:

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?

# August 18, 2008 9:28 AM

Jeremy D. Miller said:

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.

# August 18, 2008 9:48 AM

Jeremy Gray said:

@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.

# August 18, 2008 9:51 AM

Daniel Fernandes said:

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.

# August 18, 2008 10:16 AM

Tim Scott said:

It might help your cause that the author of Windsor has joined the MEF team.  hammett.castleproject.org

# August 18, 2008 10:43 AM

Glenn Block said:

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

# August 19, 2008 2:43 PM

Glenn Block said:

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.

# August 19, 2008 2:48 PM

wekempf said:

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<T>(this IServiceProvider self)

  {

     return (T)self.GetService(typeof(T));

  }

  // not showing all extensions for brevity

}

# August 21, 2008 2:02 PM

Philip Laureano said:

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.

# August 24, 2008 5:05 AM

Zen and the art of Castle maintenance » Blog Archive » Integrating MonoRail with your favorite IoC container said:

Pingback from  Zen and the art of Castle maintenance  &raquo; Blog Archive   &raquo; Integrating MonoRail with your favorite IoC container

# September 5, 2008 12:01 AM

Software Mechanics said:

Announcing: The IServiceLocator interface

# October 2, 2008 1:41 AM

My Technobabble said:

Today we launched an exciting project on CodePlex, namely the Common Service Locator library . What is

# October 2, 2008 4:15 AM

Glenn Block said:

Today we launched an exciting project on CodePlex, namely the Common Service Locator library . What is

# October 2, 2008 4:17 AM

Mirrored Blogs said:

Today we launched an exciting project on CodePlex, namely the Common Service Locator library . What is

# October 2, 2008 4:40 AM

Community Blogs said:

Today we launched an exciting project on CodePlex, namely the Common Service Locator library . What is

# October 2, 2008 4:58 AM

Weekly Links #21 | GrantPalin.com said:

Pingback from  Weekly Links #21 | GrantPalin.com

# October 5, 2008 10:46 PM

Agilification said:

Common Service Locator

# October 23, 2008 12:42 AM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add

About Jeremy D. Miller

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 previously worked as a systems architect building mission critical supply chain software for a Fortune 100 company and learned agile development practices as a .Net consultant at ThoughtWorks, one of the pioneers of agile development. Jeremy is the author of the open source StructureMap (http://structuremap.sourceforge.net) tool for Dependency Injection with .Net and the forthcoming StoryTeller (http://storyteller.tigris.org) tool for supercharged FIT testing in .Net. Jeremy's thoughts on just about everything software related can be found on his weblog "The Shade Tree Developer" at http://codebetter.com/blogs/jeremy.miller, part of the popular CodeBetter site. Jeremy is a Microsoft MVP for C#. Check out Devlicio.us!

Our Sponsors

Proudly Partnered With


This Blog

Syndication

News

All opinions expressed here constitute my (Jeremy D. Miller's) personal opinion, and do not necessarily represent the opinion of any other organization or person, including (but not limited to) my fellow employees, my employer, its clients or their agents.

About Me

"Best Of" Compendium

StructureMap (Dependency Injection for .Net)

StoryTeller (Supercharged Fit)

Build your own Cab

TestDriven

MVP