Dependency Injection - Professional ASP.NET MVC 5 (2014)

Professional ASP.NET MVC 5 (2014)

Chapter 13
Dependency Injection

—by Brad Wilson

What's In This Chapter?

· Understanding software design patterns

· Using the dependency resolver in MVC

· Using the dependency resolver in Web API

As of version 3, ASP.NET MVC has included a dependency resolver that dramatically improves the ability of an application to participate in dependency injection for both services consumed by MVC and commonly created classes like controllers and view pages.

To understand how the dependency resolver works, we first need to define some of the common software patterns that it uses. If you're already familiar with patterns such as service location and dependency injection, you might want to skim or skip the next section and go directly to the “Dependency Resolution in MVC” section.

SOFTWARE DESIGN PATTERNS

To understand what dependency injection is and how you can apply it to MVC applications, you need to understand software design patterns. A software design pattern is used to formalize the description of a problem and a solution to that problem, so that developers can use the pattern to simplify the identification and communication of common problems and solutions.

The design pattern isn't necessarily to claim the invention of something new or novel, but rather exists to give a formal name and definition from common practices in the industry. When you read about a design pattern, you might recognize it from solutions you've used in particular problems in the past.

Design Patterns

The concept of patterns and a pattern language is generally credited to Christopher Alexander, Sara Ishikawa, and Murray Silverstein in their book A Pattern Language: Towns, Buildings, and Construction (1977, Oxford University Press). The book presents a view of architecture and urban planning in terms of patterns, which they use to describe problems (and solutions to those problems).

In the software development world, Kent Beck and Ward Cunningham were among the first to adopt the idea of a pattern language, and presented their experiment at the 1987 OOPSLA conference. Perhaps the first and best known comprehensive treatment on core software development patterns was the book Design Patterns: Elements of Reusable Object-Oriented Software (1994, Addison-Wesley Professional). The book is often called the “Gang of Four” (or “GoF”) book, so named because of the four authors: Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides.

Since that time the use of software patterns has exploded, and several volumes of work have been devoted to the subject by such luminaries as Martin Fowler, Alan Shalloway, and James R. Trott.

Design Pattern: Inversion of Control

Everyone has probably seen (or written) code like this:

public class EmailService

{

public void SendMessage() { ... }

}

public class NotificationSystem

{

private EmailService svc;

public NotificationSystem()

{

svc = new EmailService();

}

public void InterestingEventHappened()

{

svc.SendMessage();

}

}

You can see that NotificationSystem has a dependency on EmailService. When a component has a dependency on something else, it's called coupling. In this case, the notification system creates an instance of the e-mail service directly inside of the notification system's constructor; in other words, the notification system knows exactly what kind of service class it's creating and consuming. This coupling is an indication of how interconnected your code is. A class that knows a lot about the other classes it interacts with (as in the preceding example) is said to be tightly coupled.

In software design, tight coupling is often considered to be a liability in your design. When one class knows explicitly about the design and implementation of another class, you raise the risk that changes to one class will break the other class.

Also consider another potential problem with the preceding design: What if the notification system wants to start sending other kinds of messages when the interesting event happens? For example, maybe the administrator of the system wants to start getting text messages instead of e-mails, or also wants to start logging every notification into a database so they can be reviewed at a later time. To enable this behavior, you have to dive back into the implementation of the notification system.

To reduce coupling, you generally take two separate but related steps:

1. Introduce an abstraction layer between two pieces of code.

To perform this step in .NET, you often use interfaces (or abstract classes) to represent the abstractions between two classes. Using the previous example, you introduce an interface to represent your abstraction, and ensure that your code only calls methods or properties on that interface. Your private copy becomes an instance of that interface rather than the concrete type, and you limit the knowledge of the actual type to the constructor, as follows:

public interface IMessagingService

{

void SendMessage();

}

public class EmailService : IMessagingService

{

public void SendMessage() { ... }

}

public class NotificationSystem

{

private IMessagingService svc;

public NotificationSystem()

{

svc = new EmailService();

}

public void InterestingEventHappened()

{

svc.SendMessage();

}

}

2. Move the responsibility of choosing the implementation of the abstraction to outside of the consuming class.

3. You need to move the creation of the EmailService class outside of NotificationSystem.

Note

