Chapter 3. DI Containers

Menu

  • XML configuration
  • Code as configuration
  • AUTO-REGISTRATION
  • COMPOSITION ROOT
  • REGISTER RESOLVE RELEASE

When I was a kid, my mother and I would occasionally make ice cream. This didn’t happen too often, because it required a lot of work and it was hard to get right. In case you’ve never tried making ice cream, figure 3.1 illustrates the process.

Figure 3.1. Making ice cream is an arduous process, with plenty of opportunities for error.

Real ice cream is based on a crème anglaise, which is a light custard made from sugar, egg yolks, and milk or cream. If heated too much, this mixture will curdle. Even if you manage to avoid this, the next phase presents more problems. Left alone in the freezer, the cream mixture will crystallize, so you have to stir it at regular intervals until it becomes so stiff that this is no longer possible. Only then will you have a good, homemade ice cream.

Although this is a slow and labor-intensive process, if you want to and you have the necessary ingredients and equipment, you can use the technique I’ve outlined to make ice cream.

Today, some 30 years later, my mother-in-law makes ice cream with a frequency unmatched by my mother and me at much younger ages—not because she loves making ice cream, but because she uses technology to help her. The technique is still the same, but instead of regularly taking out the ice cream from the freezer and stirring it, she uses an electric ice cream maker to do the work for her (see figure 3.2).

Figure 3.2. My mother-in-law’s Italian ice cream maker.

DI is first and foremost a technique, but you can use technology to make things easier. In part 3, I’ll describe DI as a technique. Then, in part 4, we’ll take a look at the technology that can be used to support the DI technique. We call this technology DI CONTAINERS.

In this chapter, we’ll look at DI CONTAINERS as a concept: how they fit into the overall topic of DI, some patterns and practices concerning their usage, and some history about .NET DI CONTAINERS. We’ll also look at some examples along the way.

The general outline of the chapter is illustrated by figure 3.3. It begins with a general introduction to DI CONTAINERS, including a description of a concept called AUTO-WIRING, followed by a section on various configuration options. You can read about each of these configuration options in isolation, but I think it would be beneficial to at least read about CODE AS CONFIGURATION before you read about AUTO-REGISTRATION.

Figure 3.3. The structure of this chapter. The DI Container landscape section is optional.

The central section in the chapter is a mini-catalog of design patterns related to DI CONTAINERS. Although it follows the catalog format, the REGISTER RESOLVE RELEASE (RRR) pattern description relies on the COMPOSITION ROOT pattern, so it makes sense to read both in sequence. You can skip the section about configuration options to go directly to the patterns, but those sections are best read in order.

The last section is different. It’s much less technical and focuses on how DI CONTAINERS fit into the .NET ecosystem. You can skip reading this section if you don’t care about that aspect.

The purpose of the chapter is to give you a good understanding of what a DI CONTAINER is and how it fits in with all the rest of the patterns and principles in this book; in a sense, you can view this chapter as an introduction to part 4 of the book. Here, we’ll talk about DI CONTAINERS in general, whereas in part 4, we’ll talk about specific containers and their APIs.

It may seem a bit strange that we talk about DI CONTAINERS here in chapter 3 and then more or less forget about them again in the next six chapters, but there’s a reason for that. In this part of the book, I want to draw the big picture of DI, and it’s essential that you understand how DI CONTAINERS fit into the scheme of things. In parts 2 and 3, I’ll occasionally show you some examples that involve a DI CONTAINER, but for the most part I’ll keep the discussion general. The principles and patterns described in the middle of the book can be applied to all DI CONTAINERS.

3.1. Introducing DI Containers

A DI CONTAINER is a software library that can automate many of the tasks involved in composing objects and managing their lifetimes. Although it’s possible to write all the required infrastructure code with POOR MAN’S DI, it doesn’t add much value to an application. On the other hand, the task of composing objects is of a general nature and can be resolved once and for all; this is what’s known as a Generic Subdomain.[1]

1 Eric Evans, Domain-Driven Design: Tackling Complexity in the Heart of Software (New York: Addison-Wesley, 2004), 406.

 

Definition

A DI CONTAINER is a library that provides DI functionality.

 

 

Note

DI CONTAINERS are also known as Inversion of Control (IoC) Containers or (more rarely) Lightweight Containers.

 

Although you need to address the application infrastructure, doing so doesn’t in itself add business value, so using a general-purpose library makes the most sense. It’s no different than implementing logging or data access. Logging application data is the kind of problem that’s best addressed by a general-purpose logging library. The same is true for composing object graphs.

 

Warning

Don’t expect a DI CONTAINER to magically make tightly coupled code loosely coupled. A DI CONTAINER can make the use of DI more efficient, but an application must first and foremost be designed with the DI patterns and techniques in mind.

 

In this section, I’ll discuss how DI CONTAINERS compose object graphs, and I’ll show you some examples to give you a general sense of what using a container might look like.

3.1.1. Hello container

A DI CONTAINER is a software library like any other software library. It exposes an API that you can use to compose objects. Composing an object graph is a single method call. All DI CONTAINERS also require you to configure them before you use them to compose objects, but I’ll revisit that in section 3.2.

Here, I’ll show you some examples of how DI CONTAINERS may resolve object graphs for the expanded sample application from section 2.3. For each request, the ASP.NET MVC framework will ask for an instance of the appropriate type of IController, so you must implement a method that uses a DI CONTAINER to compose the corresponding object graph.

 

Tip

Section 7.2 contains detailed information about how to compose ASP.NET MVC applications.

 

The MVC framework will invoke the method with a Type instance that identifies the type of IController (for example, HomeController or BasketController) that it needs, and you must return an instance of that type.

This functionality can be implemented with all the DI CONTAINERS covered in part 4, but I’ll show only a few examples here.

Resolving controllers with various DI Containers

Unity is a DI CONTAINER with a fairly pattern-conforming API. Assuming you already have an instance of Unity’s UnityContainer class, you can resolve an IController instance from a controllerType Type argument:

var controller = (IController)this.container.Resolve(controllerType);

You’ll pass the controllerType parameter to the Resolve method and get back an instance of the requested type, fully populated with all appropriate DEPENDENCIES. Because the weakly-typed Resolve method returns an instance of System.Object, it must be cast to IController.

For those cases where you know the requested type at design time, there’s also a generic version of the Resolve method.

Many of the DI CONTAINERS have APIs that are similar to Unity’s. The corresponding code for Castle Windsor looks identical to Unity’s, although the container instance would instead be an instance of the WindsorContainer class. Other containers have slightly varying names—with StructureMap, for example, the previous code would look like this:

var controller = (IController)this.container.GetInstance(controllerType);

The only real difference is that the Resolve method is called GetInstance. You can extract a general shape of a DI CONTAINER from these examples.

Resolving object graphs with DI Containers

A DI CONTAINER is an engine that resolves and manages object graphs. Although there’s more to a DI CONTAINER than resolving objects, this is a central part of any container’s API. The previous examples show that containers have a weakly-typed method for that purpose. With variations in names and signature, it looks like this:

object Resolve(Type service);

As the previous examples demonstrate, because the returned instance is typed as System.Object, you often need to cast the return value to the expected type before using it.

Many DI CONTAINERS also offer a generic version for those cases where we know which type to request at compile time. They often look like this:

T Resolve<T>();

Instead of supplying a Type method argument, such an overload takes a type parameter (T) that indicates the requested type. The method returns an instance of T.

Most containers throw an exception if they can’t resolve the requested type.

 

Warning

