The Relationship Zoo

Dependency Injection, unsurprisingly, is all about the relationships between components.

Components rarely exist or perform any useful task alone. A system that achieves a business function may use tens, hundreds or even thousands of different components, all working in collaboration to get a job done.

Early IoC containers understood one kind of relationship: “A needs a B.”

interface B { }

class A
{
    public A(B b) { ... }
}

More complex relationships, like “A creates instances of B” required the author of component A to deal directly with the container to get things done, by calling container.Resolve<B>(), for example.

Since a majority of relationships are, in fact, simple direct dependencies, this was an acceptable point from which to start developing IoC containers as a technology. Recently, more powerful, declarative ways of specifying “higher-order” dependencies have emerged. In this article we’ll look at a selection of them, using Autofac 2 as an example implementation.

I was first introduced to the term “higher-order dependencies” by Mircea Trofin at Microsoft. I’m not sure I’m using it here in precisely the same sense. The .NET Composition Primitives, which Mircea was instrumental in creating, have some elegant mechanisms for handling them – in a future article I hope to cover this in more detail.

A Catalog of Relationships

It turns out that there are a lot of different ways that one component may relate to another.

A-depends-on-B

Here’s a partial list to fill in the question mark above:

  1. A needs a B
  2. A needs a B at some point in the future
  3. A needs a B until some point in the future
  4. A needs to create instances of B

    • and provides parameters of types X and Y
  5. A needs all the kinds of B
  6. A needs to know X about B before using it

This list is chosen carefully, because each of these relationships can be expressed declaratively through simple adapter types.

Relationship Adapter Type Meaning
A needs a B None Dependency
A needs a B at some point in the future Lazy<B> Delayed instantiation
A needs a B until some point in the future Owned<B> Controlled lifetime
A needs to create instances of B Func<B> Dynamic instantiation
A provides parameters of types X and Y to B Func<X,Y,B> Parameterisation
A needs all the kinds of B IEnumerable<B> Enumeration
A needs to know X about B before using it Meta<B,X> Metadata interrogation

Some of the types above will be familiar: Lazy<T>, Func<T> and IEnumerable<T> are all .NET BCL types, and are used here with their standard semantics.

Owned<T> and Meta<T> are introduced by Autofac, and fill some of the gaps in the BCL. They’re very simple types that could be replicated in an application that wanted to use them independently.

Lazy<T>

A lazy dependency is not instantiated until its first use. This appears where the dependency is infrequently used, or expensive to construct.

class A
{
    Lazy<B> _b;
   
    public A(Lazy<B> b) { _b = b }
   
    public void M()
    {
        // The component implementing B is created the
        // first time M() is called
        _b.Value.DoSomething();
    }
}

Owned<T>

An owned dependency can be released by the owner when it is no longer required. Owned dependencies usually correspond to some unit of work performed by the dependent component.

class A
{
    Owned<B> _b;
   
    public A(Owned<B> b) { _b = b }
   
    public void M()
    {
        // _b is used for some task
        _b.Value.DoSomething();
   
        // Here _b is no longer needed, so
        // it is released
        _b.Dispose();
    }
}

In an IoC container, there’s often a subtle difference between releasing and disposing a component: releasing an owned component goes further than disposing the component itself. Any of the dependencies of the component will also be disposed. Releasing a shared component is usually a no-op, as other components will continue to use its services.

Func<T> and Func<X,Y,T>

The ‘factory’ adapters imply creation of individual instances of the dependency. The parameterised versions allow initialisation data to be passed to the implementing component.

class A
{
    Func<int, string, B> _b;
   
    public A(Func<int, string, B> b) { _b = b }
   
    public void M()
    {
        var b = _b(42, "http://hel.owr.ld");
        b.DoSomething();
    }
}

IEnumerable<T>

Dependencies of an enumerable type provide multiple implementations of the same service (interface).

class A
{
    IEnumerable<B> _bs;
   
    public A(IEnumerable<B> bs) { _bs = bs }
   
    public void M()
    {
        foreach (var b in bs)
            b.DoSomething();
    }
}

Meta<T,M>

Metadata is data-about-data. Requesting metadata with a dependency allows the dependent component to get infomation about the provider of a service without invoking it.

class A
{
    Meta<B,BMetadata> _b;
   
    public A(Meta<B,BMetadata> b) { _b = b }
   
    public void M()
    {
        if (_b.Metadata.SomeValue == "yes")
            _b.Value.DoSomething();
    }
}

The Relationship Zoo

