CHAPTER 7

image

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.

  1. Run Visual Studio 2012. Select File ➤ New ➤ Project and create a blank solution named TalentManager, as shown in Figure 7-1.

    9781430261759_Fig07-01.jpg

    Figure 7-1. A blank solution

  2. In the Solution Explorer, right-click Solution ‘TalentManager’ and choose Add ➤ New Project. Under Visual C# templates, select Web and choose ASP.NET MVC 4 Web Application. Give it a name of TalentManager.Web.
  3. Select the Web API template and leave the View engine as Razor. Click OK.
  4. In the Solution Explorer, right-click Solution ‘Talent Manager’ and choose Add ➤ New Project. Under Visual C# templates, select Windows and choose Class Library. Give it a name of TalentManager.Data. Click OK. Delete Class1.
  5. Repeat the previous step and create one more Class Library project named TalentManager.Domain. Delete Class1.
  6. Right-click References under TalentManager.Data. Select Manage NuGet Packages ➤ EntityFramework and click Install. My example uses EntityFramework 5.0.0.
  7. Right-click References under TalentManager.Data and select Add Reference. If it is not already checked, check System.Data.Entity and System.ComponentModel.DataAnnotations under Framework. Also, check TalentManager.Domain under Projects. Click OK.
  8. Right-click References under TalentManager.Web. Select Add Reference. Check TalentManager.Domain and TalentManager.Data under Projects. Click OK.
  9. Rebuild the solution to make sure all is well.
  10. In the TalentManager.Domain project, create new classes Employee and Department, as shown in Listing 7-1. An employee must always be part of a department, and the association reflects this rule. The Employee class has Department property and the navigation is one way only. The RowVersion property is for concurrency handling, which we will see in Chapter 8.

    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; }
    }
  11. In the TalentManager.Data project, create a new folder named Configuration. Create two classes: EmployeeConfiguration and DepartmentConfiguration, as shown in Listing 7-2.

    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();
        }
    }
  12. In the TalentManager.Data project, create a class Context, as shown in Listing 7-3.

    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());
     
        }
    }
  13. Modify the Application_Start method of the Globax.asax.cs file under TalentManager.Web, as shown in Listing 7-4. We create the database and tables manually and so we tell the Entity Framework not to do any of those tasks.

    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);
        }
    }
  14. In the Web.config file of TalentManager.Web, change the connection string as shown in Listing 7-5. My example uses SQL Server 2012 Development Edition. If you use the Express Edition, your connection string will need to reflect that. Also, give the server name and credentials appropriate for your SQL Server installation.

    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>
  15. In TalentManager.Web, delete ValuesController and add a new empty API controller named EmployeesController, as shown in Listing 7-6.

    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);
        }
    }
  16. Run Microsoft SQL Server Management Studio and create a new database named talent_manager.
  17. Create two tables, employee and department, with the structure shown in Figure 7-2. The table employee has a primary key of employee_id, which is an identity column. The table department has a primary key of department_id, which is an identity column as well.

    9781430261759_Fig07-02.jpg

    Figure 7-2. The structure of our tabless

  18. Create the foreign key relationship shown in Figure 7-3. The field department_id in the employee table has an FK relationship with department.department_id.

    9781430261759_Fig07-03.jpg

    Figure 7-3. The Relationship between our tables

  19. Run the SQL statements shown in Listing 7-7 from Microsoft SQL Server Management Studio to seed the 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);
  20. Rebuild the solution. Fire up Fiddler and make a GET request to http://localhost:39188/api/employees/1. The port on which my application runs is 39188. You will need to adjust the URI based on the port where your application runs.
  21. The response JSON is as follows:
    {
        "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.

  1. Create a new interface in the TalentManager.Data project named IContext, as shown in Listing 7-8.

    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; }
    }
  2. Change the Context class, as shown in Listing 7-9. It implements the IContext interface that we created in the previous step.

    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());
     
        }
    }
  3. Change the EmployeesController class, as shown in Listing 7-10. The improvement now is that except for one line of code inside the parameter-less constructor, the rest of the class has no idea about the Context class or DbSet<T>. The touch points are all through the interfaces IContext and IDbSet<T>. It is possible to remove the line that creates an instance of Context by using an Inversion of Control (IoC) container, but more on that later. The EmployeesController class is now programmed to the interfaces and not to the implementations, as recommended by the Gang of Four. The changes are shown in bold type.

    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);
        }
    }
  4. Rebuild the solution and make a GET request to http://localhost:39188/api/employees/1. You get back the same JSON as the previous exercise. So we have successfully refactored the code to invert the dependency the EmployeesController class has on Context (implicitly DbContext and DbSet). It now depends only on the abstractions of IContext and IDbSet.
  5. You will be able to unit-test this class by creating a fake object of type IContext and setting it in the constructor. The unit test will not hit the database but only call the fake object. However, it will become harder to unit-test as you start writing complex queries and using features like lazy loading.