Moving the creation of dependencies outside of the class that consumes those dependencies is called the inversion of control pattern, so named because what you're inverting here is the creation of dependencies (and in so doing, you are removing the control of dependency creation from the consumer of the class).

The inversion of control (IoC) pattern is abstract; it says that one should move dependency creation out of the consumer class, but it doesn't talk about exactly how to achieve that. The following sections explore two popular ways to apply the inversion of control pattern to achieve this responsibility shift: service locator and dependency injection.

Design Pattern: Service Locator

The service locator pattern says that inversion of control is achieved by having components get their dependencies through an external component known as the service locator. Sometimes a service locator is a very specific interface, with strongly typed requests for specific services, and sometimes it may show up as a very generic way to request services of any arbitrary type.

Strongly Typed Service Locator

A strongly typed service locator for the sample application might have an interface like this:

public interface IServiceLocator

{

IMessagingService GetMessagingService();

}

In this case, when you need an implementation of IMessagingService, you know to call GetMessagingService. The method returns exactly IMessagingService, so you won't need to cast the result.

You'll notice that I'm showing the service locator as an interface here rather than as a concrete type. Remember that one of your goals is to reduce the tight coupling between components; this includes the coupling between the consumer code and the service locator itself. If the consumer code is coded against IServiceLocator, that means you can substitute alternative implementations at run time as appropriate. This can have tremendous value in unit testing, as discussed in Chapter 14.

Now if you rewrite NotificationSystem in terms of the strongly typed service locator, it might look like this:

public class NotificationSystem

{

private IMessagingService svc;

public NotificationSystem(IServiceLocator locator)

{

svc = locator.GetMessagingService();

}

public void InterestingEventHappened()

{

svc.SendMessage();

}

}

This assumes that anybody who creates an instance of NotificationSystem will have access to a service locator. What's convenient is that if your application creates instances of NotificationSystem through the service locator, then the locator can pass itself to theNotificationSystem constructor; if you create instances of NotificationSystem outside of the service locator, you must provide an implementation of the service locator to NotificationSystem so that it can find its dependencies.

Why might you choose a strongly typed service locator? It's fairly easy to understand and consume: You know exactly what kinds of things you can get from this service locator (and, perhaps just as important, what kinds of services you cannot get). Additionally, if you need some parameters to create the implementation of IMessagingService, you can request them directly as parameters to the call to GetMessagingService.

Why might you not choose a strongly typed service locator? One reason is that this service locator is limited to creating objects of types that have been predetermined at the time that IServiceLocator was designed. It's not capable of creating any other types. Another is that it could become a maintenance burden from having to constantly expand the definition of IServiceLocator as you find need for more services in your application.

Weakly Typed Service Locator

If the downsides of a strongly typed service locator seem to outweigh the upsides, you could consider using a weakly typed service locator instead. It might look something like this:

public interface IServiceLocator

{

object GetService(Type serviceType);

}

This variant of the service locator pattern is much more flexible, because it allows you to ask for any arbitrary service type. It's called a weakly typed service locator because it takes a Type and returns an un-typed instance (that is, an object of type Object). You need to cast the result of the call to GetService to get the correctly typed object back.

NotificationSystem with this version of the service locator might look something like this:

public class NotificationSystem

{

private IMessagingService svc;

public NotificationSystem(IServiceLocator locator)

{

svc = (IMessagingService)

locator.GetService(typeof(IMessagingService));

}

public void InterestingEventHappened()

{

svc.SendMessage();

}

}

This code is a little less pretty than the previous version, owing primarily to the required casting to IMessagingService. With the introduction of generics in .NET 2.0, you could have also included a generic version of the GetService method:

public interface IServiceLocator

{

object GetService(Type serviceType);

TService GetService<TService>();

}

The contract for such a method implies that it will return an object already cast to the correct type (notice that its return type is TService now instead of Object). That makes the consuming code quite a bit cleaner:

public class NotificationSystem

{

private IMessagingService svc;

public NotificationSystem(IServiceLocator locator)

{

svc = locator.GetService<IMessagingService>();

}

public void InterestingEventHappened()

{

svc.SendMessage();

}

}

Why Bother with the Object Version?

You might be asking why we even bother having the object version of GetService, rather than just having our API consist of only the generic version. Because it saves us a cast, we will be calling the generic version pretty much everywhere, right?

