Log in

Subscribe to site updates.

Dependency Injection from a Behavioural Perspective

Posted by Nicholas Blumhardt | June 29, 2008 14:17

The basic concept of container-managed dependency injection has made its way into mainstream .NET development. However, as Brad Wilson observed in a recent Alt.NET Podcast with Nate Kohari, we're not yet taking all of the opportunites that this new approach presents.

Here's my conjecture: I believe that the big change in thinking that we're due for is to understand DI containers from a dynamic, behavioural perspective in addition to the static, structural perspective in common use today.

Most developers view DI containers as a way to map interfaces or names (services) to implementation types (components.) This interpretation is based loosely on the idea of the container as a map or Registry with a recursive instantiation procedure attached.

This is a great way start off when explaining DI containers. Jeremy Miller's StructureMap was the first successful .NET DI container (ca. 2004!) and in its name you can observe Jeremy's use of this metaphor in order to get the concept across.

The API models of most popular containers revolve around this mapping of services to implementations, and this in turn has shaped the way these containers are used.

The 'power users' of these frameworks however view them much more in the light of the Factory pattern(s).

Factory and Builder are at heart a behavioural patterns. Their essence is not so much about hiding the concrete type of an instance, as hiding the process by which that instance was created or located. ObjectBuilder wholeheartedly embraced this dynamic role, but its positioning as a meta-DI framework limited its influence on the popular perception of DI.

Autofac has its roots in the world-view of Needle. Autofac and Needle map services not to implementation types, but to functions that create component instances. You can see this in Autofac's name (it is an automated Factory.)

This shift in perspective is one aspect of Autofac that is well-understood and is getting used widely. A recent example on the Autofac news group looked something like:

builder.Register(c => someCondition ? (IUnitOfWork) new UnitOfWork() : new NullUnitOfWork())
  .ContainerScoped();

Dynamic behaviour is moving to the fore in other containers, too. Ninject has a really interesting concept of context that can be used in interesting ways, including through match expressions which are evaluated at resolve time.

// Pinched from the Ninject forum, thanks Frank!
container.Bind<IService>()
  .To<ServiceB>()
  .Only(When.Context.Matches(c => c.Member.ReflectedType == typeof(Bar)));

This kind of thing is being done elsewhere, too. For example Windsor is flexible enough that Bill Pierce has created a dynamic name resolution extension for it.

Unity has a static factory extension that provides some of the features of Autofac's lambda registrations. Unity also inherits ObjectBuilder's build chain architecture which provides a super-flexible way of adding behaviour to the build process.

Selection of an implementer is just the tip of the iceberg when considering how behaviour and logic can be applied in the activation process.

Autofac itself will go further in this direction. The OnActivated() hook is one place where there might be some new possibilities in a future version:

builder.OnActivating<ICache>(cache => cache.WarmUp());

This (imagined) example would go some way towards the kind of functionality that the 'startable' facility would otherwise be use for, while expressing intent more clearly.

Autofac also has an IRegistrationSource extension point that can be used to register components on the fly as they're requested from the container. This is used in a few places currently, like the open generic type support, and the RegisterTypesAssignableTo(Type) shortcut, but could be used to implement tricks like naming schemes on top of the container:

builder.RegisterFromName(
  name => name.StartsWith("command."),
  (reg, name) => reg.Register(FindCommandType(name)).FactoryScoped());

Food for thought, anyway.

The point I'd like to make with this post is that in most cases, the behavioural aspects of containers have been kept 'under the covers' and exposed to users through advanced API concepts like container extensions.

A wider appreciation of the behavioural possibilities in DI containers is going to push more dynamic behaviour into the first-class APIs of containers. Dynamic behaviour is going to be planted firmly in the realm of container configuration rather than container extension.

You can see in most of these examples one key facilitator for changing this. Lambda expressions in C# 3.0 make behavioural configuration possible in a terse manner without relying on subclassing or interface implementation.

This is going to have a direct effect on the perceptions of developers who are using DI, and I think it has the potential to open up new roles for DI containers within application architectures.

Comments

There are no comments on this article.

Your Comment





Reset

Disclaimer: These articles represent the opinions of the authors and may not match the official position of Ubik Systems Pty. Ltd. Confirmation should be sought on all matters involving professional advice.