LEAKY ABSTRACTION

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.

  1. Create an interface with named IEmployeeRepository in the TalentManager.Data project, as shown in Listing 7-11.

    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);
    }
  2. Create the concrete repository class EmployeeRepository, implementing IEmployeeRepository in the TalentManager.Data project, as shown in Listing 7-12.

    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);
        }
    }
  3. Change EmployeesController by commenting out the entire code of the class and copying and pasting the code from Listing 7-13.

    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);
        }
    }
  4. Rebuild the solution and make a GET request to http://localhost:39188/api/employees/1. You get back the same JSON as the previous exercise.
  5. We have implemented an additional action method that returns a list of employees based on the department. Test this method by making a GET request to the URI http://localhost : 39188/api/employees?departmentid=1. It returns all the employees that belong to department 1.

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.

  1. Create a new interface IIdentifiable, as shown in Listing 7-14, in the project TalentManager.Domain.

    Listing 7-14.  The IIdentifiable Interface

    public interface IIdentifiable
    {
            int Id { get; }
    }
  2. Modify the classes Employee and Department in the project TalentManager.Domain so that they both implement the IIdentifiable interface, as shown in Listing 7-15.

    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
    }
  3. Create a new interface, as shown in Listing 7-16, in the project TalentManager.Data. This generic repository interface will be the main contract or abstraction between the controller and the Entity framework classes. This interface defines methods for retrieving, creating, updating, and deleting entities.

    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);
    }
  4. Create the two interfaces IMyContext and IUnitOfWork, as shown in Listing 7-17, in the project TalentManager.Data. IRepository does not have a method to save changes. If you implement one, you will be able to save the changes to the individual entities. But when you need to make changes to two different entities, this approach can potentially result in corrupt data when changes to one entity succeed and those to the other one fail. By using the unit of work, you can ensure that the same database context is used across all the repositories. The Save method in the class implementing IUnitOfWork will call the SaveChanges method of IMyContext.

    Listing 7-17.  IMyContext and IUnitOfWork Interfaces

    public interface IMyContext : IDisposable
    {
        int SaveChanges();
    }
     
    public interface IUnitOfWork : IDisposable
    {
        int Save();
        IMyContext Context { get; }
    }
  5. Create the MyContext class in the project TalentManager.Data, as shown in Listing 7-18. This code calls the SetInitializer method in the static constructor instead of Application_Start. You can use Application_Start as well, but I do it this way to illustrate that there is more than one way to accomplish the task.

    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());
        }
    }
  6. Create the unit-of-work class UnitOfWork in the project TalentManager.Data, as shown in Listing 7-19. It takes in an object of type IMyContext, which it returns in the Context property. In this exercise, we will use the parameterless constructor but we will use the other one in the later parts of this chapter. The Save method simply calls the SaveChanges method of the context.

    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();
        }
    }
  7. Create the repository class in the project TalentManager.Data, as shown in Listing 7-20. Notice two things about this code:
    • a.   We specify two generic type constraints: T must be a reference type, and it must implement a simple interface of IIdentifiable. The IIdentifiable interface just has an Id property that enables an entity to be identified.
    • b.   The All method returns DbSet<T> by calling Set<T> on the context. The AllEager method is the eager-load version of the All method.

    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();
        }
    }
  8. In the project TalentManager.Web, open EmployeesController. Delete or comment the existing lines of code and copy and paste the code from Listing 7-21.

    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);
        }
    }
  9. Copy and paste the action methods for GET, POST, PUT, and DELETE, as shown in Listing 7-22. We have two GET methods: one for retrieving an employee by ID and a second one for retrieving all employees in a department.

    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();
    }
  10. Rebuild the solution. Run Fiddler and from the Composer tab make a GET request to http://localhost:39188/api/employees/1. Remember to replace port 39188 with the actual port that your application runs on in all the following steps. You’ll get back the JSON representation of the employee with ID 1 as the response.
  11. Make a POST request to http://localhost:39188/api/employees by pasting the JSON {"FirstName":"Brent","LastName":"Dodge","DepartmentId":1} into the Request Body text box and the header Content-Type: application/json into the Request Headers box. Right-click the dbo.employee table in the SQL Server Management Studio and choose Select Top 1000 Rows. You will see a record for Brent now.
  12. Make a POST request to http://localhost:39188/api/employees by pasting the JSON {"Department":{"Name":"Training"},"FirstName":"Johnny","LastName":"Scholar","DepartmentId":1} into the Request Body text box and the header Content-Type: application/json into the Request Headers box. If you query the employee table, you will see a record for Johnny, but the department will not be 1 but  the new department named Training. Also, you will be able to see that a new record is inserted in the department table for Training.
  13. Make a GET to http://localhost:39188/api/employees/2. Make a note of the RowVersion returned.
  14. Make a PUT request by pasting the JSON {"Id":2,"FirstName":"Joseph","LastName":"Border","DepartmentId":1,"RowVersion":"AAAAAAAAB9U="} into the Request Body text box and the header Content-Type: application/json into Request Headers. You will need to use the exact row version, as returned by the previous GET. If you query the employee table, you will see that the name has been changed from Joe to Joseph.
  15. Make a DELETE request to http://localhost:39188/api/employees/8. If you query the employee table, you will see that the record for Johnny Scholar has been deleted.

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>.