In practice, you find that not every consumer who calls an API will know the exact type they'll be calling it with at compile time. In an example you'll see later, the MVC framework is trying to create controller types. MVC knows what type the controller is, but it only discovers that at run time, not at compile time (for example, mapping a request for /Home into HomeController). Because the type parameter of the generic version is not only for casting but also for specifying the service type, you would not be able to call the service locator without resorting to reflection.

The downside to this weakly typed service locator approach is that it forces implementers of IServiceLocator to create two nearly identical methods instead of one. This unfortunate duplication of effort can be eliminated with a feature introduced into .NET 3.5: extension methods.

Extension methods are written as static methods on a static class, and utilize the special this keyword on their first parameter to indicate what type this extension method is attached to. Separating the generic GetService method into an extension method yields the following:

public interface IServiceLocator

{

object GetService(Type serviceType);

}

public static class ServiceLocatorExtensions

{

public static TService GetService<TService>(this IServiceLocator locator)

{

return (TService)locator.GetService(typeof(TService));

}

}

This eliminates the duplication and extra effort associated with the generic version of the method. You write it once and everybody can take advantage of your implementation.

Extension Methods in ASP.NET MVC

The MVC framework makes heavy use of extension methods. Most of the HTML helpers that you use to generate forms inside of your views are actually extension methods on the HtmlHelper, AjaxHelper, or UrlHelper class (which are the types of objects you get when you access the Html, Ajax, and Url objects in a view, respectively).

Extension methods in MVC are in their own separate namespace (usually System.Web.Mvc.Html or System.Web.Mvc.Ajax). The MVC team did this because they understood that the HTML generators may not exactly match those that you want for your application. You could write your own HTML generator extension methods, customized to your needs. If you remove MVC's namespace(s) from the Web.config file, none of the built-in extension methods will show up, allowing you to have your own and eliminate MVC's. Or, you may choose to include both. Writing the HTML generators as extension methods gives you the flexibility to decide what's right for your application.

Why might you choose a weakly typed locator? It allows you to fix many of the downsides of the strongly typed locator; that is, you get an interface that can create arbitrary types without knowing about them ahead of time, and it reduces your maintenance burden because the interface is not constantly evolving.

On the other hand, a weakly typed locator interface doesn't communicate anything about the kinds of services that might be requested, and it doesn't offer a simple way to customize the creation of the service. You could add an arbitrary optional array of objects as “creation parameters” for the service, but the only way you know services would require parameters is by way of external documentation.

The Pros and Cons of Service Locators

Using a service locator is relatively straightforward: You get the service locator from somewhere and ask it for your dependencies. You might find the service locator in a known (global) location, or you might get the service locator provided to you by whoever is creating it. As your dependencies change, your signature stays the same, because the only thing you require to find your dependencies is the locator.

The benefit of the constant signature is as much a downside as it is an upside. It creates opacity of requirements for your component: The developers who consume your component can't tell just by looking at the constructor signature what your service requirements are going to be. They are forced to consult documentation, which may be out of date, or simply to pass in an empty service locator and see what kinds of things you request.

This opacity of requirements is a strong driver behind choosing your next IoC pattern: dependency injection.

Design Pattern: Dependency Injection

The dependency injection (DI) pattern is another form of the inversion of control pattern, wherein there is no intermediary object like the service locator. Instead, components are written in a way that allows their dependencies to be stated explicitly, usually by way of constructor parameters or property setters.

Developers who choose dependency injection over service location are often making a conscious decision to choose transparency of requirements over opacity. Choosing the transparency of dependency injection also has significant advantages during unit testing, as discussed in the next chapter.

Constructor Injection

The most common form of dependency injection is called constructor injection. This technique involves creating a constructor for your class that expresses all of its dependencies explicitly (as opposed to the previous service location examples, where your constructor took the service locator as its only constructor parameter).

Now take a look at what NotificationSystem would look like if designed to support constructor injection:

public class NotificationSystem

{

private IMessagingService svc;

public NotificationSystem(IMessagingService service)

{

this.svc = service;

}

public void InterestingEventHappened()

{

svc.SendMessage();

}

}

In this code, the first benefit is that the implementation of the constructor is dramatically simplified. The component is always expecting whoever creates it to pass the required dependencies. It only needs to store the instance of IMessagingService for later use.

