Guidance on IUnityContainer usage and alternative patterns

Oct 5, 2010 at 9:49 PM

[Please note this is also posted in Prism forums where it was politely suggested Unity forums would be more appropriate]

Greetings,

I have recently inherited a reasonably young user interface project with my company (previous person resigned).

Without going into too much detail, one of the project goals is to allow injection of content and views of this content from third parties. To achieve this we are building a service oriented framework that uses dependency injection combined with MVVM techniques and allows people to register their content/views/services during bootstrap and it all magically becomes integrated.

Prism was chosen as primary guidance and in particular the Unity flavor of implementation.

Entering the project at this point I am happy with the technique/technology choices, and do not wish to change them.

The concerns I have at this point is that it appears that (in my opinion) IUnityContainer has been ... abused. It has been injected into just about everything in the framework including base class Views and View Models. It is passed deep into our framework at a confusingly fine granularity -- and this rings alarm bells for me. Individual declarations (local, properties etc) count over 100, and individual uses count over 300, and the project/product is less than one third complete.

From what I can gather, it is being used in the following (primary) ways:

1. Service Locator (confusingly, Container.Resolve is being used in constructors of classes that also use [Dependency] properties)
2. Object factory (Container.Resolve<ConcreteType>() in order to have its dependencies injected)
3. Injection post construction (MyObject obj = new MyObject(myParameter); Container.BuildUp(obj);)
4. Service Context (Container.CreateChildContainer(), register 'local' services/objects in child, and pass around this 'context' to perform 1., 2. and 3.)

My thoughts on these are:

1. I don't so much mind, but for consistency I would prefer to use either service locator or dependency injection (personally preferring DI). I don't like that I do not know the object dependencies by looking at it's constructor or properties, instead I need to read the source code (which for a framework is questionable).

2. Conceptually I don't mind, but I would prefer it to be wrapped in some kind of factory which sits on type of DI instead of using the container as a factory. This is fairly easy to achieve with an open generic type mapping of IFactory<> to UnityFactory<>.

3. Problematic, I would like to use a factory, but would prefer not to create a factory for every single type that might take non-DI injected parameters. This appears to be a common challenge on forums I have read. I could create IMyObjectFactory which has a method Create(myParameter) and then create and instance of UnityMyObjectFactory to allow it to then have DI performed on it, but given the number of types that do this it seems cumbersome and clumsy (as opposed to 2. which is quite clean but only works for types that do not have non-DI dependencies).

4. This one has really got me stumped. I can appreciate the value of context, and sometimes context with has some kind of hierarchy/inheritance/nesting/recursion (whichever you like), but I am struggling to see the value of creating IUnityContainer contexts deep inside your application. On a coarse level, I can see some value in a child unity container that holds services specific to a module, but not to any given service/view/viewmodel or indeed object instance. Whats the benefit? I can override a service at fine granularity, sure, but why? I can't test it. Runtime behavior predictability reduces even more (DI at a course level is enough of a challenge).

My feeling at this point is that IUnityContainer should really only be referenced from the Bootstrapper and most likely and Module that requires integration, but extracting/replacing IUnityContainer with other suitable patterns appears to be quite painful unless I can figure a reasonably generic way to solve 3. and 4.

So this is a call out to some seasoned Prism/Unity veterans.

Are my gut feelings valid?
Which usages sound reasonable and which do not and why?
What recommendations can anyone offer at this point to help me move forward?

A thousand thanks,

Jahmai

Oct 5, 2010 at 11:28 PM

I'm not a Prism veteran, but I think I can safely say a few things about DI use in general.

1) You've got a big mess on your hands. Service Locator should NOT be used in your application's mainline code without a very, very good reason. It's brittle, difficult to test, and very hard to reason about what your dependencies actually are, as you've seen. Service Location can be useful at the lower levels of a framework, but in the mainline application? Nope.

2) In general, the container shouldn't be used as an object factory. Instead, the application should be using explicit factories that are injected. If the factories themselves really need the container then ok, but they probably don't.

3) The BuildUp feature exists for one reason: trying to shoehorn DI into a framework that doesn't let you control object instantiation. ASP.NET and Xaml, I'm looking at you. If you're not in once of those situations, you shouldn't be using BuildUp.

4) This is something that's been part of p&p's guidance for quite a while now. Unity child containers can be used for two different kinds of context: lifetime and configuration. Lifetime is most commonly used in ASP.NET - you create a child container for a request, do stuff, and at the end of the request dispose the child container. A configuration context is what you've described - custom overrides of particular services. I can't speak to specific use cases, but I do know folks who've seen it be very useful. Having said that, you need to be judicious. A module is a reasonable context bound. A single view is not.

In general, I agree with your conclusion: the container should only be touched in the bootstrapper and the individual module initializers. Touching it elsewhere is usually (not always, but usually) a sign that something's not right. Although I think our strength of feeling on each item goes in the opposite directions.

As for what you should do? Pick a class that's using IUnityContainer directly, and take the usage out. It's probably pretty easy. Then take the next one. Repeat. Don't let anyone else do it without going through a code review. And perhaps some general education on DI patterns as well.

 

Oct 6, 2010 at 1:18 AM

Hi ctavares,

Thanks for considered response, some valuable advice there, it is appreciated.