If you’ve read this far, you’re probably desperate for an example that doesn’t use the components A and B. I feel your pain! The truth is, while these dependencies are useful in their own right, really interesting dependencies often combine multiple adapters!

Choosing a File Viewer

Metadata is excellent when there are multiple implementations of a service, and one must be selected to perform a task. The selection criterion mightn’t be inherent in the component itself, so properties make awkward choices for representing this data.

Let’s say we have a number of file viewers, and, via configuration, we associate each viewer with the file types that it should be used to view.

interface IViewer
{
    void View(string filename);
}

interface IViewerMetadata
{
    IEnumerable<string> FileTypes { get; }
}

class Browser
{
    IEnumerable<Meta<IViewer, IViewerMetadata>> _viewers;
   
    public Browser(IEnumerable<Meta<IViewer, IViewerMetadata>> viewers)
    {
        _viewers = viewers;
    }
   
    public void OnViewFile(string filename)
    {
        var ext = Path.GetExtension(filename);
        var viewer = _viewers.Single(v => v.Metadata.FileTypes.Contains(ext));
        viewer.Value.View(filename);
    }
}

In this example, IEnumerable<T> and Meta<T> are combined to express the two features of the relationship between Browser and IViewer.

Mixing in dynamic creation, controlled lifetime or lazy initialization would often also make sense in this kind of scenario.

You can see how metadata is associated with an Autofac component here, or examine one of the many examples based on the MEF APIs here.

Creating and Releasing a Message Handler

Another common combination is that of dynamic instantiation and ownership of the instances. A message pump may create, use, then release a handler for each incoming message:

interface IMessageHandler
{
    void Handle(Message message);
}

class MessagePump
{
    Func<Owned<IMessageHandler>> _handlerFactory;
   
    public MessagePump(Func<Owned<IMessageHandler>> handlerFactory)
    {
        _handlerFactory = handlerFactory;
    }
   
    public void Go()
    {
        while(true)
        {
            var message = NextMessage();
           
            using (var handler = _handlerFactory())
            {
                handler.Value.Handle(message);
            }
        }
    }
}

Many instances, with dynamic creation and controlled lifetime and parameters and and and whoah!

Yes, IEnumerable<Meta<Func<X,Y,Owned<T>>,M>> is in fact a valid relationship type. Glad you asked?

Common combinations can be eaiser to work with when they are baked into a single class. In MEF, for example, Meta<Lazy<T>> is represented as Lazy<T,M>. Likewise Func<Owned<T>> is represented as PartCreator<T>. You can plug these composite adapter types into Autofac via a custom registration source if you need them.

Custom delegate types can also ease angle-bracket-eyestrain if you use the parameterised factories a lot.

The core of Autofac 2 doesn’t have any special knowledge of any of these adapter types. They’re all plugged in as IRegistrationSource implementations, meaning that you are free to change or extend the set of adapter types as your needs dictate.

Muahahahahhahahaahhh!!!!

I heard that maniac laugh just then! Let me just slip in the disclaimer now: just because you can model almost any conceivable relationship type this way, doesn’t mean that you should. Where a simple direct dependency will suffice, don’t gouge out your eyes on six layers of nested angle brackets, or invest in new adapter types. Like all sharp tools, use context adapters with discretion.

At the same time, there’s practically no excuse to use IContainer or IComponentContext in your components anymore.

Conclusions and a Suggestion

You might not be using Autofac today, and might not have any desire to. That’s okay! Anything new and useful you see here will no doubt make its way back into the container you prefer – this is the magic of being part of an Open Source community.

If you do use Autofac, or are curious, download the .NET 4.0 version from the project site.

A note to container developers…

Although this is an Autofac-centric article, it draws on the combined experience of many passionate people who are driving .NET composition technology forward.

I’m not sure yet that we share a common vision of the “v.Next” IoC container – one thing we do seem to agree on is that it is harmful to have the container abstraction leak through into components. Perhaps in the future, IoC containers will be completely transparent, but we have some way yet to go.

Common Service Locator (CSL) was a step forward in interoperability. A good range of applications and frameworks that need dynamic or lazy instantiation no longer have to depend on a specific IoC container; I think this is one way we’ve maintained diversity and a healthy ecosystem.

CSL is a stopgap though – a service locator that could handle all of the scenarios above would be a monster of an interface indeed! CSL also breaks the declarative nature of components that use it.

I’d be very interested to hear thoughts on a new project, Common Context Adapters, which would include types like Owned<T> and Meta<T,M> to fill gaps in the BCL while it evolves. It would also provide baseline documentation on how wiring of Func<T> etc. should work.

