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.