Another benefit is that you've reduced the number of things NotificationSystem needs to know about. Previously, it needed to understand service locators in addition to its own dependencies; now, it is focused solely on its own dependencies.

The third benefit, as alluded to previously, is this new transparency of requirements. Any code that wants to create an instance of NotificationSystem can look at the constructor and know exactly what kinds of things are necessary to make NotificationSystem function. There is no guesswork and no indirection through the service locator.

Property Injection

A less common form of dependency injection is called property injection. As the name implies, dependencies for a class are injected by setting public properties on the object rather than through the use of constructor parameters.

A version of NotificationSystem that uses property injection would look like this:

public class NotificationSystem

{

public IMessagingService MessagingService

{

get;

set;

}

public void InterestingEventHappened()

{

MessagingService.SendMessage();

}

}

This code removes the constructor arguments (in fact, it removes the constructor entirely) and replaces it with a property. This class expects any consumers to provide you with your dependencies via properties rather than the constructor.

The InterestingEventHappened method is now slightly dangerous. It presumes that the service dependency has already been provided; if it hasn't, then it will throw a NullReferenceException. You should update the InterestingEventHappened method to ensure that it has been provided with its dependency before using the service:

public void InterestingEventHappened()

{

if (MessagingService == null)

{

throw new InvalidOperationException(

"Please set MessagingService before calling " +

"InterestingEventHappened()."

);

}

MessagingService.SendMessage();

}

It should be obvious that you've slightly reduced your transparency of requirements here; property injection is not quite as opaque as using the service locator, but it's definitely more error prone than constructor injection.

With this reduced transparency, you're probably wondering why a developer would choose property injection over constructor injection. Two situations might warrant that choice:

· If your dependencies are truly optional in the sense that you have some fallback when the consumer doesn't provide you with one, property injection is probably a good choice.

· Instances of your class might be created in such a way that you don't have control over the constructor that's being called. This is a less obvious reason. You'll see a couple of examples of this later in the chapter during the discussion of how dependency injection is applied to view pages.

In general, developers tend to favor using constructor injection whenever possible, falling back to property injection only when one of the preceding reasons dictates. Obviously, you can mix both techniques in a single object: put your mandatory dependencies in as constructor parameters, and your optional dependencies in as properties.

Dependency Injection Containers

One big piece of the puzzle that's missing in both examples of dependency injection is exactly how it takes place. It's one thing to say, “Write your dependencies as constructor arguments,” but it's another to understand how they might be fulfilled. The consumer of your class could manually provide you with all those dependencies, but that can become a pretty significant burden over time. If your entire system is designed to support dependency injection, creating any component means you have to understand how to fulfill everybody's requirements.

Using a dependency injection container is one way to make the resolution of these dependencies simpler. A dependency injection container is a software library that acts as a factory for components, automatically inspecting and fulfilling their dependency requirements. The consumption portion of the API for a dependency injection container looks a lot like a service locator because the primary action you ask it to perform is to provide you with some component, usually based on its type.

The difference is in the details, of course. The implementation of a service locator is typically very simple: You tell the service locator, “If anybody asks for this type, you give them this object.” Service locators are rarely involved in the process of actually creating the object in question. A dependency injection container, on the other hand, is often configured with logic like, “If anybody asks for this type, you create an object of this concrete type and give them that.” The implication is that creating the object of that concrete type will, in turn, often require the creation of other types to fulfill its dependency requirements. This difference, while subtle, makes a fairly large difference in the actual usage of service locators versus dependency injection containers.

More or less, all containers have configuration APIs that allow you to map types (which is the equivalent of saying, “When someone asks for type T1, build an object of type T2 for them.”). Many also allow configuration by name (“When someone asks for the type T1named N1, build an object of type T2.”). Some will even attempt to build arbitrary types, even if they have not been preconfigured, as long as the requested type is concrete and not abstract. A few containers even support a feature called interception, wherein you can set the equivalent of event handlers for when types get created, and/or when methods or properties get called on those objects.

For the purposes of this book, the discussion of the use of these advanced features is beyond our scope. When you have decided on a dependency injection container, you will typically find documentation online that will discuss how to do advanced configuration operations.

DEPENDENCY RESOLUTION IN MVC