image 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="
}
  1. In the TalentManager.Web project, right-click References in the Solution Explorer of Visual Studio and select Manage NuGet Packages. Search for AutoMapper. Click Install, as shown in Figure 7-4.

    9781430261759_Fig07-04.jpg

    Figure 7-4. The AutoMapper NuGet package

  2. In the TalentManager.Web project, create the EmployeeDto class, as shown in Listing 7-23. We use the Models folder for this. This class does not have a Department property but only the DepartmentId property.

    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; }
    }
  3. In the TalentManager.Web project, create a class DtoMapperConfig in the App_Start folder, as shown in Listing 7-24. Use the namespace TalentManager.Web.

    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>();
            }
        }
    }
  4. In Global.asax.cs, add the following line to the end of the Application_Start method:
    DtoMapperConfig.CreateMaps();
    
  5. Modify EmployeesController, as shown in Listing 7-25. The changes are shown in bold type.

    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;
        }
    }
  6. Rebuild the solution.
  7. Make a POST request by pasting the JSON {"Department":{"Name":"ITSupport"},"FirstName":"Alice","LastName":"Smith","DepartmentId":1} into the Request Body text box and the header Content-Type: application/json into the Request Headers box. If you query the employee table, you will see a record for Alice but you will not be able to see a new record in the department table, unlike last time.
  8. Now, let us map Employee to EmployeeDto on the way out. Modify the DtoMapperConfig class as shown in Listing 7-26.

    Listing 7-26.  Changes to the DtoMapperConfig Class

    public static class DtoMapperConfig
    {
        public static void CreateMaps()
        {
            Mapper.CreateMap<EmployeeDto, Employee>();
            Mapper.CreateMap<Employee, EmployeeDto>();
        }
    }
  9. Modify the GET action method in EmployeesController as shown in Listing 7-27. Changes are shown in bold type.

    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));
    }
  10. Rebuild the solution and make a GET request to http://localhost:39188/api/employees/1. The response JSON will not contain any department information except for the department ID, as follows:
    {
       "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.

  1. In the TalentManager.Web project, right-click References in the Solution Explorer of Visual Studio and select Manage NuGet Packages. Search for StructureMap. Click Install.
  2. Create a class StructureMapDependencyScope in the TalentManager.Web project, as shown in Listing 7-28. In the GetService method, we call the GetInstance method on the container, if the type to be resolved is not an abstract class or an interface. Otherwise, we call the TryGetInstance method. TryGetInstance can create instances of the types registered with StructureMap, and concrete classes like controllers are not registered. In that case, GetInstance is called.

    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();
        }
    }
  3. Create a class StructureMapContainer in the TalentManager.Web project, as shown in Listing 7-29.

    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());
        }
    }
  4. In the App_Start folder, create a class IocConfig, as shown in Listing 7-30. Use the namespace TalentManager.Web. This code does the following:
    • a.   We configure StructureMap to scan the assemblies with names starting "TalentManager", namely TalentManager.Web, TalentManager.Data, and TalentManager.Domain, to resolve types using the default convention. If a class Foo implements IFoo, StructureMap will be able to automatically instantiate Foo, when the type to be resolved is IFoo, with this configuration.
    • b.   In addition, we manually set up the mapping for IMappingEngine of AutoMapper and IRepository<T>.
    • c.   The DependencyResolver property of the configuration object is set to an instance of StructureMapContainer passing in the Container of the ObjectFactory initialized in the method.

    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);
            }
        }
    }
  5. In Global.asax.cs, add the following line to the Application_Start method:
    IocConfig.RegisterDependencyResolver(GlobalConfiguration.Configuration);
    
  6. Remove the parameterless constructor from EmployeesController, as shown in Listing 7-31.

    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
    }
  7. Rebuild the solution and test the action methods. They will all work, as they did before the changes.
  8. Make a GET request to http://localhost:39188/api/employees/1 from Fiddler. Remember to replace the port 39188 with the actual port that your application runs on. You will get the JSON back.
  9. Make a POST request to http://localhost:39188/api/employees from Fiddler by copy-pasting the JSON {"FirstName":"Jane","LastName":"Bowen","DepartmentId":2} in the Request Body text box and the header Content-Type: application/json in Request Headers. If you query the employee table, you will see a record for Jane.

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.

  1. In the Solution Explorer, right-click Solution ‘Talent Manager’ and select Add ➤ New Project. Under Visual C# templates, select Test and choose Unit Test Project. Give it a name of TalentManager.Test.
  2. Rename the UnitTest1 class generated by Visual Studio to EmployeesControllerTest.
  3. In the TalentManager.Test project, right-click References in the Solution Explorer of Visual Studio and select Manage NuGet Packages. Search for rhinomocks. Click Install.
  4. Similarly, search for Automapper and Json.NET and install them as well.
  5. In the TalentManager.Test project, right-click References in the Solution Explorer of Visual Studio and select Add Reference. Click Solution on the left and check TalentManager.Web, TalentManager.Data, and TalentManager.Domain. Click OK.
  6. Also, add references to the System.Net.Http, System.Net.Http.Formatting, and System.Web.Http assemblies under Assemblies ➤ Extensions, similar to the previous step.
  7. Add a test method MustReturnEmployeeForGetUsingAValidId to the EmployeesControllerTest, as shown in Listing 7-32. There are three parts to the unit test, namely Arrange, Act, and Assert.

    Listing 7-32.  The EmployeesControllerTest Class (Incomplete)

    [TestClass]
    public class EmployeesControllerTest
    {
        [TestMethod]
        public void MustReturnEmployeeForGetUsingAValidId()
        {
            // Arrange
            // Act
            // Assert
        }
    }
  8. Arrange refers to setting the test target and the necessary mock objects in place. See Listing 7-33. In this test, we test the Get(int id) method of the controller. To create an instance of the controller, we need objects of type IUnitOfWork, IRepository<Employee>, and IMappingEngine.
    • a.   We let Rhino Mocks create a mock object for IRepository<Employee>. Here, by calling Stub, we tell the mock object to return the employee object that we just created in the previous step, when the Find method was called on the mock object.
    • b.   In this test, methods will not be called on IUnitOfWork. So, we just generate a mock.
    • c.   It is possible to create a mock for IMappingEngine but I choose to actually run AutoMapper as part of the test. AutoMapper just maps in-memory objects and there is only a very small overhead. So, I create a map by calling Mapper.CreateMap.
    • d.   Finally, create an instance of the controller passing in both the mock object and Mapper.Engine. We also call an extension method EnsureNotNull on the controller instance.

    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
        }
    }
  9. Create a static class ControllerHelper, as shown in Listing 7-34. The EnsureNotNull extension method creates an instance of HttpRequestMessage, and sets that in the controller instance along with setting the configuration and a related request property. We need the Request property of the controller to be not null, since we call the Request.CreateResponse method in the action method.

    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);
        }
    }
  10. Complete the MustReturnEmployeeForGetUsingAValidId test method, as shown in Listing 7-35. Calling the Get method on the controller instance is the Act part of the unit test. Then, we inspect the HttpResponseMessage returned to check if all is well, which constitutes the Assert part. Since we return EmployeeDto from the controller, we assert that response.Content is of the type ObjectContent<EmployeeDto>. Also, we assert the status code to be 200 - OK and check for correct values in the response. If anything is not in line with the assertion, the unit test will fail and indicate that there is some difference between how we expect the code to work and how it actually works.

    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);
    }
  11. Add one more test method MustReturn404WhenForGetUsingAnInvalidId to check that the action method returns a 404 - Not Found, if an invalid employee ID is passed in. See Listing 7-36. This test is almost the same as the previous one, but by configuring Stub in such a way that the mock returns null, we simulate the scenario of an invalid ID coming in. We catch HttpResponseException and check whether the status code is 404.

    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);
        }
    }
  12. Add another extension method to the class ControllerHelper, as shown in Listing 7-37. This is an upgraded version of the EnsureNotNull method. This method not only ensures that Request is not null but also configures the route.

    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);
    }
  13. Add another test method, as shown in Listing 7-38, to test the action method for POST. This method does the following:
    • a.   We set the requestUri as http://localhost:8086/api/employees/. We then assert that the URI of the newly created resource is http://localhost:8086/api/employees/12345, where 12345 is the new ID.
    • b.   For the mock objects corresponding to IRepository and IUnitOfWork, we do not stub out the methods but set the expectation that the methods Insert and Save must be called once and only once. For the Insert method, we don’t care about the input, so we call IgnoreArguments, and the input is ignored.
    • c.   In the Assert part, by calling the VerifyAllExpectations method on the mock objects, we assert the expectation we set earlier, which is that these methods must be called once.
    • d.   In addition, we also assert the status code to be 201 and the URI of the newly created resource.

    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);
    }
  14. Rebuild the solution. From Visual Studio, select TEST ➤ Run ➤ All Tests.
  15. Visual Studio displays the results in the Test Explorer window, as shown in Figure 7-5.

    9781430261759_Fig07-05.jpg

    Figure 7-5. The Test Explorer window

  16. If there are failures, the Test Explorer window shows the failure with more information, as illustrated in Figure 7-6.

    9781430261759_Fig07-06.jpg

    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.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.147.27.131