The signature of the Resolve method is extremely powerful and versatile. You can request an instance of any type and your code will still compile. In fact, the Resolve method fits the signature of a SERVICE LOCATOR,[2] and you’ll need to exercise care not to use your DI CONTAINER as a SERVICE LOCATOR.

2 Mark Seemann, “Pattern Recognition: Abstract Factory or Service Locator?” http://blog.ploeh.dk/2010/11/01/PatternRecognitionAbstractFactoryOrServiceLocator.aspx

 

If we view the Resolve method in isolation, it looks almost like magic. From a compiler perspective, it’s possible to ask it to resolve instances of arbitrary types. How does the container know how to compose the requested type, including all DEPENDENCIES?

It doesn’t know this, and you’ll have to tell it first. You do so using registration or configuration and this is where you map ABSTRACTIONS to concrete types—I’ll return to this topic in section 3.2. If a container has insufficient configuration to fully compose a requested type, it will normally throw a descriptive exception. As an example, Castle Windsor has exemplary exceptions messages like this one:

Can’t create component ‘Ploeh.Samples.MenuModel.Mayonnaise’ as it has dependencies to be satisfied.

Ploeh.Samples.MenuModel.Mayonnaise is waiting for the following dependencies:

Services:

- Ploeh.Samples.MenuModel.EggYolk which was not registered.

In this example, you can see that Castle Windsor can’t resolve Mayonnaise because it wasn’t configured to deal with the EggYolk class.

If the container is correctly configured, it can resolve even complex object graphs from the requested type. If something is missing from the configuration, the container can provide detailed information about what’s missing. In the next section, we’ll take a closer look at how this is done.

3.1.2. Auto-wiring

DI CONTAINERS thrive on the static information compiled into all classes that use CONSTRUCTOR INJECTION. Using Reflection, they can analyze the requested class and figure out which DEPENDENCIES are needed.

Some DI CONTAINERS also understand the PROPERTY INJECTION pattern, but all of them inherently understand CONSTRUCTOR INJECTION and compose object graphs by combining their own configuration with the information extracted from the classes’ constructors. This is called AUTO-WIRING.

 

Definition

AUTO-WIRING is the ability to automatically compose an object graph from maps between ABSTRACTIONS and concrete types.

 

Figure 3.4 describes the general algorithm most DI CONTAINERS follow to AUTO-WIRE an object graph. A DI CONTAINER will use its configuration to find the appropriate concrete class that matches the requested type. It then uses Reflection to examine the class’s constructor. If there’s a default constructor, it will invoke the constructor and return the created instance.

Figure 3.4. Simplified workflow for AUTO-WIRING. A DI CONTAINER will recursively find concrete types and examine their constructors until it can create the entire object tree.

If the constructor requires arguments, a recursive process starts where the DI CONTAINER will repeat the process for each argument type until all constructors can be satisfied.

In section 3.2, we’ll take a closer look at how containers can be configured, but for now the most important thing to understand is that at the core of the configuration is a map of how various ABSTRACTIONS map to concrete classes. That sounds a bit theoretical (I’m sure that the word abstraction doesn’t help), so I think an example will be helpful.

Example: Auto-wiring a BasketController

In this example, I’ll explain how AUTO-WIRING works in principle. The example doesn’t rely on any particular DI CONTAINER but instead provides an outline of how containers compose object graphs.

Imagine that you want to resolve an instance of the BasketController class. You do this by invoking the Resolve method with typeof(BasketController). In the end, you’d like to end up with an instance of BasketController, composed as shown in figure 2.26. In order to achieve this, you must first make sure that the container has the correct configuration. Table 3.1 shows how this configuration maps ABSTRACTIONS to concrete types. I also added a column that shows whether the ABSTRACTION is an interface or abstract base class—from a DI CONTAINER’S perspective this isn’t important, but I thought it would help to clarify what’s going on.

Table 3.1. Mapping types to support AUTO-WIRING of BasketController

ABSTRACTION type

ABSTRACTION

Concrete type

Concrete BasketController BasketController
Interface IBasketService BasketService
Abstract class BasketRepository SqlBasketRepository
Abstract class BasketDiscountPolicy RepositoryBasketDiscountPolicy
Abstract class DiscountRepository SqlDiscountRepository
String connString “metadata=res://*/CommerceModel.csdl| [...]”

When a DI CONTAINER receives a request for a BasketController, the first thing it will do is look up the type in its configuration. BasketController is a concrete class, so it maps to itself. The container then uses Reflection to inspect BasketController’s constructor. From section 2.3.2 you may recall that BasketController has a single constructor with this signature:

public BasketController(IBasketService basketService)

Because this constructor isn’t a default constructor, we need to repeat the process for the IBasketService constructor argument when following the general flowchart from figure 3.4.

The container looks up IBasketService in its configuration and finds that it maps to the concrete BasketService class. The single public constructor for BasketService has this signature:

public BasketService(BasketRepository repository,
    BasketDiscountPolicy discountPolicy)

That’s still not a default constructor, and now you have two constructor arguments to deal with. The container takes care of each in order so it starts with the abstract BasketRepository class that, according to the configuration, maps to SqlBasketRepository.

SqlBasketRepository has a public constructor with this signature:

public SqlBasketRepository(string connString)

The single constructor argument is a string parameter with the name connString which is configured to have a particular value. Now that the container has the appropriate value, it can invoke the SqlBasketRepository constructor. It has now successfully handled the repository parameter for the BasketService constructor, but it will need to hold on to that value for a while longer because it also needs to take care of the discountPolicy parameter.

According to the configuration, BasketDiscountPolicy maps to the concrete RepositoryBasketDiscountPolicy class, which has this public constructor:

public RepositoryBasketDiscountPolicy(DiscountRepository repository)

Looking up DiscountRepository in its configuration, the container finds that it maps to SqlDiscountRepository, which has this constructor:

public SqlDiscountRepository(string connString)

This situation is the same as what you encountered with the SqlBasketRepository. The connString argument is mapped to a particular connection string that the container can supply to the constructor.

It can now pass the new SqlDiscountRepository instance to the RepositoryBasketDiscountPolicy constructor. Together with the SqlBasketRepository from before, it can now fulfill the BasketService constructor and invoke it via Reflection. Finally, it passes the newly created BasketService instance to the BasketController constructor and returns the BasketController instance.

Essentially, this is how AUTO-WIRING works, although it’s more complicated than that. DI CONTAINERS also need to take care of LIFETIME MANAGEMENT and perhaps address PROPERTY INJECTION as well as other, more special creational requirements. The salient point is that CONSTRUCTOR INJECTION statically advertises the DEPENDENCY requirements of a class, and DI CONTAINERS use that information to AUTO-WIRE complex object graphs.

As the example shows, the container must be configured before it can compose object graphs. Registration of components can be done in various ways.

3.2. Configuring DI Containers

Although the Resolve method is where all the action happens, you should expect to spend more time with a DI CONTAINER’S configuration API. Resolving object graphs is, after all, a single method call.

DI CONTAINERS tend to support two or three of the common configuration options shown in figure 3.5. A few don’t support AUTO-REGISTRATION and a single one also lacks support for CODE AS CONFIGURATION, whereas XML configuration is ubiquitous. Most allow you to mix several approaches in the same application.

Figure 3.5. The most common ways to configure a DI CONTAINER shown against dimensions of explicitness and the degree of binding

These three configuration options have different characteristics that make them useful in different situations. Both XML and CODE AS CONFIGURATION tend to be explicit because they require us to register each component individually. AUTO-REGISTRATION, on the other hand, is much more implicit because it uses conventions to register a set of components by a single rule.

