• No results found

Register Resolve Release

In document Manning Dependency Injection in .NET (Page 113-119)

A comprehensive example

3.3 DI Container patterns

3.3.2 Register Resolve Release

How should we use a DI Container?

BYFOLLOWINGTHESTRICT REGISTER RESOLVE RELEASEMETHODCALLSEQUENCE.

The COMPOSITION ROOT pattern describes where you should use a DI CONTAINER. How-

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

DEFINITION The REGISTER RESOLVE RELEASE pat-

tern states that a DI CONTAINER’S methods must

be invoked in this strict sequence: Register,

Resolve, and Release (see figure 3.10).

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 recon- figure the container once you have started resolving object graphs. Sometimes people ask about how to add

Common Service Locator

There’s an open source project called the Common Service Locator (http://common- servicelocator.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.

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

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.

STATICSTRUCTURE

In its pure form, the REGISTER RESOLVE RELEASE pattern states that you should only

make a single method call in each phase. Krzysztof Koz´mic calls this the Three Calls Pat-

tern10—you’re only allowed to make three method calls to the container.

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 corol- lary, you should always Release what you Resolve.

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 ABSTRAC- TIONS 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 MAN- AGEMENT, including the importance of cleaning up.

Additionally, in part 4, I look at LIFETIME MANAGEMENT APIs for individ- ual DI CONTAINERS.

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

83

DI Container patterns

TIP Any object graph composed with the Resolve method should be decom- missioned 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 con- tainer 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 under- stood more conceptually. The important point is that registration should be com- pleted before the Resolve method is called. Figure 3.11 illustrates how the sequence looks, including the encapsulation of many Register method calls.

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.

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.

DYNAMICINTERACTION

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.

In a request-based application such as a website, web service, or asynchronous mes- sage 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.

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

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.

Figure 3.13 In a request-based application, the Register method is invoked only once, whereas the

85

DI Container patterns

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 demon- strates, 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 regis- trations in a single class. The following listing shows the implementation of the CommerceWindsorInstaller.

public class CommerceWindsorInstaller : IWindsorInstaller {

public void Install(IWindsorContainer container, IConfigurationStore store) { container.Register(AllTypes .FromAssemblyContaining<HomeController>() .BasedOn<IController>() .Configure(r => r.LifeStyle.PerWebRequest));

Listing 3.3 Encapsulating multiple registrations

Register calls

container.Register(AllTypes .FromAssemblyContaining<BasketService>() .Where(t => t.Name.EndsWith("Service")) .WithService .FirstInterface()); container.Register(AllTypes .FromAssemblyContaining<BasketDiscountPolicy>() .Where(t => t.Name.EndsWith("Policy")) .WithService

.Select((t, b) => new[] { t.BaseType })); string connectionString = ConfigurationManager.ConnectionStrings ["CommerceObjectContext"].ConnectionString; container.Register(AllTypes .FromAssemblyContaining<SqlProductRepository>() .Where(t => t.Name.StartsWith("Sql")) .WithService

.Select((t, b) => new[] { t.BaseType }) .Configure(r => r.LifeStyle.PerWebRequest .DependsOn((new { connString = connectionString })))); } }

The CommerceWindsorInstaller looks complicated, but the important thing to notice is that it encapsulates four calls to the Register method

B

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 Wind- sor’s AUTO-REGISTRATIONAPI in section 10.1.2.

Because the sample application is a website, Resolve and Release should be imple- mented 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.NETMVC’s DefaultControllerFactory—you can read more about the details of the ASP.NET MVCSEAM 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) {

Register calls

87

DI Container landscape

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

In document Manning Dependency Injection in .NET (Page 113-119)