ASP.NET MVC Tip: Dependency Injection with StructureMap
In this tip, I demonstrate how you can use the dependency
injection with StructureMap within an MVC application.
StructureMap is an open source Dependency Injection
framework for the .NET platform and has been in use since
2004 .StructureMap supports both setter and constructor
injection and also offers testing and diagnostic features
such as logging, tracing and wiring of mock objects. For
download and more details visit
http://structuremap.sourceforge.net/default.htm. Jeremy D Miller’s (Creator of StructureMap)
blog
is a great learning resource for StructureMap and good
object oriented programming thoughts.
If you are not familiar with Dependency Injection (DI)
and Inversion of Control (IoC), read Martin Fowler’s
article
Inversion of Control Containers and the Dependency
Injection pattern
. A Dependency Injection framework injects the
dependencies into a class when the dependencies are
needed. Dependency Injection enables looser coupling
between classes and their dependencies and can be improve
architectural qualities of an object oriented business
application.
Dependency Injection provides the following
advantages,
1) Enables looser coupling between classes and their
dependencies
2) It provides better testability of an application
3) It removes the need for clients to know about their
dependencies and how to create
them.
4) It avoid behind the problems of creating factory
classes, by moving construction responsibility to a
framework.
5) Improve the architecture quality of an object oriented
system.
In this tip, I show how you can use the
StructureMap to perform
Dependency Injection within an
ASP.NET MVC application. In this
tip, I will show you how to perform constructor injection.
Constructor Injection will push dependencies into a
concrete class through constructor arguments.
The below code listings show that a controller class and
its dependent classes.
Listing 1 – CategoryController
public
class
CategoryController
:
Controller
{
ICategoryService _categoryService =
null;
public
CategoryController(ICategoryService
categoryService) {
_categoryService =
categoryService;
}
public
ActionResult
List(int? page) {
var
categories =
_categoryService.GetCategories(page ?? 0, 10);
return
View("CategoryList",categories);
}
Listing 2 – ICategoryService
public
interface
ICategoryService
{
PagedList<Category>
GetCategories(int
pageIndex,int
pageSize);
}
Listing 3 – CategoryService
public
class
CategoryService
:
ICategoryService {
ICategoryRepository
_repository =
null;
public
CategoryService(ICategoryRepository
repository) {
_repository =
repository;
if (_repository == null) {
throw
new
InvalidOperationException("Repository cannot be null");
}
return
_repository.GetCategories().ToPagedList<Category>(pageIndex,
pageSize);
}
}
Listing 4 – ICategoryRepository
public
interface
ICategoryRepository
{
IQueryable<Category>
GetCategories();
}
Listing 5 – CategoryRepository
public
class
CategoryRepository
:
ICategoryRepository {
DBDataContext
_db;
public
CategoryRepository(DBDataContext
dataContext) {
_db =
dataContext;
}
public
IQueryable<Category> GetCategories()
{
return
from
c
in
_db.Categories
select
new
Category {
ID=c.ID,
Name=c.Name,
Description=c.Description,
CategoryType =
c.CategoryType
};
}
}
Depedencies
The
CategoryController
has a dependency with ICategoryService. The concrete implementation of
ICategoryService, i.e.
CategoryService has a dependency with ICategoryRepository. The concrete implementation of
ICategoryRepository, i.e.
CategoryRepository
has a dependency with DBDataContext. When we calling action methods of Category controller, we
need to create objects CategoryService, CategoryRepository
and DBDataContext.
The below steps will configure StructureMap to perform constructor injection in our
ASP.NET MVC application.
Step 1 – Add reference to StructureMap
Add a reference to StructureMap.dll.
Download available from http://sourceforge.net/projects/structuremap/
Step 2 – Add
StructureMap configuration
using
StructureMap;
using
StructureMap.Configuration.DSL;
using
StructureMap.Configuration;
using
StructureMap.Pipeline;
public
class
DomainRegistry
:
Registry {
protected
override
void
configure() {
.TheDefaultIsConcreteType<CategoryService>();
ForRequestedType<ICategoryRepository>()
.TheDefaultIsConcreteType<CategoryRepository>();
}
}
public
class
DBServiceRegistry:Registry
{
protected
override
void
configure() {
ForRequestedType<DBDataContext
>()
.TheDefaultIs(() => new DBDataContext())
.CacheBy(InstanceScope.Hybrid);
}
}
The above configuration tells StructureMap to inject
CategoryService when there is a request for
ICategoryService. And also inject CategoryRepository when
there is a request for ICategoryRepository. The
CategoryRepository class has a dependency with Linq
DataContext DBDataContext. So we configure that create a new instance
DBDataContext when there is request for DBDataContext. we
are telling the StructureMapConfiguration to make hybrid
instance scope. The below are the different type of
instance scoping,
1. PerRequest - The default
operation. A new instance will be
created for each request.
2. Singleton - A single instance
will be shared across all requests
3. ThreadLocal - A single
instance will be created for each requesting thread. Caches the instances with ThreadLocalStorage.
4. HttpContext - A single
instance will be created for each HttpContext. Caches the instances in the HttpContext.Items
collection.
5. Hybrid - Uses HttpContext
storage if it exists, otherwise uses ThreadLocal
storage
Step 3 – Register with StructureMap registry
The below code will add instances of
DomainRegistry
and
DBServiceRegistry into
StructureMapConfiguration registry.
using
StructureMap;
public
static
class
Bootstrapper
{
public
static
void
ConfigureStructureMap()
{
StructureMapConfiguration.AddRegistry(new
DBServiceRegistry());
StructureMapConfiguration.AddRegistry(new
DomainRegistry
());
}
}
Step 4 – Controller Factory for
StructureMap
using
StructureMap;
public
class
StructureMapControllerFactory
:
DefaultControllerFactory
{
protected
override
IController
GetControllerInstance(Type
controllerType) {
try {
return
ObjectFactory.GetInstance(controllerType) as
Controller;
catch (StructureMapException) {
System.Diagnostics.Debug.WriteLine(ObjectFactory.WhatDoIHave());
throw;
}
}
Step 5 – Modify Global.asax.cs
protected
void
Application_Start()
{
RegisterRoutes(RouteTable.Routes);
//Configure StructureMapConfiguration
Bootstrapper.ConfigureStructureMap();
//Set current Controller factory as
StructureMapControllerFactory
ControllerBuilder.Current.SetControllerFactory( new
myFinance.Web.Controllers.StructureMapControllerFactory()
);
}
The above code will set our controller factory and
configure StructureMap configuration when our ASP.NET MVC
application is started.
Summary
In this tip, I demonstrated how you can use the StructureMap to perform Dependency Injection through constructor injection within an ASP.NET MVC application. One of the greatest advantages of ASP.NET MVC is testability. With a Dependency Injection framework, we can make our ASP.NET MVC application is fully testable and maintainable.