When we use CODE AS CONFIGURATION, we compile the container configuration into an assembly, whereas XML configuration enables us to support late binding where we can change the configuration without recompiling the application. In that dimension, AUTO-REGISTRATION falls somewhere in the middle, because we can ask it to scan a single assembly known at compile time, or alternatively to scan all assemblies in a predefined folder.

Table 3.2 lists the advantages and disadvantages of each option.

Table 3.2. Configuration options

Style

Description

Advantages

Disadvantages

XML Configuration settings (often in .config files) specify the mappings. Supports replacement without recompilation High degree of control No compile-time checks Verbose
CODE AS CONFIGURATION Code explicitly determines mappings. Compile-time checks High degree of control No support for replacement without recompilation
AUTO-REGISTRATION Rules are used to locate suitable components and build the mappings. Supports replacement without recompilation Less effort required Helps enforce conventions to make a code base more consistent Partial compile-time checks Less control

Historically, DI CONTAINERS started out with XML configuration, which also explains why all of them support this option. However, the current trend is that this feature is being downplayed in favor of more convention-based approaches.[3] Although AUTO-REGISTRATION is the most modern option, it’s not the most obvious place to start. Because of its implicitness it may seem more abstract than the more explicit options, so instead I’ll cover each option in historical order, starting with XML configuration.

3 For a good overview article on Convention over Configuration, see Jeremy Miller, “Patterns in Practice: Convention Over Configuration,” (MSDN Magazine, February 2009). Also available online at http://msdn.microsoft.com/en-us/magazine/dd419655.aspx

3.2.1. Configuring containers with XML

When DI CONTAINERS first appeared back in the early 2000s, they all used XML as a configuration mechanism—most things did back then. Much experience with XML as a configuration mechanism has later revealed that this is rarely the best option.

XML tends to be verbose and brittle. When you configure a DI CONTAINER in XML, you identify various classes and interfaces, but you have no compiler support to warn you if you misspell something. Even if the class names are correct, there’s no guarantee that the required assembly is going to be in the application’s probing path.

The advantage of XML configuration is that you can change the behavior of the application without recompilation. This is valuable if you develop software that ships to thousands of customers because it gives them a way to customize the application. However, if you write an internal application or a website where you control the deployment environment, it’s often easier to just recompile and redeploy the application when you need to change the behavior.

 

Tip

Use XML configuration only in those cases where you actively wish to support late binding. Prefer CODE AS CONFIGURATION or AUTO-REGISTRATION in all other cases.

 

A DI CONTAINER is often configured with XML by pointing it at a particular XML file, but sometimes it can also pick up the configuration from the application configuration file. The following example uses the latter option.

Example: configuring the sample commerce application with XML

Because the Unity container is one of the more XML-centric of the DI CONTAINERS covered in this book, it makes sense to use it for an example of XML configuration.

In this example, you’ll configure the sample commerce application from section 2.3. A large part of the task is to apply the configuration outlined in table 3.1, but you must also supply a similar configuration to support composition of the HomeController class. The following listing shows the configuration necessary to get the application up and running.

Listing 3.1. Configuring Unity with XML

As you can see from even this simple code listing, XML configuration tends to be quite verbose. Simple mappings like the one from the IBasketService interface to the BasketService class are easily expressed with a simple register element.

However, as you may recall, some of the concrete classes take a connection string as input, so you need to specify how the value of this string is found . With Unity, you can do that by indicating that you use a custom type converter called ConnectionStringConverter. This converter will look up the value CommerceObjectContext among the standard web.config connection strings and return the connection string with that name.

The rest of the elements repeat these two patterns.

Because Unity can automatically resolve requests for concrete types even if there are no explicit registrations, you don’t need to supply XML elements for HomeController and BasketController.

Loading the configuration into the container is done with a single method call:

container.LoadConfiguration();

The LoadConfiguration method loads the XML configuration from listing 3.1 into the container. With the configuration in place, the container can now resolve requests for HomeController, and so on.

Other DI CONTAINERS also support XML configuration. The exact XML schema is different for each container, but the overall structure tends to be similar.

 

Warning

As your application grows in size and complexity, so will your configuration file if you use configuration-based composition. It can grow to become a real stumbling block because it models coding concepts such as classes, parameters, and such, but without the benefits of the compiler, debugging options, and so forth. Configuration files will tend to become brittle and opaque to errors, so only use this approach when you need late binding.

 

Because of the disadvantages of verbosity and brittleness, you should prefer the other alternatives for configuring containers. CODE AS CONFIGURATION is similar to XML configuration in granularity and concept, but obviously uses code instead of XML.

3.2.2. Configuring containers with code

Perhaps the easiest way to compose an application is to write code that does it. This may seem to go against the whole spirit of DI, because it hard-codes which concrete implementations should be used for all ABSTRACTIONS. However, if done in a COMPOSITION ROOT, it only violates a single of the benefits listed in table 1.1.

The benefit of late binding is lost if DEPENDENCIES are hard-coded, but, as I mentioned in chapter 1, this may not be relevant for all types of applications. If your application is deployed in a limited number of instances in a controlled environment, it may be easier to recompile and redeploy the application if you need to replace modules.

I often think that people are over-eager to define configuration files. Often a programming language makes a straightforward and powerful configuration mechanism.

Martin Fowler[4]

4 Martin Fowler, “Inversion of Control Containers and the Dependency Injection pattern,” 2004, http://martinfowler.com/articles/injection.html

When we use CODE AS CONFIGURATION, we explicitly state the same discrete mappings as we do when we use XML configuration—only, we use code instead of XML.

With the single exception of Spring.NET, all DI CONTAINERS fully support CODE AS CONFIGURATION as an alternative to XML configuration—in fact, most of them present this as the default mechanism with XML configuration as an optional feature.

The API exposed to support CODE AS CONFIGURATION differs from DI CONTAINER to DI CONTAINER, but the overall goal is still to define discrete mappings between ABSTRACTIONS and concrete types.

 

Tip

Prefer CODE AS CONFIGURATION over XML configuration unless you need late binding. The compiler can be helpful and the Visual Studio build system will automatically copy all required DEPENDENCIES to the output folder.

 

Many configuration APIs use generics and Fluent Builders to register components; StructureMap is no exception.

Example: configuring the sample commerce application with code

In section 3.2.1, you saw how to configure the sample commerce application with XML, using Unity. I could also demonstrate CODE AS CONFIGURATION with Unity, but in this example I’ll instead use StructureMap; because it has a terser API, it fits better on the pages of the book.

Using StructureMap’s configuration API, you can express the configuration from listing 3.1 more compactly, as shown in the following listing.

Listing 3.2. Configuring StructureMap with code
c.For<IBasketService>().Use<BasketService>();
c.For<BasketDiscountPolicy>()
    .Use<RepositoryBasketDiscountPolicy>();

string connectionString =
    ConfigurationManager.ConnectionStrings
    ["CommerceObjectContext"].ConnectionString;
c.For<BasketRepository>().Use<SqlBasketRepository>()
    .Ctor<string>().Is(connectionString);
c.For<DiscountRepository>().Use<SqlDiscountRepository>()
    .Ctor<string>().Is(connectionString);
c.For<ProductRepository>().Use<SqlProductRepository>()
    .Ctor<string>().Is(connectionString);
c.For<CurrencyProvider>().Use<SqlCurrencyProvider>()
    .Ctor<string>().Is(connectionString);

Compare this code with listing 3.1 and notice how much more compact it is—even though it does the same thing. A simple mapping like the one from IBasketService to BasketService is expressed with the generic For and Use methods. The c variable is actually something called a ConfigurationExpression, but think about it as the container itself.