Now that you understand the fundamentals of inversion of control, we can talk about how it works inside of ASP.NET MVC.

Note

Although this chapter talks about the mechanics of how to provide services to MVC, it doesn't discuss how to implement any of those specific services; for that, you should consult Chapter 15.

The primary way that MVC talks to containers is through an interface created for MVC applications: IDependencyResolver. The interface is defined as follows:

public interface IDependencyResolver

{

object GetService(Type serviceType);

IEnumerable<object> GetServices(Type serviceType);

}

This interface is consumed by the MVC framework itself. If you want to register a dependency injection container (or a service locator, for that matter), you need to provide an implementation of this interface. You can typically register an instance of the resolver inside your Global.asax file, with code much like this:

DependencyResolver.Current = new MyDependencyResolver();

Using NuGet to Get Your Container

Not having to implement the IDependencyResolver interface on your own, just because you want to use dependency injection, certainly would be ideal. Thankfully, NuGet can come to the rescue here.

NuGet is the package manager included with ASP.NET MVC. It enables you to add references to common open source projects on the Web with almost no effort. For more information on using NuGet, see Chapter 10.

At the time of this writing, a search on NuGet for phrases such as “IoC” and “dependency” shows several dependency injection containers available for download. Many of them have a corresponding MVC support package, which means they come bundled with an implementation of MVC's IDependencyResolver.