Common Context Adapters would play a similar role to CSL by isolating applications and frameworks from the specifics of an IoC container, but in a way that is compatible with declarative dependency wiring and inversion of control.

I’m prepared to put effort into making this happen if there is enough interest.

  • Vijay Santhanam

    Brilliant!! Awesome article…

  • Pingback: Dew Drop – January 25, 2010 | Alvin Ashcraft's Morning Dew()

  • Hi Nick,

    Correct me if I’m wrong, but it sounds like you’re implicitly proposing a Common Configuration Convention Library of sorts, through the use of functors. It does bring up a couple issues, though:

    1. Does your proposal represent the lowest common denominator for having an application express its dependencies? I can’t speak for the other IoC container authors out there, but it’s safe to say that there are significant differences between each container framework’s modes of configuration, and while this proposal does sound good on paper, I’m not sure if most people are willing to (or even need to) sacrifice the configuration features of their ‘Favorite IOC container framework X’ just so they can swap containers at will.

    2. Do we really need a library like this? It’s a pretty basic question, but this project might be a big undertaking if you’re asking 5-7 other IOC container framework authors to implement these common configuration conventions. What if it turns out that only less than 5-7 people in the world end up using it? Is this a common problem that occurs enough to be rectified, or is this merely a corner case?

    Don’t get me wrong, though–as an IOC container geek myself, I think something like this would be really cool to have in my own container. I’m just weary of having to implement something that a lot of developers might not really need.

  • Little bit OT:
    Suppose I have a type called TransactedUnitOfWorkScope which either reuses the ambient UnitOfWorkScope or creates one if its not present. In case the ambient UOWS is (object) inherited assume the instance cannot be owned, in the other case it must be owned. Q: How would you model this? Tweet the answer @seagile ๐Ÿ˜‰

  • Pingback: The Way of the Whiteboard, Modern Hardware, Writing DSLs with LINQ and Component Relationships | Gamlor()

  • @Vijay thanks ๐Ÿ™‚

    @Philip thanks for visiting!

    I don’t think I’d call these ‘conventions’ in the sense that it is currently used. How Func parameters map to constructor parameters, for example, would be closer to what I’d call a convention today. If you squint hard enough everything looks like a convention though. ๐Ÿ™‚

    Regarding applicability – the use of CSL alone illustrates the broad need for a shared approach. Everywhere you see CSL, an injected container, or a static ObjectFactory.CreateInstance() call, applying context adapters will reduce friction and improve the quality of components.

    I’m not so much calling for everyone to go out and implement these features, as asking that we do so together to improve the ecosystem. They’re already being implemented anyway:

    StructureMap: Func support is being added (http://groups.google.com/group/structuremap-users/browse_thread/thread/ac934bc9d758b796/9b53eb030740e4ec). It already supports T[] as an enumeration adapter.

    Unity: Version 2 will support Func (http://www.tavaresstudios.com/Blog/post/Unity-20-Automatic-Factories.aspx), IEnumerable is already supported, and some composition between these is possible.

    MEF, as noted in the text, includes Lazy, Metadata (via Lazy) and IEnumerable support in v1. You can see from MEF’s IEnumerable implementation that this technique works in the absence of convention (MEF enumerable imports require a custom attribute.) In the pipeline, PartCreator and PartLifetimeContext give a glimpse of what’s to come.

    Windsor: TypedFactoryFacility covers similar ground to Func (http://kozmic.pl/archive/2009/12/24/castle-typed-factory-facility-reborn.aspx); I’ve seen an implementation of Func itself via Krzysztof Kozmic. Since Windsor has lifetime management, any impl of Func will likely come with a class matching Owned. IEnumerable is no doubt in there, and the .NET 4 version has been reported to be including Lazy (as will be the case with most containers.)

    Ninject – it has been a while since I looked, actually…. I’m willing to wager that if Ninject doesn’t already support a big chunk of this, its extremely productive community will have an implementation whipped up by Sunday evening (I’m talking about you, @innovatian!)

    Funq: Func is of course in there, no doubt more are/will be supported as @kzu is one of the original proponents of this approach.

    In my experience – yours may vary – nearly every non-trivial project that uses an IoC container has at least one, usually a few, components that adapt a service interface to calling into the container (or a static ObjectFactory or IoC class). Among frameworks the situation is similar – nearly every framework I’ve seen in the last year comes out at v1 with either a CSL dependency or a host of IWhateverFactory interfaces that need to be adapted by hand to every IoC container.

    If you take the framework case, you’ll see that the effort now required to maintain a million-and-one container adapters is much greater than what would be required to implement a small set of composable primitives. In Autofac, maintaining AutofacContrib.FrameworkX, FrameworkY, FrameworkZ and the FrameworkTheKitchenSink is a lot of work, and the situation will only get worse as more third-party libraries attempt to support IoC.

    Do we really need as many integration adapters as n-containers * n-container-versions * n-framework-extension-points * n-framework-versions? We’re on an unsustainable path.

    Although I fully realise that there’s effort involved, in the end we’ll be in a better place.

    @Seagile That one would be better dealt with on your favourite IoC container’s mailing list ๐Ÿ™‚

  • hammett

    great write-up. I’m against “dependency injection” as you know, but besides that, I agree 100% with you! :-p

  • Nice post Nick, a prolific work as usual.

    On the common adapters thing, sounds awesome. Where do I sign up? ๐Ÿ™‚

  • @hammett I started with those two words just for you! ๐Ÿ˜€

    @gblock Will certainly be in touch good sir. ‘Prolific’ might be the right word.. I need a few days for my wrists to recover from typing that monster… ๐Ÿ™‚

  • I would almost be tempted to alias the other adapter types in order to bring them into line with the more intuitively named Meta and Owned! ๐Ÿ™‚

    Excellent post. Great explanations with concrete examples. A fantastic elucidation of an oft muddied topic. Well done!!

  • Two other variations on the above are ‘A needs a subset of B’, ‘A needs a subset of B in a specific order’ and ‘A needs a special B’. In either case the required dependency is either specific for A’s context or the ‘nature’ of A. I see both quite a bit wrt extensibility scenarios where there are multiple B’s present. The challenge when they do occur is how to have A get just the B’s he needs without pushing the filtering/ordering concern into A.

  • I guess that’s three variations ๐Ÿ™‚

  • Those two (er, three) are pretty challenging and I’ve been thinking about them more lately. Although the .NET type system can’t declaratively express metadata constraints in a signature, we’re not in a completely hopeless situation unless you need to do rejection. By using IQueryable of a metadata-enabled type as an adapter, the container gets the declaratively-specified requirements via the query. This opens a few doors to dynamic dependency satisfaction and prevents unnecessary instantiation of the components.

    (BTW, I only realised recently – if the dependency is on IQueryable<Meta<T, TMetadata>>, despite the fact that Meta<T, TMetadata> is not lazy, the appropriate component/s can be selected without instantiating others unnecessarily.)

  • This is a good and fairly inclusive enumeration of possible dependency relationship types.

    I think some of them are valuable as more explicit dependency relationships, whereas others are important DI Container creation strategies, but not necessarily as explict Adapters. Let’s look at each one:

    Func<B>: This is simply an Abstract Factory, which is one of the most important DI patterns available. It allows us to bridge the gap where a consumer needs to create an arbitrary number of abstract instances managed by the container. Some DI Containers can automatically create such strongly typed Factories at run-time based on registered parts and the type of the Abstract Factory, but it would be very valuable if they all could.

    Func: This is also an Abstract Factory, but an even more important variation, as it bridges the gap where we need to combine long-lived services (B’s dependencies) with run-time values (X and Y). In reality, we will need Func, Func, Func etc. It would be even more valuable if we could agree on a Common Context Adapter that allows each DI Container to automatically emit an implementation that does this. I have more thoughts on this, but let’s continue that discussion over at the Common Context Adapters project ๐Ÿ™‚

    IEnumerable<B>: This is another common DI pattern, but in many cases I consider it a Leaky Abstraction if a consumer explicitly requests it like this. It would be more correct to simply request B and then configure the container to return a CompositeB or a CoalescingB (both implement B), thus encapsulating the knowledge that there may be more than one. Once again, it would be valuable if a DI Container could automatically emit these. It ought to be doable for Composites, but I’m not sure about Coalescers…

    Owned<B>: This one I’m not quite sure of. As a general principle, I consider DI to imply that we are giving up on controlling the lifetime of dependencies, yet here we attempt to do so anyway. It feels like a Leaky Abstraction as well, but I presume that for a long-lived service that consumes short-lived dependencies (created by Func), this provides a mechanism to signal the release of the dependency (and thereby allowing for potential disposal). I have to think about this some more.

    Lazy<B>: This can be a valid lifetime strategy, although I consider it a bit of a corner case. I’ve already described my thoughts on this approach here: http://blog.ploeh.dk/2010/01/20/RebuttalConstructorOverinjectionAntipattern.aspx. In summary, I find it valuable if a DI Container supports this lifetime strategy, but I consider it a Leaky Abstraction to take a dependency on Lazy<B>. Rather, Lazy<B> ought to be a lifetime strategy that we can configure in the container so that when A requests B, they really get a Lazy<B> (which means that Lazy<B> must derive from B).

    Meta<B>: This is basically just a Tuple, but with a bite more intention-revealing name. Once more, my knee-jerk reaction is that if a consumer requests such a dependency it would be a Leaky Abstraction. A better approach would be to model the dependency either by the Tester-Doer pattern or try to adhere more to the Hollywood Principle. But once again, as seen from the Container’s side, it would be awesome if we could specify complex rules that injects dependencies based on some sort of context. I would rather want to see X expressed in terms of the Specification pattern (basically a Predicate). At this point, we are very close to a parameterized Abstract Factory – it’s Func where S is a Specification.

  • Mark, thanks for the thoughtful comment!

    Enumerability, and its combination with Metadata, isn’t a leaky abstraction when the identities of the individual injected components matter to the consuming component. You mentioned coalescing – instead of applying these adapters to the consumer of the coalescer, they’d be used to implement the coalescer in a container-independent way. Likewise for the composite.

    As a concrete example, instead of exposing all of the individual “shape renderers” to the different components in a drawing application, they’d use a composite shape renderer that accepted a shape and dispatched it to the appropriate underlying “atomic” renderer. It is the implementation of the composite renderer that needs enough information to correctly dispatch the shape.

    Agreeing on small building blocks like these mean that more complex composition patterns like these two you’ve mentioned can be defined in an application-specific way. Deciding on a common specification for implementing these patterns in their entirety would be orders of magnitude harder than the task before us here. (Imagine trying to write a common spec for how rendering an unknown shape would proceed in the above example.)

    Regarding ownership/component release – the general problem here is that in order for the container to manage lifetime “for us” it must have some sense of where a Unit of Work begins and ends. Most current approaches require container-specific configuration based on ambient state and application lifecycle events. It is not always possible to associate a unit of work in an application with these.

    On Lazy and inheritance – I agree that the transparent proxying approach is nice, but not all services are expressed as interfaces or virtualised ABCs. Providing the transparent mechanism via configuration is a nice addition to a baseline ‘portable’ alternative like Lazy.

    Your ideas on expressing metadata selection as a Func/Predicate structure are really interesting – I’m glad you’ve popped up on the CCA forum, this will be a fantastic alternative to consider when we get to selection-from-alternatives ๐Ÿ™‚

  • Mark, i think you are missing one important aspect of metadata. It is not only used for fitering, it is also used for providing information that self-describes the export. For example it might provide category information. For example if you use Meta<Lazy, FooMetadata> or Lazy (in the MEF case) you are able to access information about Foo WITHOUT instantiating it. Having a magic policy that automatically applies a filter to the collection would be nice, but it doesn’t suffice for the second capabillity I just mentioned.

    On the coallescing of multiple Foos into a composite Foo, that also does not meet the use cases. Imagine you are pulling in menu actions that will displayed on a toolbar. The UI binds to the list of actions, it doesn’t bind to a compose action. Each action is worked with individually. Composing the action would then mean that the UI sees them all as a single action, when they are not. Now if you are doing a chain of responsibility on the other hand, then having the components as a single would be very nice.

  • Pingback: Autofac 2.1 goes RC (the “me too!” release) : Nicholas Blumhardt()

  • Pingback: Introducing Autofac 2.1 RTW : Nicholas Blumhardt()

  • Pingback: nRoute: Now, More Wholesome()

  • Pingback: Autofacๆญฃๅผๅ‘ๅธƒ2.1็‰ˆ - ่‡ช็”ฑใ€ๅˆ›ๆ–ฐใ€็ ”็ฉถใ€ๆŽข็ดข - ๅšๅฎขๅ›ญ()

  • I keep coming back to this epic post when I’m thinking about DI. Great stuff!

  • Thanks Wim!

  • Pingback: The IIndex<K,V> Relationship Type : Nicholas Blumhardt()

  • Pingback: Functional Caching Part 4: Dependency Injection « Executable Intent()

  • Pingback: Executable Intent » Functional Caching Part 4: Dependency Injection()

  • Pingback: An Autofac Lifetime Primer : Nicholas Blumhardt()

  • Pingback: How to completely disable Autofac components | Blair Conrad()

  • Pingback: Developer day on IoC | Sara's Blog()

  • Pingback: Lightweight Adaptation โ€“ Coming Soon – Nicholas Blumhardt()