To support those classes that require a connection string, you continue the For/ Use sequence with invoking the Ctor method and supplying the connection string. This Ctor method looks for a string parameter in the concrete class’s constructor and uses the supplied value for that parameter.

The rest of the code repeats these two patterns.

Not only is CODE AS CONFIGURATION much more compact than XML configuration, it also enjoys compiler support. The type arguments used in listing 3.2 represent real types that the compiler checks. StructureMap’s fluent API even comes with some generic constraints that tell the compiler to check whether the type identified by the Use method matches the ABSTRACTIONS indicated by the For method. If a conversion isn’t possible, the code will not compile.

Although CODE AS CONFIGURATION is safe and easy to use, it still requires more maintenance than you might like. Every time you add a new type to an application, you must also remember to register it—and many registrations end up being similar. AUTO-REGISTRATION addresses this issue.

3.2.3. Configuring containers by convention

In listing 3.2, did you notice how similar many of the registrations are? Particularly all the SQL Server–based data access components follow a common pattern wherein you configure the component with the appropriate connection string.

Repeatedly writing registration code like that violates the DRY[5] principle. It also seems like an unproductive piece of infrastructure code that doesn’t add much value to the application. You can save time and make fewer errors if you can automate the registration of components.

5 Don’t Repeat Yourself

An increasingly popular architectural model is the concept of Convention over Configuration. Instead of writing and maintaining a lot of configuration code, you can agree on conventions that affect the code base.

The way ASP.NET MVC finds Controllers based on controller names is a great example of a simple convention:

1.  A request comes in for a Controller named Home.

2.  The default Controller Factory searches through a list of well-known namespaces for a class named HomeController. If it finds such a class and it implements IController, it’s a match.

3.  The default Controller Factory uses the default constructor of the matched class to create an instance of the Controller.

At least two conventions are in play here: a controller must be named [ControllerName]Controller and it must have a default constructor.

You can deviate from these conventions by implementing your own IControllerFactory, and that’s what I have done so far to support CONSTRUCTOR INJECTION—I discuss this in more detail in chapter 7.

It would be nice if you could use some conventions to get rid of all that error-prone and time-consuming container configuration. With the DefaultControllerFactory, adding a new Controller is as simple as adding an appropriately named class in the correct namespace. We’d like to keep that convenience even when we use CONSTRUCTOR INJECTION.

Many DI CONTAINERS provide AUTO-REGISTRATION capabilities that allow us to introduce our own conventions.

 

Definition

AUTO-REGISTRATION is the ability to automatically register components in a container by scanning one or more assemblies for implementations of desired ABSTRACTIONS.

 

Conventions can be applied to more than ASP.NET MVC Controllers. The more conventions you add, the more you can automate the various parts of the container configuration.

 

Tip

Convention over Configuration has more advantages than supporting DI configuration. It makes your code more consistent because your code will automatically work as long as you follow your conventions.

 

In reality, you may need to combine AUTO-REGISTRATION with CODE AS CONFIGURATION or XML configuration because you may not be able to fit every single component into a meaningful convention. However, the more you can move your code base towards conventions, the more maintainable it will be.

Example: configuring the sample commerce application with auto-registration

StructureMap supports AUTO-REGISTRATION, but I thought it would be more interesting to use yet another DI CONTAINER to configure the sample commerce application using conventions. I chose Autofac because it has a fairly readable AUTO-REGISTRATION API.

If you consider listings 3.1 and 3.2, I hope you’ll agree that the registrations of the various data access components are the most repetitive. Can we express some sort of convention around them?

All four concrete types share some characteristics:

  • They are all defined in the same assembly.
  • Each is a concrete class inheriting from an abstract base class.
  • Each has a name that starts with Sql.
  • Each has a single public constructor that takes a string parameter called connString.

It seems as though an appropriate convention would express these similarities by scanning the assembly in question and register all classes that match the convention. With Autofac it would look like this:

string connectionString =
    ConfigurationManager.ConnectionStrings
    ["CommerceObjectContext"].ConnectionString;
var a = typeof(SqlProductRepository).Assembly;
builder.RegisterAssemblyTypes(a)
    .Where(t => t.Name.StartsWith("Sql"))
    .As(t => t.BaseType)
    .WithParameter("connString", connectionString);

This particular convention should scan the assembly that contains the data access components. There are many ways you could get a reference to that assembly, but the easiest way is to pick a representative type, such as SqlProductRepository, and get the assembly from that. You could also have chosen a different class or found the assembly by name.

Now that you have the assembly, you can tell the container that you want to scan it. The RegisterAssemblyTypes method indicates an intention to register all types in the assembly that fit the criterion that the class name must start with Sql. The builder variable is an instance of the ContainerBuilder class, but you can think about it as representing the container.

Each of the classes that make it through the Where filter should be registered against their base class. For example, because SqlProductRepository’s base class is ProductRepository, it will end up as a mapping from ProductRepository to SqlProductRepository.

Finally, you state that you expect each constructor to have a connString parameter and that its value should be assigned from the connection string read from the configuration file.

Comparing this convention against the four registrations in listing 3.2 may not be entirely fair, because we’re also holding two different DI CONTAINERS up against each other. Still, you may think that the benefit looks negligible. However, the convention scales much better.

Because there are only four data access components in the current example, you only save a few code statements with the convention. However, once the convention is written, it handles hundreds of components without extra effort.

You can also address the other mappings from listings 3.1 and 3.2 by conventions, but currently there wouldn’t be much value from doing it. As an example, you can register all services by this convention:

builder.RegisterAssemblyTypes(typeof(BasketService).Assembly)
    .Where(t => t.Name.EndsWith("Service"))
    .AsImplementedInterfaces();

This convention scans the identified assembly for all types where the name ends with Service and registers each type against the interfaces it implements. This effectively registers BasketService against the IBasketService interface, but because you currently don’t have any other matches for this convention, nothing much is gained. Still, it may make sense to formulate a convention up front in order to encourage developers to follow it.

AUTO-REGISTRATION is a powerful technique that has the potential to make the DI CONTAINER invisible. Once appropriate conventions are in place, you may only have to modify the container configuration on rare occasions.

So far, you’ve seen three different approaches to configuring a DI CONTAINER:

  • XML
  • CODE AS CONFIGURATION
  • AUTO-REGISTRATION

None of these are mutually exclusive. You can choose to mix AUTO-REGISTRATION with specific mappings of abstract to concrete types, and even mix all three approaches to have some AUTO-REGISTRATION, some CODE AS CONFIGURATION, and some of the configuration in XML for late binding purposes.

As a rule of thumb, you should prefer AUTO-REGISTRATION as a starting point, complemented by CODE AS CONFIGURATION to handle more special cases. You should reserve XML for cases where you need to be able to vary an implementation without recompiling the application (which is rarer than you may think).

Now that we have covered how to configure a DI CONTAINER and how to resolve object graphs with it, you should have a good idea about how to use it. Using a DI CONTAINER is one thing, but using it correctly is another.

3.3. DI Container patterns

DI CONTAINERS are great tools, but, as with all tools, there are correct and incorrect ways of using them. In the same way that cooks know to treat their knives with respect, so should you learn to properly wield your DI CONTAINER—it doesn’t have the potential to lop off your fingers, but you may not harvest the benefits it can provide.

The most important thing to understand is where in the application architecture a DI CONTAINER should be used. Once you understand that, you must also learn how to use it. The following two mini-patterns provide the answers.

3.3.1. Composition Root

Where should we compose object graphs?

