Dependency Injection in ASP.NET MVC
Welcome to part one of a two-part series on dependency injection in ASP.NET MVC. Part one of this series will focus on the basics of dependency injection and code structure. In part two, we will dive into the specifics of DI in an ASP.NET MVC application.
Although dependency injection frameworks (DI/IoC containers) have been in use for quite some time in many development platforms, it has grown in popularity in recent years within the .NET community. Many of the early DI frameworks were ports of their Java brethren. Some of those early ports still exist today such as Spring.NET, albeit with several differences from the Java version. Microsoft released their own framework some time ago called Unity, although it's future is uncertain. Microsoft's focus in recent years on solid design patterns such as MVVM in the Silverlight/WPF world and MVC in the more recent incarnations of ASP.NET, has really brought the use of dependency injection into the spotlight as a tool for writing loosely coupled code.
For those unfamiliar with dependency injection or the more general principle of inversion of control, here is a brief example. Suppose we have the following AlbumSearch class. Given an album name, this fictitious class will return a track listing.
As you can see, the FindTracksByAlbum method creates an instance of FreeDBService. Presumably, this FreeDBService goes out on the web and retrieves the appropriate track listing from the FreeDB internet database of CD data. Since we are creating an instance of the FreeDBService directly, we can classify its use as a dependency for our AlbumSearch class.
While functional, there is an issue with this approach. What if we wanted to use the CDDB instead of FreeDB for some or all of our searches? What if we wanted to write a unit test for our Find method that could take advantage of mock data? Why would we want to do either of those two things? Although this is a trivial example, let's assume our AlbumSearch class is part of a larger library that will be consumed by an unknown application. Further, let's say that the unknown application will be responsible for deciding which CD database to use.
How do we fix this? First, we need to make a few simple modifications to our sample code.
Note that we have introduced an interface, IDBService, to represent our track listing service. Plus we have added two implementations of IDBService, one for the FreeDB database and one for the CDDB database. This allows our AlbumSearch class to rely on a generic implementation internally rather than a hard-coded instance of the FreeDBService. Finally, notice that we now have a constructor that requires an instance of IDBService. This is where the magic or “injection” occurs. By structuring our class this way, we have given the consuming application the ability to determine what implementation of IDBService is appropriate.
The consuming application or code can use a variety of techniques and frameworks to determine what implementation of IDBService to use at runtime. This could simply be an implementation of the service locator pattern or preferably some sort of dependency injection framework such as Structure Map, Unity, or Ninject. Modern DI frameworks rely on configuration to determine what the appropriate implementation of a given interface should be at runtime. Configuration is generally xml file based or code-based using some sort of fluent API. If we were using Ninject, you may find a fluent API line like this somewhere in our applications configuration code:
When we actually create an instance of our AlbumSearch class, you may find some code like this:
This is a simplistic example, but you get the point. Within the Ninject Kernel or registry we have basically said that all requests for an instance of IDBService will actually return an instance of FreeDBService. We could have implemented this a multitude of ways, but this is the most straightforward. In part two, we will take a look at a cleaner approach in ASP.NET MVC.
We now have a handle on the general code structure needed to support some simple dependency injection scenarios. Check back for part two of this series to learn how to implement dependency injection in an actual ASP.NET MVC application.