Managing Controller Dependencies
The term dependency or coupling is used to denote the degree to which a software component depends on another. The controller class that you create is one of the important components in ASP.NET Web API, since it is the business end where all the application-specific action happens.
ASP.NET Web API uses the same convention-over-configuration programming model as ASP.NET MVC. One of the great things—maybe the greatest thing—about ASP.NET MVC compared to ASP.NET Web Forms is the ease with which controller classes can be unit-tested without spinning the wheels of ASP.NET infrastructure. In that aspect, ASP.NET Web API is as testable as ASP.NET MVC. However, you need to pay attention to the dependencies your controller classes take, as you design and construct your service to ensure testability.
A very common dependency that an ASP.NET Web API controller takes is the dependency on the classes related to persistence infrastructure such as a database. If your controller classes are tightly coupled to the database, unit tests you write will start exercising the database, and they will no longer be unit tests but become integration tests. In this chapter, I’ll show you how to use the Entity Framework, which is the Microsoft-recommended technology for data access with ASP.NET Web API in a loosely coupled way. The Entity Framework is an object-relational mapper (ORM) that enables .NET developers to work with a relational database such as Microsoft SQL Server using normal CLR objects.
7.1 Taking Dependency on the Entity Framework
In this exercise, you will use Microsoft SQL Server 2012 as the persistence store for your web API. You will use Entity Framework as the ORM and adopt the simplest approach, which is to use the Entity Framework classes directly inside your controller class and thereby take a hard dependency on EF. For this exercise, I’ve used Microsoft SQL Server 2012 Developer Edition but you can also use Express Edition.
It is quite possible for you to work with the existing tables and the data using the Entity Framework’s Code First approach. Code First allows you to define your classes and map the classes and properties to tables and columns using a nice fluent API. You will follow this approach in this exercise and create two entities: Employee and Department.
Figure 7-1. A blank solution
Listing 7-1. Domain Classes
public class Employee
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
// Foreign key association
public int DepartmentId { get; set; }
// Independent association
public virtual Department Department { get; set; }
public byte[] RowVersion { get; set; }
}
public class Department
{
public int Id { get; set; }
public string Name { get; set; }
public byte[] RowVersion { get; set; }
}
Listing 7-2. Configuration Classes
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
using TalentManager.Domain;
public class EmployeeConfiguration : EntityTypeConfiguration<Employee>
{
public EmployeeConfiguration()
{
HasKey(k => k.Id);
Property(p => p.Id)
.HasColumnName("employee_id")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(p => p.FirstName).HasColumnName("first_name");
Property(p => p.LastName).HasColumnName("last_name");
Property(p => p.DepartmentId).HasColumnName("department_id");
HasRequired(x => x.Department);
Property(p => p.RowVersion).HasColumnName("row_version").IsRowVersion();
}
}
using System.ComponentModel.DataAnnotations.Schema;
using System.Data.Entity.ModelConfiguration;
using TalentManager.Domain;
public class DepartmentConfiguration : EntityTypeConfiguration<Department>
{
public DepartmentConfiguration()
{
HasKey(k => k.Id);
Property(p => p.Id)
.HasColumnName("department_id")
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Property(p => p.Name).HasColumnName("name");
Property(p => p.RowVersion).HasColumnName("row_version")
.IsRowVersion();
}
}
Listing 7-3. The Context Class
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using TalentManager.Data.Configuration;
using TalentManager.Domain;
public class Context : DbContext
{
public Context() : base("DefaultConnection") { }
public DbSet<Employee> Employees { get; set; }
public DbSet<Department> Departments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions
.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations
.Add(new EmployeeConfiguration())
.Add(new DepartmentConfiguration());
}
}
Listing 7-4. The Application_Start Method
using System.Data.Entity;
using System.Web.Http;
using System.Web.Mvc;
using System.Web.Optimization;
using System.Web.Routing;
using TalentManager.Data;
public class WebApiApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
Database.SetInitializer<Context>(null);
}
}
Listing 7-5. A Connection String in Web.Config
<connectionStrings>
<add name="DefaultConnection" providerName="System.Data.SqlClient"
connectionString="Server=MyServer;Database=talent_manager;
User Id=appuser;Password=p@ssw0rd!;" />
</connectionStrings>
Listing 7-6. The EmployeesController Class
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using TalentManager.Data;
using TalentManager.Domain;
public class EmployeesController : ApiController
{
private Context context = new Context();
public HttpResponseMessage Get(int id)
{
var employee = context.Employees.FirstOrDefault(e => e.Id == id);
if (employee == null)
{
var response = Request.CreateResponse(HttpStatusCode.NotFound,
"Employee not found");
throw new HttpResponseException(response);
}
return Request.CreateResponse<Employee>(HttpStatusCode.OK, employee);
}
protected override void Dispose(bool disposing)
{
if(context != null)
context.Dispose();
base.Dispose(disposing);
}
}
Figure 7-2. The structure of our tabless
Figure 7-3. The Relationship between our tables
Listing 7-7. SQL Statements
INSERT INTO dbo.department (name) VALUES('HR'),
INSERT INTO dbo.department (name) VALUES('Finance'),
INSERT INTO dbo.department (name) VALUES('Marketing'),
INSERT INTO dbo.employee (first_name, last_name, department_id)
VALUES('John', 'Human', 1);
INSERT INTO dbo.employee (first_name, last_name, department_id)
VALUES('Joe', 'Border', 1);
INSERT INTO dbo.employee (first_name, last_name, department_id)
VALUES('Pete', 'Callaghan', 1);
INSERT INTO dbo.employee (first_name, last_name, department_id)
VALUES('Alan', 'Dime', 2);
INSERT INTO dbo.employee (first_name, last_name, department_id)
VALUES('Rich', 'Nickel', 2);
INSERT INTO dbo.employee (first_name, last_name, department_id)
VALUES('Nick', 'Greenback', 2);
{
"Department": {
"Id": 1,
"Name": "HR",
"RowVersion": "AAAAAAAAB+E="
},
"Id": 1,
"FirstName": "Johnny",
"LastName": "Human",
"DepartmentId": 1,
"RowVersion": "AAAAAAAAF3U="
}
7.2 Inverting Entity Framework Dependencies
In this exercise, you will invert the dependency EmployeesController has taken on the Entity framework. The dependency here is that the EmployeesController class has full knowledge of the Context class (which derives from the Entity Framework’s DbContext). EmployeesController is tightly coupled with the Context class. Imagine you have to move away from the Entity Framework for some reason. With the implementation we have currently, you would need to rewrite the Get action method. So the EmployeesController class has a hard dependency on the Entity Framework. Also, as you start writing unit tests for the action method of the EmployeesController class, they will all exercise the database. As you unit-test the Get method, the data will be retrieved from the database. Not only is that unnecessary overhead in most cases, it also slows down things. The unit tests take more time to run, and if the database is down all the tests will fail. Effectively, such unit tests are not unit tests at all. They are integration tests.
In object-oriented programming, the dependency inversion principle is one of the SOLID 1 design principles. According to this principle, high-level modules should not depend on low-level modules, and both should depend on abstractions. In our case, the high-level module is the EmployeesController class. It depends on the low-level module, which is the Context class.
What we want is for the EmployeesController class not to depend on the Context class but to depend only on an abstraction. The abstraction will be an interface. Let’s call it IContext. The EmployeesController class and the Context class will depend only on the IContext interface. Though the EmployeesController class depends on the Context class, what it really needs is DbSet<T>, which implements the IDbSet<T> interface. We will use this to invert the dependency the EmployeesController class has on the Context class.
Listing 7-8. The IContext Interface
using System.Data.Entity;
using TalentManager.Domain;
public interface IContext
{
IDbSet<Employee> Employees { get; set; }
IDbSet<Department> Departments { get; set; }
}
Listing 7-9. The Modified Context Class Implementing IContext
public class Context : DbContext, IContext
{
public Context() : base("DefaultConnection") { }
public IDbSet<Employee> Employees { get; set; }
public IDbSet<Department> Departments { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations
.Add(new EmployeeConfiguration())
.Add(new DepartmentConfiguration());
}
}
Listing 7-10. The Dependency-Inverted EmployeesController Class
public class EmployeesController : ApiController
{
// private Context context = new Context();
private IContext context = null;
public EmployeesController()
{
this.context = new Context();
}
public EmployeesController(IContext context)
{
this.context = context;
}
public HttpResponseMessage Get(int id)
{
var employee = context.Employees.FirstOrDefault(e => e.Id == id);
if (employee == null)
{
var response = Request.CreateResponse(HttpStatusCode.NotFound, "Employee not found");
throw new HttpResponseException(response);
}
return Request.CreateResponse<Employee>(HttpStatusCode.OK, employee);
}
protected override void Dispose(bool disposing)
{
//if (context != null)
// context.Dispose();
if (context != null && context is IDisposable)
{
((IDisposable)context).Dispose();
}
base.Dispose(disposing);
}
}
A leaky abstraction is one that intends to hide the complexity of the underlying details but is not very successful in doing so; the underlying details are not completely hidden and leak through the abstraction. The IDbSet interface, specifically its parent IQueryable, is considered a leaky abstraction. For example, the query we use is FirstOrDefault(e => e.Id == id). In this case, id is an integer but assume it is a string. The query will return an employee based on the case-sensitivity of the database. A database can be created to be case-sensitive or not. So, if you query for an ID of johnh and the database stores the ID as JohnH, you will get the employee back if the database is case-insensitive. Your unit test has succeeded so far based on this assumption that an ID of johnh will not return any data, but it will be proved wrong once you hit the database. Leaky abstraction is an important consideration when you are designing to invert the controller dependencies using the technique covered in Exercise 7-2.
7.3 Using the Repository Pattern
In this exercise, you will use the repository pattern to invert the dependency that EmployeesController has on the Entity Framework. Here is the definition of the repository pattern, as defined in the Catalog of Patterns of Enterprise Application Architecture by Martin Fowler: it "mediates between the domain and data mapping layers using a collection-like interface for accessing domain objects" (source: http://martinfowler.com/eaaCatalog/repository.html).
The application of the repository pattern over an object-relational mapper (ORM) is a topic for debate and there are arguments for and against the usage of the repository pattern. The major argument against using this pattern with an ORM is that ORM itself is an abstraction from the database, and why do we need another abstraction over ORM? We will nonetheless apply the pattern to see how it helps us in our objective of inverting the dependency that EmployeesController has on the Entity Framework.
Listing 7-11. The Repository Interface
using System;
using System.Collections.Generic;
using TalentManager.Domain;
public interface IEmployeeRepository : IDisposable
{
IEnumerable<Employee> GetByDepartment(int departmentId);
Employee Get(int id);
}
Listing 7-12. The Repository Implementation
using System;
using System.Collections.Generic;
using System.Linq;
using TalentManager.Domain;
public class EmployeeRepository : IEmployeeRepository
{
private Context context = new Context();
public IEnumerable<Employee> GetByDepartment(int departmentId)
{
return context.Employees.Where(e => e.DepartmentId == departmentId);
}
public Employee Get(int id)
{
return context.Employees.FirstOrDefault(e => e.Id == id);
}
private bool disposed = false;
protected virtual void Dispose(bool disposing)
{
if (!this.disposed)
{
if (disposing)
{
if(context != null)
context.Dispose();
}
}
this.disposed = true;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
}
Listing 7-13. EmployeesController Using Repository
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using TalentManager.Data;
using TalentManager.Domain;
public class EmployeesController : ApiController
{
private readonly IEmployeeRepository repository = null;
public EmployeesController()
{
this.repository = new EmployeeRepository();
}
public EmployeesController(IEmployeeRepository repository)
{
this.repository = repository;
}
public HttpResponseMessage Get(int id)
{
var employee = repository.Get(id);
if (employee == null)
{
var response = Request.CreateResponse(HttpStatusCode.NotFound, "Employee not found");
throw new HttpResponseException(response);
}
return Request.CreateResponse<Employee>(HttpStatusCode.OK, employee);
}
public HttpResponseMessage GetByDepartment(int departmentId)
{
var employees = repository.GetByDepartment(departmentId);
if (employees != null && employees.Any())
{
return Request.CreateResponse<IEnumerable<Employee>>(HttpStatusCode.OK, employees);
}
throw new HttpResponseException(HttpStatusCode.NotFound);
}
protected override void Dispose(bool disposing)
{
if(repository != null)
repository.Dispose();
base.Dispose(disposing);
}
}
Even with the refactoring we have done to implement the repository pattern, EmployeesController continues to work perfectly, returning the list of employees from a department as well as individual employees based on ID. Except for the parameterless constructor, the controller has no dependency on any database-related infrastructure. Of course, the dependency the parameterless constructor has can be fixed by using an IoC, which we will look at later in this chapter. By moving the code closely related to the database to repository classes, controllers can be fully unit-tested. The repository classes can be integration-tested.
An important point to note is that this simplistic repository approach can result in a proliferation of classes with one repository per entity. It might not be a concern for simple projects, though.
7.4 Using the Generic Repository Pattern
In this exercise, you will use the generic repository pattern to invert the dependency EmployeesController has on the Entity Framework. A generic repository uses .NET generics and is generic enough to be used with any entity.
Listing 7-14. The IIdentifiable Interface
public interface IIdentifiable
{
int Id { get; }
}
Listing 7-15. Modified Employee and Department Domain Classes
public class Employee : IIdentifiable
{
// Existing properties remain unchanged
}
public class Department : IIdentifiable
{
// Existing properties remain unchanged
}
Listing 7-16. The IRepository Interface
using System;
using System.Linq;
using System.Linq.Expressions;
using TalentManager.Domain;
public interface IRepository<T> : IDisposable where T : class, IIdentifiable
{
IQueryable<T> All { get; }
IQueryable<T> AllEager(params Expression<Func<T, object>>[] includes);
T Find(int id);
void Insert(T entity);
void Update(T entity);
void Delete(int id);
}
Listing 7-17. IMyContext and IUnitOfWork Interfaces
public interface IMyContext : IDisposable
{
int SaveChanges();
}
public interface IUnitOfWork : IDisposable
{
int Save();
IMyContext Context { get; }
}
Listing 7-18. The Context Class
using System.Data.Entity;
using System.Data.Entity.ModelConfiguration.Conventions;
using TalentManager.Data.Configuration;
public class MyContext : DbContext, IMyContext
{
static MyContext()
{
Database.SetInitializer<MyContext>(null);
}
public MyContext() : base("DefaultConnection") { }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Configurations
.Add(new EmployeeConfiguration())
.Add(new DepartmentConfiguration());
}
}
Listing 7-19. The UnitOfWork Class
public class UnitOfWork : IUnitOfWork
{
private readonly IMyContext context;
public UnitOfWork()
{
context = new MyContext();
}
public UnitOfWork(IMyContext context)
{
this.context = context;
}
public int Save()
{
return context.SaveChanges();
}
public IMyContext Context
{
get
{
return context;
}
}
public void Dispose()
{
if (context != null)
context.Dispose();
}
}
Listing 7-20. The Repository Class
using System;
using System.Data;
using System.Data.Entity;
using System.Linq;
using System.Linq.Expressions;
using TalentManager.Domain;
public class Repository<T> : IRepository<T> where T : class, IIdentifiable
{
private readonly MyContext context;
public Repository(IUnitOfWork uow)
{
context = uow.Context as MyContext;
}
public IQueryable<T> All
{
get
{
return context.Set<T>();
}
}
public IQueryable<T> AllEager(params Expression<Func<T, object>>[] includes)
{
IQueryable<T> query = context.Set<T>();
foreach (var include in includes)
{
query = query.Include(include);
}
return query;
}
public T Find(int id)
{
return context.Set<T>().Find(id);
}
public void Insert(T item)
{
context.Entry(item).State = EntityState.Added;
}
public void Update(T item)
{
context.Set<T>().Attach(item);
context.Entry(item).State = EntityState.Modified;
}
public void Delete(int id)
{
var item = context.Set<T>().Find(id);
context.Set<T>().Remove(item);
}
public void Dispose()
{
if(context != null)
context.Dispose();
}
}
Listing 7-21. The Revised EmployeesController Class (Incomplete)
public class EmployeesController : ApiController
{
private readonly IUnitOfWork uow = null;
private readonly IRepository<Employee> repository = null;
public EmployeesController()
{
uow = new UnitOfWork();
repository = new Repository<Employee>(uow);
}
public EmployeesController(IUnitOfWork uow, IRepository<Employee> repository)
{
this.uow = uow;
this.repository = repository;
}
// Action methods go here
protected override void Dispose(bool disposing)
{
if(repository != null)
repository.Dispose();
if(uow != null)
uow.Dispose();
base.Dispose(disposing);
}
}
Listing 7-22. The EmployeesController Class, Continued (Action Methods)
public HttpResponseMessage Get(int id)
{
var employee = repository.Find(id);
if (employee == null)
{
var response = Request.CreateResponse(HttpStatusCode.NotFound,
"Employee not found");
throw new HttpResponseException(response);
}
return Request.CreateResponse<Employee>(HttpStatusCode.OK, employee);
}
public HttpResponseMessage GetByDepartment(int departmentId)
{
var employees = repository.All.Where(e => e.DepartmentId == departmentId);
if (employees != null && employees.Any())
{
return Request.CreateResponse<IEnumerable<Employee>>(
HttpStatusCode.OK, employees);
}
throw new HttpResponseException(HttpStatusCode.NotFound);
}
public HttpResponseMessage Post(Employee employee)
{
repository.Insert(employee);
uow.Save();
var response = Request.CreateResponse<Employee>(HttpStatusCode.Created, employee);
string uri = Url.Link("DefaultApi", new { id = employee.Id });
response.Headers.Location = new Uri(uri);
return response;
}
public void Put(int id, Employee employee)
{
repository.Update(employee);
uow.Save();
}
public void Delete(int id)
{
repository.Delete(id);
uow.Save();
}
In this exercise we have implemented CRUD operations with SQL Server as the persistence store, with the help of the Entity Framework. Most importantly, if you review the EmployeesController code, you will see that it has no dependency on any of the classes related to the Entity Framework. All the operations are through the contracts of IUnitOfWork and IRepository<Employee>.
Note Since we expose IQueryable (a leaky abstraction) to the controller, you need to be careful about the queries that you write. A weakness of the generic repository is that it makes the IRepository contract so generic that it becomes possible to write any kind of queries. If you write complex queries or make use of lazy loading, as we implicitly do here, you need to be careful about what you do. While having tight APIs exposed through type-specific repositories, as we did in Exercise 7-3, prevents the abstraction from leaking, maintenance can be difficult with so many classes. On the other hand, having a generic repository minimizes the number of classes, but using IQueryable means that the abstraction is no longer tight. By adopting a hybrid approach of using a generic repository for simple operations (which is unit-tested) and using a type-specific repository that exposes a tight API (which is integration-tested with the database), you can achieve optimal effect.
7.5 Mapping a Domain to Data Transfer Object (DTO)
In this exercise, you will use AutoMapper (https://github.com/AutoMapper/AutoMapper) to map the domain to data-transfer object and vice versa. If you have a keen eye for detail, you should have felt uncomfortable in the previous exercise, where a POST request to EmployeesController results in a new department getting created.
You can try it one more time! Make a POST request by copying and pasting the JSON {"Department":{"Name":"Sales"},"FirstName":"Evelyn","LastName":"Evilicious","DepartmentId":1} into the Request Body text box and the header Content-Type: application/json into Request Headers. If you query the employee table, you will see a record for Evelyn but you will also be able to see that a new record is inserted in the department table for Sales. This is over-posting in action.
This approach makes sense for the use case at hand, where a user may be from the Human Resources team to create an employee. Allowing this user to create new departments, however, does not make sense. By over-posting, either intentionally or otherwise, we allow a user to create new departments.
The root cause of this problem is that we are using the domain objects as models for the request to be bound to. It is good practice to use data transfer objects as models for binding and copy the properties over to the domain or entity objects and persist them.
Similarly, for serialization, the controller serializes the department details for each employee. For example, if you do a GET for an employee, the department details are also sent back (see the following JSON). This is not desirable in most cases. Using a DTO will help ensure that you send back only the details you intend to.
{
"Department": {
"Id": 1,
"Name": "HR",
"RowVersion": "AAAAAAAAB+E="
},
"Id": 1,
"FirstName": "John",
"LastName": "Human",
"DepartmentId": 1,
"RowVersion": "AAAAAAAAF3U="
}
Figure 7-4. The AutoMapper NuGet package
Listing 7-23. The EmployeeDto Class
public class EmployeeDto
{
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int DepartmentId{ get; set; }
public byte[] RowVersion { get; set; }
}
Listing 7-24. The DtoMapperConfig Class
using AutoMapper;
using TalentManager.Domain;
using TalentManager.Web.Models;
namespace TalentManager.Web
{
public static class DtoMapperConfig
{
public static void CreateMaps()
{
Mapper.CreateMap<EmployeeDto, Employee>();
}
}
}
DtoMapperConfig.CreateMaps();
Listing 7-25. EmployeesController Using AutoMapper
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Web.Http;
using AutoMapper;
using TalentManager.Data;
using TalentManager.Domain;
using TalentManager.Web.Models;
public class EmployeesController : ApiController
{
private readonly IUnitOfWork uow = null;
private readonly IRepository<Employee> repository = null;
private readonly IMappingEngine mapper = null;
public EmployeesController()
{
uow = new UnitOfWork();
repository = new Repository<Employee>(uow);
mapper = Mapper.Engine;
}
public EmployeesController(IUnitOfWork uow, IRepository<Employee> repository,
IMappingEngine mapper)
{
this.uow = uow;
this.repository = repository;
this.mapper = mapper;
}
public HttpResponseMessage Post( EmployeeDto employeeDto)
{
var employee = mapper.Map<EmployeeDto, Employee>(employeeDto);
repository.Insert(employee);
uow.Save();
var response = Request.CreateResponse<Employee>(HttpStatusCode.Created, employee);
string uri = Url.Link("DefaultApi", new { id = employee.Id });
response.Headers.Location = new Uri(uri);
return response;
}
}
Listing 7-26. Changes to the DtoMapperConfig Class
public static class DtoMapperConfig
{
public static void CreateMaps()
{
Mapper.CreateMap<EmployeeDto, Employee>();
Mapper.CreateMap<Employee, EmployeeDto>();
}
}
Listing 7-27. The GET Action Method Using AutoMapper
public HttpResponseMessage Get(int id)
{
var employee = repository.Find(id);
if (employee == null)
{
var response = Request.CreateResponse(HttpStatusCode.NotFound, "Employee not found");
throw new HttpResponseException(response);
}
return Request.CreateResponse<EmployeeDto>(
HttpStatusCode.OK,
mapper.Map<Employee, EmployeeDto>(employee));
}
{
"Id":1,
"FirstName":"John",
"LastName":"Human",
"DepartmentId":1,
"RowVersion":"AAAAAAAAF3U="
}
7.6 Injecting Dependencies Using StructureMap
In this exercise, you will constructor-inject the dependencies into a controller using StructureMap. Dependency Injection (DI) is a pattern that enables you to develop loosely coupled code. When we started using the Entity Framework in this chapter, our controller class had a hard dependency on the classes related to the framework. Then we managed to invert the dependency using one or more interfaces as the contract. Even in the case of Exercise 7-4, where we used the repository pattern, technically, the controller class still depends on a few classes, though not directly on the classes from the Entity Framework. In the constructor we will now create new instances of these dependencies and hence the controller will be dependent on the UnitOfWork and Repository<T> classes. By injecting these dependencies into the constructor, you will be able to achieve a clean separation. The ASP.NET Web API framework provides an entity called the dependency resolver, which creates the dependency objects for the framework, including ApiController. By creating a custom dependency resolver, you can configure the dependencies to be plugged in when the framework creates the controller instance.
Listing 7-28. The StructureMapDependencyScope Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web.Http.Dependencies;
using StructureMap;
public class StructureMapDependencyScope : IDependencyScope
{
private readonly IContainer container = null;
public StructureMapDependencyScope(IContainer container)
{
this.container = container;
}
public object GetService(Type serviceType)
{
bool isConcrete = !serviceType.IsAbstract && !serviceType.IsInterface;
return isConcrete ?
container.GetInstance(serviceType) :
container.TryGetInstance(serviceType);
}
public IEnumerable<object> GetServices(Type serviceType)
{
return container.GetAllInstances<object>()
.Where(s => s.GetType() == serviceType);
}
public void Dispose()
{
if (container != null)
container.Dispose();
}
}
Listing 7-29. The StructureMapContainer Class
using System.Web.Http.Dependencies;
using StructureMap;
public class StructureMapContainer : StructureMapDependencyScope, IDependencyResolver
{
private readonly IContainer container = null;
public StructureMapContainer(IContainer container) : base(container)
{
this.container = container;
}
public IDependencyScope BeginScope()
{
return new StructureMapDependencyScope(container.GetNestedContainer());
}
}
Listing 7-30. The IocConfig Class
using System;
using System.Linq;
using System.Web.Http;
using AutoMapper;
using StructureMap;
using TalentManager.Data;
namespace TalentManager.Web
{
public static class IocConfig
{
public static void RegisterDependencyResolver(HttpConfiguration config)
{
ObjectFactory.Initialize(x =>
{
x.Scan(scan =>
{
scan.WithDefaultConventions();
AppDomain.CurrentDomain.GetAssemblies()
.Where(a => a.GetName().Name.StartsWith("TalentManager"))
.ToList()
.ForEach(a => scan.Assembly(a));
});
x.For<IMappingEngine>().Use(Mapper.Engine);
x.For(typeof(IRepository<>)).Use(typeof(Repository<>));
});
config.DependencyResolver = new StructureMapContainer(ObjectFactory.Container);
}
}
}
IocConfig.RegisterDependencyResolver(GlobalConfiguration.Configuration);
Listing 7-31. EmployeesController with No Dependencies
public class EmployeesController : ApiController
{
private readonly IUnitOfWork uow = null;
private readonly IRepository<Employee> repository = null;
private readonly IMappingEngine mapper = null;
public EmployeesController(IUnitOfWork uow, IRepository<Employee> repository,
IMappingEngine mapper)
{
this.uow = uow;
this.repository = repository;
this.mapper = mapper;
}
// Action Methods go here
}
Thus, we managed to free the EmployeesController class from all low-level class dependencies. The controller now depends only on the abstractions: the three interfaces IUnitOfWork, IRepository<Employee>, and IMappingEngine.
7.7 Unit-Testing the Controller
In this exercise, you will unit-test the action methods in EmployeesController. The greatest benefit in isolating the controller dependencies is that you can perform automated unit-testing on all the action methods of the controllers by mocking the dependencies to simulate various scenarios that you will want to test. A mock object is a simulated object (generally proxies) that mimics the behavior of the real object in controlled ways. A programmer sets expectations on the mock object, that is, a certain method will be called with certain parameters and when that happens, an expected result will be returned. I use Rhino Mocks for mocking the dependency types and Visual Studio Unit Testing Framework for creating tests. You will need at a minimum the Professional Edition of Visual Studio, but the Ultimate Edition is recommended for this exercise.
Listing 7-32. The EmployeesControllerTest Class (Incomplete)
[TestClass]
public class EmployeesControllerTest
{
[TestMethod]
public void MustReturnEmployeeForGetUsingAValidId()
{
// Arrange
// Act
// Assert
}
}
Listing 7-33. The MustReturnEmployeeForGetUsingAValidId Test Method (Arrange)
using System.Net;
using System.Net.Http;
using System.Web.Http;
using AutoMapper;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Rhino.Mocks;
using TalentManager.Data;
using TalentManager.Domain;
using TalentManager.Web.Controllers;
using TalentManager.Web.Models;
[TestClass]
public class EmployeesControllerTest
{
[TestMethod]
public void MustReturnEmployeeForGetUsingAValidId()
{
// Arrange
int id = 12345;
var employee = new Employee()
{
Id = id, FirstName = "John", LastName = "Human"
};
IRepository<Employee> repository = MockRepository.GenerateMock<IRepository<Employee>>();
repository.Stub(x => x.Find(id)).Return(employee);
IUnitOfWork uow = MockRepository.GenerateMock<IUnitOfWork>();
Mapper.CreateMap<Employee, EmployeeDto>();
var controller = new EmployeesController(uow, repository, Mapper.Engine);
controller.EnsureNotNull();
// Act
// Assert
}
}
Listing 7-34. The SetBlankRequest Extension Method
using System.Net.Http;
using System.Web.Http;
using System.Web.Http.Hosting;
using System.Web.Http.Routing;
public static class ControllerHelper
{
public static void EnsureNotNull(this ApiController controller)
{
controller.Configuration = new HttpConfiguration();
controller.Request = new HttpRequestMessage();
controller.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey,
controller.Configuration);
}
}
Listing 7-35. The MustReturnEmployeeForGetUsingAValidId Test Method (Act and Assert)
public void MustReturnEmployeeForGetUsingAValidId()
{
// Arrange
// Code from the previous steps
// Act
HttpResponseMessage response = controller.Get(id);
// Assert
Assert.IsNotNull(response);
Assert.IsNotNull(response.Content);
Assert.IsInstanceOfType(response.Content, typeof(ObjectContent<EmployeeDto>));
Assert.AreEqual(HttpStatusCode.OK, response.StatusCode);
var content = (response.Content as ObjectContent<EmployeeDto>);
var result = content.Value as EmployeeDto;
Assert.AreEqual(employee.Id, result.Id);
Assert.AreEqual(employee.FirstName, result.FirstName);
Assert.AreEqual(employee.LastName, result.LastName);
}
Listing 7-36. The MustReturn404WhenForGetUsingAnInvalidId Test Method
[TestMethod]
public void MustReturn404WhenForGetUsingAnInvalidId()
{
// Arrange
int invalidId = 12345;
IRepository<Employee> repository = MockRepository.GenerateMock<IRepository<Employee>>();
repository.Stub(x => x.Find(invalidId)).Return(null); // Simulate no match
IUnitOfWork uow = MockRepository.GenerateMock<IUnitOfWork>();
Mapper.CreateMap<Employee, EmployeeDto>();
var controller = new EmployeesController(uow, repository, Mapper.Engine);
controller.EnsureNotNull();
// Act
HttpResponseMessage response = null;
try
{
response = controller.Get(invalidId);
Assert.Fail();
}
catch (HttpResponseException ex)
{
// Assert
Assert.AreEqual(HttpStatusCode.NotFound, ex.Response.StatusCode);
}
}
Listing 7-37. The SetRequest Extension Method
public static void SetRequest(this ApiController controller, string controllerPrefix,
HttpMethod method, string requestUri)
{
controller.Configuration = new HttpConfiguration();
var route = controller.Configuration.Routes.MapHttpRoute(
name: "DefaultApi",
routeTemplate: "api/{controller}/{id}",
defaults: new { id = RouteParameter.Optional }
);
var routeValues = new HttpRouteValueDictionary();
routeValues.Add("controller", controllerPrefix);
var routeData = new HttpRouteData(route, routeValues);
controller.Request = new HttpRequestMessage(method, requestUri);
controller.Request.Properties.Add(HttpPropertyKeys.HttpConfigurationKey,
controller.Configuration);
controller.Request.Properties.Add(HttpPropertyKeys.HttpRouteDataKey, routeData);
}
Listing 7-38. The MustReturn201AndLinkForPost Test Method
[TestMethod]
public void MustReturn201AndLinkForPost()
{
// Arrange
int id = 12345;
var employeeDto = new EmployeeDto() { Id = id, FirstName = "John", LastName = "Human" };
string requestUri = " http://localhost:8086/api/employees/ ";
Uri uriForNewEmployee = new Uri(new Uri(requestUri), id.ToString());
IRepository<Employee> repository = MockRepository.GenerateMock<IRepository<Employee>>();
repository.Expect(x => x.Insert(null)).IgnoreArguments().Repeat.Once();
IUnitOfWork uow = MockRepository.GenerateMock<IUnitOfWork>();
uow.Expect(x => x.Save()).Return(1).Repeat.Once();
Mapper.CreateMap<EmployeeDto, Employee>();
var controller = new EmployeesController(uow, repository, Mapper.Engine);
controller.SetRequest("employees", HttpMethod.Post, requestUri);
// Act
HttpResponseMessage response = controller.Post(employeeDto);
// Assert
repository.VerifyAllExpectations();
uow.VerifyAllExpectations();
Assert.AreEqual(HttpStatusCode.Created, response.StatusCode);
Assert.AreEqual(uriForNewEmployee, response.Headers.Location);
}
Figure 7-5. The Test Explorer window
Figure 7-6. The Test Explorer window displaying an error
Summary
The term dependency or coupling is used to denote the degree to which a software component depends on another. The controller class that you create is one of the important components in ASP.NET Web API, since it is the business end where all the application-specific action happens. A common dependency that an ASP.NET Web API controller takes is the dependency on the classes related to persistence infrastructure such as a database. If your controller classes are tightly coupled to the database, unit tests you write will start exercising the database, and unit tests will no longer be unit tests but become integration tests.
One of the great things about ASP.NET MVC compared to ASP.NET Web Forms is the ease with which controller classes can be unit-tested without spinning the wheels of ASP.NET infrastructure. ASP.NET Web API is as testable as ASP.NET MVC. However, paying attention to the dependencies your controller classes take, as you design and construct your service, is important to ensure testability.
In object-oriented programming, dependency inversion is one of the SOLID design principles. According to this principle, high-level modules should not depend on low-level modules, and both should depend on abstractions. A high-level module such as a controller class can be designed to depend on an abstraction such as the IDbSet<T> interface instead of taking direct dependency on concrete classes related to the Entity Framework. However, IDbSet derives from IQueryable, which is considered a leaky abstraction.
Another option is to apply the repository pattern. The application of the repository pattern over an ORM is a topic for debate, and there are arguments for and against the usage of the repository pattern. The major argument against using the repository pattern with an ORM is that an ORM itself is an abstraction over the database, so why do we need another abstraction over ORM? While having a repository per-entity simplifies design and keeps the abstraction tight, this approach could result in proliferation of repository classes in larger projects. In such a case, a generic repository could be used, but this approach also typically uses IQueryable. This aspect of a generic repository using IQueryable makes the repository so generic that it becomes possible to write any kind of queries, thereby making the abstraction loose or leaky.
Once the controller becomes dependent only on abstractions (interfaces), it becomes easy to inject the dependencies through a container like StructureMap. It also becomes easy to mock these abstractions through a mocking framework like Rhino Mocks for automated unit-testing.
1 Single responsibility, Open-closed, Liskov substitution, Interface segregation and Dependency inversion.