AS CLOSE AS POSSIBLE TO THE APPLICATION’S ENTRY POINT.

A DI CONTAINER is a library that you can potentially use from wherever you would like—but that doesn’t mean that you should. Although you can spread out the use of the container so that it permeates a large percentage of your classes, you should instead concentrate it into a single area of your application. This place is called the COMPOSITION ROOT and you should only use a DI CONTAINER from within that place.

Figure 3.6. When composing an application from many loosely coupled classes, the composition should take place as close to the application’s entry point as possible. The COMPOSITION ROOT composes the object graph, which subsequently performs the actual work of the application.

The COMPOSITION ROOT concept isn’t particularly tied to DI CONTAINERS. It also applies if you use POOR MAN’S DI, but I find it important to discuss it in this context because understanding this pattern enables you to use your DI CONTAINER correctly and effectively. Before I discuss the implications of COMPOSITION ROOT on the use of DI CONTAINERS, I’ll briefly talk about it in general.

Composition Root as a general concept

When you write loosely coupled code, you create many classes that you must compose to create an application. It can be tempting to compose these classes a little at a time in order to create small subsystems, but that limits your ability to INTERCEPT those systems to modify their behavior. Instead, you should compose classes all at once.

 

Definition

A COMPOSITION ROOT is a (preferably) unique location in an application where modules are composed together.

 

 

Tip

The COMPOSITION ROOT can be spread out across multiple classes as long as they all reside in a single module.

 

When you look at CONSTRUCTOR INJECTION in isolation you may wonder, doesn’t it just defer the decision about selecting a dependency to another place? Yes, it does, and that’s a good thing; this means that you get a central place where you can connect collaborating classes. The COMPOSITION ROOT acts as a third party that connects consumers with their services. In fact, Nat Pryce prefers the term Third-party Connect over DI[6] for exactly that reason.

6 Nat Pryce, “‘Dependency Injection’ Considered Harmful,” 2011, http://www.natpryce.com/articles/000783.html

The longer you defer the decision on how to connect classes together, the more you keep your options open. Thus, the COMPOSITION ROOT should be placed as close to the application’s entry point as possible.

 

Note

I like to think about COMPOSITION ROOTS as the architectural equivalent to a concept from Lean Software Development:[7] the Last Responsible Moment. The idea is to defer all decisions as long as responsibly possible (but no longer), because we’d like to keep our options open and base our decisions upon as much information as possible. When it comes to composing applications, we can similarly defer the decision about wiring DEPENDENCIES to the application root.

7 See, for example, Mary Poppendieck and Tom Poppendieck, Implementing Lean Software Development: From Concept to Cash (New York: Addison-Wesley, 2007).

 

Even a modular application that uses loose coupling and late binding to compose itself has a root that contains the entry point into the application. Examples are

  • A console application is an executable (.exe) with a Main method.
  • An ASP.NET web application is a library (.dll) with an Application_Start event handler in its Global.asax.
  • A WPF application is an executable (.exe) with an App.xaml file.
  • A WCF service is a library (.dll) with a class that derives from a service interface, although you can hook into a more low-level entry point by creating a custom ServiceHostFactory.

Many other technologies exist, but common to them all is that one module contains the entry point of the application: this is the root of the application, as illustrated in figure 3.7. The COMPOSITION ROOT of the application should be located in the application’s root so that it can properly compose the application.

Figure 3.7. The entry point of an application is the root of a modular application. Either directly or indirectly, the root consumes the other modules. A COMPOSITION ROOT should be placed in the application’s root—as close to the entry point as possible.

You shouldn’t attempt to compose classes in any of the modules because that approach limits your options. All classes in application modules should use CONSTRUCTOR INJECTION (or, in rare cases, one of the other patterns from chapter 4) and leave it up to the COMPOSITION ROOT to compose the application’s object graph. Any DI CONTAINER in use should be limited to the COMPOSITION ROOT.

Using a DI Container in a Composition Root

A DI CONTAINER can be misused as a SERVICE LOCATOR, but it should only be used as an engine that composes object graphs. When you consider a DI CONTAINER in that perspective, it only makes sense to constrain it to the COMPOSITION ROOT. This also has the great benefit of removing any coupling between the DI CONTAINER and the rest of the application code base.

 

Tip

A DI CONTAINER should only be referenced from the COMPOSITION ROOT. All other modules should have no reference to the container.

 

In figure 3.8 you can see that only the COMPOSITION ROOT has a reference to the DI CONTAINER. The rest of the application has no reference to the container and instead relies on the patterns described in chapter 4. DI CONTAINERS understand those patterns and use them to compose the application’s object graph.

Figure 3.8. Only the COMPOSITION ROOT contained in the application root should have a reference to a DI CONTAINER. All other modules in the application should rely entirely on DI patterns and have no reference to the container.

A COMPOSITION ROOT can be implemented with a DI CONTAINER. This means that you use the container to compose the entire application’s object graph in a single call to the Resolve method. Whenever I talk to developers about doing it like this, I can always tell that it makes them uncomfortable because they’re afraid that it’s terribly inefficient and bad for performance. You don’t have to worry about that because it’s almost never the case, and in the very few situations where it is, there are ways to address the issue.[8]

8 Mark Seemann, “Compose object graphs with confidence,” 2011, http://blog.ploeh.dk/2011/03/04/ComposeObjectGraphsWithConfidence.aspx

 

Tip

Don’t worry about the performance overhead of using a DI CONTAINER to compose large object graphs. It’s almost never an issue.

 

When it comes to request-based applications, such as websites and services, you configure the container only once, but resolve an object graph for each incoming request. The sample commerce application is an example of that.

Example: implementing a Composition Root

The sample commerce application from section 2.3 must have a COMPOSITION ROOT to compose object graphs for incoming HTTP requests. As with all other .NET web applications, the entry point is in the Application_Start method in the Global.asax file.

In this example, I use the Castle Windsor DI CONTAINER, but the code would be similar with any other container. With Castle Windsor the Application_Start method could look like this:

protected void Application_Start()
{
    MvcApplication.RegisterRoutes(RouteTable.Routes);

    var container = new WindsorContainer();
    container.Install(new CommerceWindsorInstaller());

    var controllerFactory =
        new WindsorControllerFactory(container);

    ControllerBuilder.Current.SetControllerFactory(
        controllerFactory);
}

Before you can configure the container, you must create a new instance. Because the entire setup for the application is encapsulated in a class called CommerceWindsorInstaller, you install that into the container to configure it. The code in CommerceWindsorInstaller is obviously implemented with Castle Windsor’s API, but, conceptually, it’s identical to the examples in section 3.2.

To enable the container to wire up Controllers in the application, you must employ the appropriate SEAM in ASP.NET MVC, called an IControllerFactory (discussed in detail in section 7.2). For now, it’s enough to understand that to integrate with ASP.NET MVC, you must create an Adapter[9] around the container and tell the framework about it.

9 Erich Gamma et al., Design Patterns: Elements of Reusable Object-Oriented Software (New York: Addison-Wesley, 1994), 139.

Because the Application_Start method only runs once, the container is a single instance which is only initialized a single time. When requests come in, this container instance must handle each concurrently—but because all containers are implemented with thread-safe Resolve methods, that’s not an issue.

Because you set up ASP.NET MVC with the custom WindsorControllerFactory, it will invoke its GetControllerInstance method for each incoming HTTP request (you can read about the details in section 7.2). The implementation delegates the work to the container:

protected override IController GetControllerInstance(
    RequestContext requestContext, Type controllerType)
{
    return (IController)this.container.Resolve(controllerType);
}