Because prior versions of MVC did not have this concept of a dependency resolver, it is considered optional (and there isn't one registered by default). If you don't need dependency resolution support, you are not required to have a resolver. In addition, almost everything that MVC can consume as a service can be registered either inside of the resolver or with a more traditional registration point (and, in many cases, both).

When you want to provide services to the MVC framework, you can choose which registration mode suits you best. MVC generally consults the dependency resolver first when it needs services, and falls back to the traditional registration points when it can't find the service in the dependency resolver.

The code we can't show here is how to register something in the dependency resolver. Why not? Because the registration API that you'll utilize is dependent on which dependency injection container you choose to use. You should consult the container documentation for information on registration and configuration.

You'll notice that two methods are on the dependency resolver interface—that's because MVC consumes services in two different ways.

Should You Consume DependencyResolver in Your Application?

You might be tempted to consume IDependencyResolver from within your own application. Resist that temptation.

The dependency resolver interface is exactly what MVC needs—and nothing more. It's not intended to hide or replace the traditional API of your dependency injection container. Most containers have complex and interesting APIs; in fact, it's likely that you will choose your container based on the APIs and features that it offers more than any other reason.

Singly Registered Services in MVC

MVC has services that it consumes for which the user can register one (and exactly one) instance of that service. It calls these services singly registered services, and the method used to retrieve singly registered services from the resolver is GetService.

For all the singly registered services, MVC consults the dependency resolver for the service the first time it is needed, and caches the result for the lifetime of the application. You can either use the dependency resolver API or the traditional registration API (when available), but you cannot use both because MVC is expecting to use exactly one instance of any singly registered service.

Implementers of GetService should return an instance of the service that is registered in the resolver, or return null if the service is not present in the resolver. Table 13.1 below shows the default service implementations for singly registered MVC services; Table 13.2shows the traditional registration APIs for these services.

Table 13.1 Default Service Implementations for Singly Registered Services in MVC

Service

Default Service Implementation

IControllerActivator

DefaultControllerActivator

IControllerFactory

DefaultControllerFactory

IViewPageActivator

DefaultViewPageActivator

ModelMetadataProvider

DataAnnotationsModelMetadataProvider

Table 13.2 Traditional Registration APIs for Singly Registered Services in MVC

Service

Traditional Registration API

IControllerActivator

None

IControllerFactory

ControllerBuilder.Current
.SetControllerFactory

IViewPageActivator

None

ModelMetadataProvider

ModelMetadataProviders.Current

Multiply Registered Services in MVC

In contrast with singly registered services, MVC also consumes some services where the user can register many instances of the service, which then compete or collaborate to provide information to MVC. It calls these kinds of services multiply registered services,and the method that is used to retrieve multiply registered services from the resolver is GetServices.

For all the multiply registered services, MVC consults the dependency resolver for the services the first time they are needed, and caches the results for the lifetime of the application. You can use both the dependency resolver API and the traditional registration API, and MVC combines the results in a single merged services list. Services registered in the dependency resolver come before services registered with the traditional registration APIs. This is important for those multiply registered services that compete to provide information; that is, MVC asks each service instance one by one to provide information, and the first one that provides the requested information is the service instance that MVC will use.

Implementers of GetServices should always return a collection of implementations of the service type that are registered in the resolver, or return an empty collection if none are present in the resolver.

MVC supports two “multi-service models” for multiply registered services as explained below:

· Competitive services: Those where the MVC framework will go from service to service (in order), and ask the service whether it can perform its primary function. The first service that responds that it can fulfill the request is the one that MVC uses. These questions are typically asked on a per-request basis, so the actual service that's used for each request may be different. An example of competitive services is the view engine service: Only a single view engine will render a view in a particular request.

· Cooperative services: Those where the MVC framework asks every service to perform its primary function, and all services that indicate that they can fulfill the request will contribute to the operation. An example of cooperative services is filter providers: Every provider may find filters to run for a request, and all filters found from all providers will be run.

The following lists show the multiply registered services that MVC uses, including designations to show which are cooperative or competitive.

Service: Filter Provider

1. Interface: IFilterProvider

2. Traditional Registration API: FilterProviders.Providers

3. Multi-service model: cooperative

4. Default Service Implementations:

· FilterAttributeFilterProvider

· GlobalFilterCollection

· ControllerInstanceFilterProvider

Service: Model Binder Provider

1. Interface: IModelBinderProvider

2. Traditional Registration API: ModelBinderProviders.BinderProviders

3. Multi-service model: competitive

4. Default Service Implementations: None

Service: View Engine

1. Interface: IViewEngine

2. Traditional Registration API: ViewEngines.Engines

3. Multi-service model: competitive

4. Default Service Implementations:

· WebFormViewEngine

· RazorViewEngine

Service: Model Validator Provider

1. Type: ModelValidatorProvider

2. Traditional Registration API: ModelValidatorProviders.Providers

3. Multi-service model: cooperative

4. Default Service Implementations:

· DataAnnotationsModelValidatorProvider

· DataErrorInfoModelValidatorProvider

· ClientDataTypeModelValidatorProvider

Service: Value Provider Factory

1. Type: ValueProviderFactory

2. Traditional Registration API: ValueProviderFactories.Factories

3. Multi-service model: competitive

4. Default Service Implementations:

· ChildActionValueProviderFactory

· FormValueProviderFactory

· JsonValueProviderFactory

· RouteDataValueProviderFactory

· QueryStringValueProviderFactory

· HttpFileCollectionValueProviderFactory

Arbitrary Objects in MVC

Two special cases exist where the MVC framework will request a dependency resolver to manufacture arbitrary objects—that is, objects that are not (strictly speaking) services. Those objects are controllers and view pages.

As you saw in the previous two sections, two services called activators control the instantiation of controllers and view pages. The default implementations of these activators ask the dependency resolver to create the controllers and view pages, and failing that, they will fall back to calling Activator.CreateInstance.

Creating Controllers

If you've ever tried to write a controller with a constructor with parameters before, at run time you'll get an exception that says, “No parameterless constructor defined for this object.” In an MVC application, if you look closely at the stack trace of the exception, you'll see that it includes DefaultControllerFactory as well as DefaultControllerActivator.

The controller factory is ultimately responsible for turning controller names into controller objects, so it is the controller factory that consumes IControllerActivator rather than MVC itself. The default controller factory in MVC splits this behavior into two separate steps: the mapping of controller names to types, and the instantiation of those types into objects. The latter half of the behavior is what the controller activator is responsible for.

Custom Controller Factories and Activators

Note that because the controller factory is ultimately responsible for turning controller names into controller objects, any replacement of the controller factory may disable the functionality of the controller activator. In MVC versions prior to MVC 3, the controller activator did not exist, so any custom controller factory designed for an older version of MVC will not know about the dependency resolver or controller activators. If you write a new controller factory, you should consider using controller activators whenever possible.

Because the default controller activator simply asks the dependency resolver to make controllers for you, many dependency injection containers automatically provide dependency injection for controller instances because they have been asked to make them. If your container can make arbitrary objects without preconfiguration, you should not need to create a controller activator; simply registering your dependency injection container should be sufficient.

However, if your dependency injection container does not like making arbitrary objects, it will also need to provide an implementation of the activator. This allows the container to know that it's being asked to make an arbitrary type that may not be known of ahead of time, and allow it to take any necessary actions to ensure that the request to create the type will succeed.

The controller activator interface contains only a single method:

public interface IControllerActivator

{

IController Create(RequestContext requestContext, Type controllerType);

}

In addition to the controller type, the controller activator is also provided with the RequestContext, which includes access to the HttpContext (including things like Session and Request), as well as the route data from the route that mapped to the request. You may also choose to implement a controller activator to help make contextual decisions about how to create your controller objects, because it has access to the context information. One example of this might be an activator that chooses to make different controller classes based on whether the logged-in user is an administrator or not.

Creating Views

Much as the controller activator is responsible for creating instances of controllers, the view page activator is responsible for creating instances of view pages. Again, because these types are arbitrary types that a dependency injection container will probably not be preconfigured for, the activator gives the container an opportunity to know that a view is being requested.

The view activator interface is similar to its controller counterpart:

public interface IViewPageActivator

{

object Create(ControllerContext controllerContext, Type type);

}

In this case, the view page activator is given access to the ControllerContext, which contains not only the RequestContext (and thus HttpContext), but also a reference to the controller, the model, the view data, the temp data, and other pieces of the current controller state.

Like its controller counterpart, the view page activator is a type that is indirectly consumed by the MVC framework, rather than directly. In this instance, it is the BuildManagerViewEngine (the abstract base class for WebFormViewEngine and RazorViewEngine) that understands and consumes the view page activator.

A view engine's primary responsibility is to convert view names into view instances. The MVC framework splits the actual instantiation of the view page objects out into the view activator, while leaving the identification of the correct view files and the compilation of those files to the build manager view engine base class.

ASP.NET's Build Manager

The compilation of views into classes is the responsibility of a component of the core ASP.NET run time called BuildManager. This class has many duties, including converting .aspx and .ascx files into classes for consumption by WebForms applications.

The build manager system is extensible, like much of the ASP.NET core run time, so you can take advantage of this compilation model to convert input files into classes at run time in your applications. In fact, the ASP.NET core run time doesn't know anything about Razor; the ability to compile .cshtml and .vbhtml files into classes exists because the ASP.NET Web Pages team wrote a build manager extension called a build provider.

Examples of third-party libraries that did this were the earlier releases of the SubSonic project, an object-relational mapper (ORM) written by Rob Conery. In this case, SubSonic would consume a file that described a database to be mapped, and at run time it would generate the ORM classes automatically to match the database tables.

The build manager operates during design time in Visual Studio, so any compilation that it's doing is available while writing your application. This includes IntelliSense support inside of Visual Studio.

DEPENDENCY RESOLUTION IN WEB API

The new Web API feature (refer to Chapter 11) also includes the ability to support dependency resolution. The design of the dependency resolver in Web API is slightly different from the one in MVC but, in principle, serves the same purposes: to allow developers to easily get dependency injection for their controllers, as well as making it easy to provide services to Web API that are themselves created via dependency-injection techniques.

There are two significant differences in the dependency resolution implementation in Web API. One is that there are no static APIs for default registration of services; these old static APIs in MVC were there for historical reasons. Instead, there is a loosely typed service locator that can be accessed at HttpConfiguration.Services, where developers can enumerate and replace the default services used by Web API.

Another difference is that the actual dependency resolver API has been modified slightly to support the notion of scopes. One criticism of the original dependency resolver interface in MVC was the lack of any kind of resource-cleanup mechanism. After consultation with the community, we landed on a design that used the concept of a scope as the way that Web API would trigger this cleanup. The system automatically creates a new scope per request, which is available as an HttpRequestMessage extension method namedGetDependencyScope. Like the dependency resolver interface, the scope interface has both GetService and GetServices methods; the difference is that resources acquired from the request-local scope will be released when the request is completed.

Getting or setting the dependency resolver for Web API is done via HttpConfiguration.DependencyResolver.

Singly Registered Services in Web API

Like MVC, Web API has services that it consumes for which the user can register one (and exactly one) instance of that service. The resolver retrieves these singly registered services by calling GetService.

For all the singly registered services, Web API consults the dependency resolver for the service the first time it is needed, and caches the result for the lifetime of the application. When Web API cannot find the service in the resolver, it uses the service found in the default services list in HttpConfiguration.Services. Table 13.3 shows the list of singly registered services that Web API uses.

Table 13.3 Singly Registered Services in Web API

Service

Default Service Implementation

IActionValueBinder

DefaultActionValueBinder

IApiExplorer

ApiExplorer

IAssembliesResolver

DefaultAssembliesResolver*

IBodyModelValidator

DefaultBodyModelValidator

IContentNegotiator

DefaultContentNegotiator

IDocumentationProvider

None

IHostBufferPolicySelector

None

IHttpActionInvoker

ApiControllerActionInvoker

IHttpActionSelector

ApiControllerActionSelector

IHttpControllerActivator

DefaultHttpControllerActivator

IHttpControllerSelector

DefaultHttpControllerSelector

IHttpControllerTypeResolver

DefaultHttpControllerTypeResolver**

ITraceManager

TraceManager

ITraceWriter

None

ModelMetadataProvider

CachedDataAnnotationsModel-MetadataProvider

* When the application is running in ASP.NET, this is replaced by WebHostAssembliesResolver.

** When the application is running in ASP.NET, this is replaced by WebHostHttpControllerTypeResolver.

Multiply Registered Services in Web API

Again borrowing the concepts from MVC, Web API has multiply registered services, and combines the list of those services from the dependency resolver with the list in HttpConfiguration.Services. To retrieve the services from the dependency resolver, Web API calls the GetServices method. The following lists show the multiply registered services that Web API uses, and whether those services are cooperative or competitive.

Service: Filter Provider

1. Interface: IFilterProvider

2. Multi-service model: cooperative

3. Default Service Implementations:

· ConfigurationFilterProvider

· ActionDescriptorFilterProvider

Service: Model Binder Provider

1. Type: ModelBinderProvider

2. Multi-service model: competitive

3. Default Service Implementations:

· TypeConverterModelBinderProvider

· TypeMatchModelBinderProvider

· KeyValuePairModelBinderProvider

· ComplexModelDtoModelBinderProvider

· ArrayModelBinderProvider

· DictionaryModelBinderProvider

· CollectionModelBinderProvider

· MutableObjectModelBinderProvider

Service: Model Validator Provider

1. Type: ModelValidatorProvider

2. Multi-service model: cooperative

3. Default Service Implementations:

· DataAnnotationsModelValidatorProvider

· DataMemberModelValidatorProvider

· InvalidModelValidatorProvider

Service: Value Provider Factory

1. Type: ValueProviderFactory

2. Multi-service model: competitive

3. Default Service Implementations:

· QueryStringValueProviderFactory

· RouteDataValueProviderFactory

Arbitrary Objects in Web API

Three special cases exist where the Web API framework will request a dependency resolver to manufacture arbitrary objects—that is, objects that are not (strictly speaking) services. Like MVC, controllers are one class of these objects. The other two are model binders attached with the [ModelBinder] attribute and the per-controller services that are attached to controllers via [HttpControllerConfiguration].

The services attached via the attributes are cached for the lifetime of the application, just like the built-in services, which means Web API will request them from the dependency resolver attached to the configuration. Controllers, on the other hand, typically have request-scoped lifetimes, so they are requested from the scope that's attached to the request.

Dependency Resolvers in MVC vs. Web API

Although MVC and Web API share the idea of dependency resolvers, the actual interfaces are different, as described previously. In addition, the actual services that might be contained in those dependency resolvers are different, because MVC and Web API share no common service interfaces, either. That means that the implementation of the two dependency resolver interfaces differ, and you shouldn't expect an MVC dependency resolver to work in Web API (or vice versa).

That said, having those two dependency resolver interface implementations backed by the same concrete dependency injection container so that any custom services you use throughout your application would be available to both MVC and Web API controllers is perfectly reasonable. You should consult the documentation for your dependency injection container to determine how to use a single container for an application that includes both MVC and Web API.

SUMMARY

The dependency resolvers in ASP.NET MVC and Web API enable several new and exciting opportunities for dependency injection in your web applications. This can help you design applications that reduce tight coupling and encourage better “plugability,” which tends to lead to more flexible and powerful application development.