Notice that you’re more or less back to the introductory examples from section 3.1.1. The Resolve method composes the complete graph that should be used to serve this particular request and returns it. This is the only place in the application where you invoke the Resolve method.

 

Tip

An application’s code base should only contain a single call to the Resolve method.

 

The COMPOSITION ROOT in this example is spread out across a few classes, as shown in figure 3.9. This is expected—the important thing is that all classes are contained in the same module, which in this case is the application root.

Figure 3.9. The COMPOSITION ROOT is spread across three classes, but they’re all defined within the same module.

The most important thing to notice here is that these three classes are the only classes in the entire sample application that reference the DI CONTAINER. All the rest of the application code only uses the CONSTRUCTOR INJECTION pattern; go back and reread chapter 2 if you don’t believe me.

 

Tip

I like to summarize all the guidance contained in this section by paraphrasing the Hollywood Principle: don’t call the container; it’ll call you.

 

 

Common Service Locator

There’s an open source project called the Common Service Locator (http://commonservicelocator.codeplex.com/) that aims to decouple application code from specific DI CONTAINERS by hiding each container behind a common IServiceLocator interface.

I hope that this explanation of how a COMPOSITION ROOT effectively decouples the rest of the application code from DI CONTAINERS enables you now to understand why you don’t need the Common Service Locator. As I explain in section 5.4, because SERVICE LOCATOR is an anti-pattern, it’s best to stay away from it—and with a COMPOSITION ROOT, you don’t need it, either.

 

You can read specific details on how to implement COMPOSITION ROOTS in various frameworks (including ASP.NET MVC) in chapter 7. In the current context, how you do it is much less important than where you do it. As the name implies, the COMPOSITION ROOT is that part of the application root where you compose all the loosely coupled classes. This is true whether you use a DI CONTAINER or POOR MAN’S DI. However, when you use a DI CONTAINER, you should follow the REGISTER RESOLVE RELEASE pattern.

3.3.2. Register Resolve Release

How should we use a DI Container?

BY FOLLOWING THE STRICT REGISTER RESOLVE RELEASE METHOD CALL SEQUENCE.

The COMPOSITION ROOT pattern describes where you should use a DI CONTAINER. However, it doesn’t state how to use it. The REGISTER RESOLVE RELEASE pattern addresses this question.

A DI CONTAINER should be used in three successive phases called Register, Resolve, and Release. Table 3.3 describes each of the phases in more detail.

Table 3.3. Container phases

Phase

What happens in this phase?

Further reading

Register Register components with the container. You configure the container by informing it about which classes it can use, how it should map ABSTRACTIONS to concrete types, and, optionally, how certain classes should be wired together. In section 3.2, I already discussed how to configure a DI CONTAINER. In part 4, I discuss configuration of six individual DI CONTAINERS in detail.
Resolve Resolve root components. A single object graph is resolved from a request for a single type. In section 3.1, we saw how to resolve object graphs with a DI CONTAINER. In part 4, you can read more about container-specific APIs.
Release Release components from the container. All object graphs resolved in the previous phase should be released when they’re no longer needed. This signals to the container that it can clean up the object graph, which is particularly important if some of the components are disposable. In chapter 8, I discuss LIFETIME MANAGEMENT, including the importance of cleaning up. Additionally, in part 4, I look at LIFETIME MANAGEMENT APIs for individual DI CONTAINERS.

 

Definition

The REGISTER RESOLVE RELEASE pattern states that a DI CONTAINER’S methods must be invoked in this strict sequence: Register, Resolve, and Release (see figure 3.10).

 

Figure 3.10. Container methods should be invoked in the strict sequence shown: first, the Register method, followed by the Resolve method, and terminated by the Release method.

You must use the three phases in the correct order and you’re not allowed to move back and forth willy-nilly. As an example, you shouldn’t go back and reconfigure the container once you have started resolving object graphs. Sometimes people ask about how to add more components to a container after they have started resolving components. Don’t do that—it will only give you grief.

 

Note

Some DI CONTAINERS don’t support explicit Release of object graphs and instead rely on the .NET garbage collector. When using such containers, you must use a modified Register Resolve pattern instead and address the potential resource leaks in your object implementations. See chapter 8 for more details.

 

In the following section I talk about Register, Resolve, and Release methods as well as phases. Castle Windsor actually has three methods with these exact names, and the phases are named after these methods. Other DI CONTAINERS may use different names, but the underlying concept is identical. I only use the Castle Windsor names because they provide a consistent terminology—as well as a nice alliteration.

Static structure

In its pure form, the REGISTER RESOLVE RELEASE pattern states that you should only make a single method call in each phase. Krzysztof Koźmic calls this the Three Calls Pattern[10]—you’re only allowed to make three method calls to the container.

10 Krzysztof Koźmic, “How I use Inversion of Control containers,” 2010, http://kozmic.pl/2010/06/20/how-i-use-inversion-of-control-containers

The Resolve and Release methods make this easy. In section 3.3.1 I already stated that an application should only contain a single call to the Resolve method. As a corollary, you should always Release what you Resolve.

 

Tip

Any object graph composed with the Resolve method should be decommissioned with the Release method.

 

Configuring a DI CONTAINER in a single method call requires more explanation. The reason that registration of components should happen in a single method call is because you should regard configuration of a DI CONTAINER as a single, atomic action. Once configuration is completed, the container should be regarded as read-only.

Autofac even makes this notion explicit by separating configuration of the container out into a distinct ContainerBuilder: you Register components with the ContainerBuilder and when you’re done you ask it to build a container instance from the configuration. In Autofac, you don’t directly configure the container.

By regarding configuration as a single atomic action, it becomes easier to manage the configuration code because it’s evident where it should go. Many DI CONTAINERS also use this concept to freeze the configuration once you start resolving object graphs from it. This makes them perform better.

If you recall listing 3.2, you may argue that it contains more than a single method call. Registration is always going to involve many statements, but most DI CONTAINERS have a packaging mechanism that allows you to encapsulate all those configuration statements into a single class (perhaps composed of other classes). Autofac calls them Modules, StructureMap calls them Registries, and Castle Windsor calls them Installers. Common to them all is that they can be used to configure the container with a single method call. In section 3.3.1, you already saw Castle Windsor using an Installer:

container.Install(new CommerceWindsorInstaller());

For DI CONTAINERS that don’t have a packaging mechanism, you can always create a custom class that encapsulates the configuration in a single method.

The advice that there must only be a single line of code each for Resolve and Release should be taken seriously—but for the Register phase, it should be understood more conceptually. The important point is that registration should be completed before the Resolve method is called. Figure 3.11 illustrates how the sequence looks, including the encapsulation of many Register method calls.

Figure 3.11. Any number of calls to the Register method can take place in the Register phase, but you should still regard it as an atomic action. In the Resolve and Release phase, you literally should have only one invocation of each method.

A common source of confusion is that the Three Calls Pattern makes an adamant statement about how often each method must appear in your code base, but it says nothing about how many times they must be invoked.

Dynamic interaction

The name of the Three Calls Pattern may lead you to believe that each method must only be called once. The source of this confusion lies in the name itself, and this is one of several reasons I prefer the name REGISTER RESOLVE RELEASE.

The Three Calls Pattern states that there must only be a single line of code that invokes each method. However, depending on circumstances, some of the methods may be invoked more than once.

In a single-threaded application such as a desktop application, command-line utility, or batch job, each method will normally only be invoked once, as figure 3.12 illustrates.

Figure 3.12. In a single-threaded application, each method will tend to be invoked only once. Configuration of the container is immediately followed by composing the application’s object graph that performs the actual work. When the work is completed, the Release method is invoked before the application exits.

In a request-based application such as a website, web service, or asynchronous message consumer, the COMPOSITION ROOT composes an object graph for each incoming request. In this type of application, as illustrated in figure 3.13, the Register method is still only invoked once, whereas the Resolve and Release methods are invoked in a pair for each request—a potentially huge number of times.

Figure 3.13. In a request-based application, the Register method is invoked only once, whereas the Resolve and Release methods are invoked many times—once per request.

It’s important to note that you must only configure the container once. The container is a shared instance that’s used to resolve multiple requests, but the configuration must remain stable and complete.

The dynamic picture of REGISTER RESOLVE RELEASE is almost the inverse of the static view—contrast figure 3.11 with figure 3.13. In the static view, we tolerate multiple lines of code that invoke the Register method, but in the dynamic view, this block of code must be invoked exactly once. On the other hand, the static rule is that you must only have one line of code invoking Resolve and Release, but at runtime, these may be called multiple times.

This may sound complicated and difficult, but as the following example demonstrates, it’s only three method calls.

Example: using Register Resolve Release

In this example you’ll implement the COMPOSITION ROOT of the sample application from section 2.3 with the Castle Windsor DI CONTAINER. This is the same container you used in the example in section 3.3.1, so this example can be read as a continuation of the previous.

The entry point of the application is the Application_Start method, and because this is a website, the Register phase is isolated from the Resolve and Release phases because you must only configure the container once. The code is the same as in the previous example, but I want to change the focus a bit:

protected void Application_Start()
{
    MvcApplication.RegisterRoutes(RouteTable.Routes);

    var container = new WindsorContainer();
    container.Install(new CommerceWindsorInstaller());

    var controllerFactory =
        new WindsorControllerFactory(container);

    ControllerBuilder.Current.SetControllerFactory(
        controllerFactory);
}

According to the REGISTER RESOLVE RELEASE pattern, the first method call you make on the container instance should be an atomic Register call. In this case, the method is called Install and the CommerceWindsorInstaller encapsulates the individual registrations in a single class. The following listing shows the implementation of the CommerceWindsorInstaller.

Listing 3.3. Encapsulating multiple registrations

The CommerceWindsorInstaller looks complicated, but the important thing to notice is that it encapsulates four calls to the Register method and that’s the only way it interacts with the container. The rest of the code isn’t important right now. It uses conventions to configure the container. You can read more about Castle Windsor’s AUTO-REGISTRATION API in section 10.1.2.

Because the sample application is a website, Resolve and Release should be implemented as a single pair. For each HTTP request, you must Resolve an object graph that will handle that request, and when it’s completed you must Release it again. You do this from a class called WindsorControllerFactory that derives from ASP.NET MVC’s DefaultControllerFactory—you can read more about the details of the ASP.NET MVC SEAM in section 7.2.

The ASP.NET MVC framework invokes the GetControllerInstance method to resolve IControllers and the ReleaseController method when the request has been handled. These are the appropriate methods for us to invoke the Resolve and Release methods:

protected override IController GetControllerInstance(
    RequestContext requestContext, Type controllerType)
{
    var controller =
        this.container.Resolve(controllerType);
    return (IController)controller;
}

public override void ReleaseController(IController controller)
{
    this.container.Release(controller);
}

In the GetControllerInstance method, you pass the controllerType argument to the Resolve method and return the resulting object graph. When the request has been handled, the ASP.NET MVC framework invokes the ReleaseController method with the IController instance previously created by the GetControllerInstance method, and you can pass that controller instance to the Release method.

Notice that these are the only appearances of the Resolve and Release methods in the entire code base of the application.

This example dug a little deeper than the previous example that demonstrated the COMPOSITION ROOT pattern, but it’s essentially the same code. The COMPOSITION ROOT pattern addresses where you should compose object graphs, whereas REGISTER RESOLVE RELEASE deals with how to use a DI CONTAINER within a COMPOSITION ROOT.

In the next chapter I’ll review more DI patterns, but before I do that I want to take a little detour and discuss how DI CONTAINERS fit into the overall .NET ecosystem.

3.4. DI Container landscape

Now that I have described what a DI CONTAINER is and how to apply it in a COMPOSITION ROOT, I want to change the pace a bit and provide an overview of the current state of DI CONTAINERS in the .NET ecosystem. These are the softer aspects of DI CONTAINERS, such as historical background and why there are so many open source containers available.

Because there’s a plethora of DI CONTAINERS to choose from, I also want to provide a little bit of guidance on how to select a container.

3.4.1. Selecting a DI Container

The decision to use DI as a technique shouldn’t hinge on the choice of a particular DI CONTAINER. DI is first and foremost a technique, and I’ll use POOR MAN’S DI throughout most of parts 2 and 3 to emphasize this point.

Still, a DI CONTAINER will make your life easier, so use one whenever you can. Used according to the patterns outlined in this book, there are few disadvantages to using a container, but there are still things to consider.

Decision process

A DI CONTAINER is a STABLE DEPENDENCY, so from a DI perspective, using one isn’t an issue, but there are other, minor concerns to consider:

  • Adding another library always adds a bit to the complexity of an application—not in terms of maintainability, but in terms of the learning curve. New developers will not only need to learn to understand the application’s code, but also understand the API of the selected DI CONTAINER. In this chapter I hope that I managed to give you the impression that by isolating container usage to a COMPOSITION ROOT you can shield the container from beginners. If you use AUTO-REGISTRATION the container may even take care of the infrastructure for you without calling much attention to itself.
  • With the exception of the Managed Extensibility Framework (MEF), you need to deploy the DI CONTAINER assemblies with your application. This could potentially have legal implications, although this isn’t likely. All common open source DI CONTAINERS have permissive licenses, but I’m not a lawyer, so don’t go and bet your business on my word: consult your own legal advisers.
  • Once more with the exception of MEF, all other DI CONTAINERS are open source libraries. For each you have to assess how much you trust the people or organization behind it.
  • There are technical differences between the various DI CONTAINERS. In the introduction to part 4 I’ve provided a table that lists the advantages and disadvantages of each container covered in this book. You can use this table as a starting point and then read the chapter on each of the containers you find interesting.

Selecting a DI CONTAINER need not be a big deal. Take one for a spin and see if it fits your need—if it doesn’t, then replace it with another. When you constrain the DI CONTAINER to a COMPOSITION ROOT you can replace containers with relative ease.

Selected DI Containers

I won’t tell you which DI CONTAINER to choose. Selecting a DI CONTAINER involves more than technical evaluation. You must also evaluate whether the license model is acceptable, whether you trust the people or organization that develops and maintains the DI CONTAINER, how it fits in to your organization’s IT strategy, and so on.

Most .NET DI CONTAINERS are open source projects—also something to keep in mind, because there may be no official support and often limited documentation.

Table 3.4 lists the DI CONTAINERS covered in part 4 of the book. The selection is based on criteria such as relevance, market share, and distinguishing features, but can never be anything but a subjective and incomplete list. Several popular containers (such as Ninject) aren’t included, mostly due to time and space constraints.

Table 3.4. Selected DI CONTAINERS. More are available, but these are selected either because they’re in widespread usage, because they offer an interesting angle on DI, or because they’re poised to become important in the future.

Name

Organization

Comments

Castle Windsor Open source Mature and widely used
StructureMap Open source Mature and widely used
Spring.NET SpringSource Mature and widely used port of the Java Spring DI CONTAINER
Autofac Open source More recent DI CONTAINER designed around C# 3.0 language features
Unity Microsoft patterns & practices Microsoft’s first play in the DI space, but not a product per se
Managed Extensibility Framework (MEF) Microsoft Ships with .NET 4, but not really a DI CONTAINER

Part 4 is dedicated to these DI CONTAINERS, where each is covered by an entire chapter.

Note how this field is dominated by open source and other non-commercial projects, with Microsoft relegated to a minor role.

3.4.2. Microsoft and DI

Even though the .NET platform is a product of Microsoft, other organizations (often single individuals) are much more prominent when it comes to DI in .NET. In short, this can be attributed to the fact that Microsoft doesn’t offer any DI CONTAINER in the Base Class Library (BCL). Even as a separate offering, Microsoft’s only DI CONTAINER is the relatively recent Unity.

I think it’s fair to say that for the first many years of the .NET Framework’s life, Microsoft blissfully ignored the very concept of DI. It’s not easy to explain exactly why, and I doubt that it was ever an explicit strategy.

A brief history of DI in .NET

Here’s my subjective attempt to outline the history of DI in .NET to explain why Microsoft ignored DI for so long (see figure 3.14). As far as I’m aware, there’s no authoritative answer to that question.[11]

11 Although it doesn’t deal with DI in particular, a good overview of the history of .NET in general can be found in Jon Skeet, C# in Depth, Manning, 2008.

Figure 3.14. Timeline of selected platform and DI CONTAINER releases. Notice how mature Java appeared in 2004 compared to .NET.

Before it hit .NET, DI seems to have grown out of the Java open source community. Martin Fowler published his DI article[12] in early 2004 as a reaction to ongoing work. At that time, .NET 1.1 was the current version, and Microsoft was working on .NET 2.0, while Java was rapidly approaching its decennary. It’s my belief that Microsoft simply had their efforts directed elsewhere. Even if they’d been widely aware of DI at the time, I think they would still have prioritized other features, such as Generics, first.

12 Martin Fowler, “Inversion of Control Containers and the Dependency Injection pattern,” 2004, http://martinfowler.com/articles/injection.html

In June 2004, the StructureMap DI CONTAINER was released, beating Castle Windsor by approximately half a year.

In late 2005, Microsoft released .NET 2.0 with Generics as the major new feature, and then decided to focus on WCF, WPF, and, later, LINQ for their next big release (Visual Studio 2008).

In the meantime, DI slowly gained in popularity. Spring.NET appeared in 2006.

It wasn’t until early 2008, when Microsoft patterns & practices released Unity, that the mainstream Microsoft school of thought seemed to approach DI. That four-year time span gave skilled individuals a good head start, and DI CONTAINERS like StructureMap and Castle Windsor grew in popularity.

DI grassroots

An interesting observation about figure 3.14 is how quickly members of the .NET development community picked up the idea of DI. According to the Castle Windsor homepage, the concepts had been germinating even before Fowler’s article:

Castle was born from the Apache Avalon project, in mid 2003, as an attempt to build a very simple inversion of control container.

Castle web page at 2008.08.01[13]

13http://www.castleproject.org/castle/history.html

For a long time, DI CONTAINERS for .NET remained a grassroots movement, and many leading members were sympathetic to Agile development. In fact, even if modular application architecture has a number of different benefits, it was first and foremost the question of TESTABILITY that seemed to motivate people to develop and use DI CONTAINERS (that was also true in my case).

At that time, the official development methodology at Microsoft was the Microsoft Solutions Framework (MSF) 3.0—a waterfall-style process that leaves little room for Agile development practices such as Test-Driven Development (TDD). In short, it was a completely different mindset.

Over time, Agile development, TDD, and DI have proven effective and gained in popularity, and Microsoft slowly seems to be moving to support that development style as well. In contrast to the situation in 2004, product teams now openly discuss DI, TDD, and other matters related to Agile development.

Microsoft DI Containers

Over the years, the patterns & practices (p&p) team in Microsoft has been developing a lot of proof of concepts for various .NET-related areas. Much of the experience harvested from these projects has been used to define and scope further development of the .NET Framework itself. As an example, the Updater Application Block provided a wealth of experience that was later used when ClickOnce was developed.

In early 2008, p&p released their first Community Technical Preview (CTP) of Unity, their new DI CONTAINER, and release 1.0 followed in April 2008. Unity is a full-fledged DI CONTAINER that supports OBJECT COMPOSITION, LIFETIME MANAGEMENT, and INTERCEPTION. It’s not a Microsoft product, but rather a disclosed source[14] project that just happens to be developed by Microsoft.

14 While the Unity source code is freely available, Microsoft doesn’t accept patches. You can review the source, but not contribute. For that reason I find the term disclosed source more appropriate, as open source normally indicates that contributions can go both ways. However, to be fair the license is permissive, so you can use, modify, and redistribute the source.

 

Object Builder

There seems to be some confusion as to exactly when p&p introduced a DI CONTAINER to the world. When the p&p Enterprise Library for .NET 2.0 was introduced in early 2006 it contained a module called Object Builder that was used to build complex objects from constituent elements.

It was attribute-driven and only worked for classes that integrated tightly with Object Builder itself. It was never introduced as a DI CONTAINER, although it was acknowledged that it might be possible to build a DI CONTAINER on top of it.

Many people mistakenly believe that Object Builder was Microsoft’s first DI CONTAINER, but this isn’t true: Unity has that title.

 

With .NET 4.0, Microsoft delivered the Managed Extensibility Framework (MEF) that marks the first time DI concerns are explicitly being addressed within .NET itself. In its first installment, MEF isn’t a full-fledged DI CONTAINER that supports all three aspects of DI, but rather an engine focused on OBJECT COMPOSITION.

The MEF team is well aware of such aspects as LIFETIME MANAGEMENT and INTERCEPTION, so it’s not unlikely that we’ll see MEF evolve into a fully featured DI CONTAINER over the next few years (as I’m writing this, technical previews indicate that this is indeed the case).

3.5. Summary

A DI CONTAINER can be a tremendously helpful tool if you use it correctly. The most important thing to understand about it is that the use of DI in no way hinges on the use of a DI CONTAINER. An application can be made from many loosely coupled classes and modules, and none of these modules must know anything about a container. The most effective way to make sure that application code is unaware of any DI CONTAINER is to rigidly implement the REGISTER RESOLVE RELEASE pattern in a COMPOSITION ROOT. This effectively prevents you from inadvertently applying the SERVICE LOCATOR anti-pattern because it constrains the container to a small isolated area of the code.

Used in this way, a DI CONTAINER becomes an engine that takes care of a lot of the application’s infrastructure. It composes object graphs based on its configuration. This can be particularly beneficial if you employ CONVENTION-BASED CONFIGURATION—if suitably implemented, it can take care of composing object graphs and you can concentrate your efforts on adding new classes implementing new features. The container will automatically discover new classes that follow the established conventions and make them available to consumers.

In some cases, you need more explicit control over the configuration of the container. It’s most efficient to use CODE AS CONFIGURATION, but if you need to support late binding you can also use XML to configure DI CONTAINERS.

This chapter concludes the first part of the book. The purpose of part 1 was to “put DI on the map.” The previous chapters introduced DI in general, whereas this chapter explained how DI CONTAINERS relate to DI and application design in general. I found it only fitting to round off the chapter with a historical overview of DI CONTAINERS in the .NET ecosystem to really put the various containers on the map.

The chapter introduced COMPOSITION ROOT and REGISTER RESOLVE RELEASE as two mini-patterns that relate to DI CONTAINERS. In the next chapter, we’ll focus on design patterns.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.118.138.195