Building a Web API 2 project from scratch using OWIN/Katana .NET Part 4: async controllers and mini-DDD
July 13, 2015 Leave a comment
Introduction
In the previous post we briefly looked at a new hosting project by Microsoft called Helios. It is meant to be the future of web application deployment where Helios removes the necessity of having the entire System.Web dependency in your web project. We saw that Helios is only in a preview state so it shouldn’t be used for real-life projects yet.
In this post we’ll diverge from OWIN/Katana and instead see how we can add asynchronous controller methods to our current CustomersApi web project. We’ll also build a miniature version of a layered application. We’ll put the layers into separate folders for simplicity.
await/async
If you work professionally with .NET web projects and/or Windows store apps then the await and async keywords must be familiar to you by now. If you’re not sure what they are and how they are used then there’s a dedicated series on this blog that starts here. In short they are meant to use the available threads on your server more efficiently. I won’t go through the things that were discussed there. If you cannot follow the below code samples then refer back to this series.
So if you have asynchronous controllers in your MVC/Web API project then the available threads on your deploy server will be better utilised. A significant benefit is a more responsive and scalable web application.
We currently have one controller action in CustomersController:
public class CustomersController : ApiController { public IHttpActionResult Get() { IList<Customer> customers = new List<Customer>(); customers.Add(new Customer() { Name = "Nice customer", Address = "USA", Telephone = "123345456" }); customers.Add(new Customer() { Name = "Good customer", Address = "UK", Telephone = "9878757654" }); customers.Add(new Customer() { Name = "Awesome customer", Address = "France", Telephone = "34546456" }); return Ok<IList<Customer>>(customers); } }
We’ll turn the Get action into an asynchronous method. The key to this is that the Get method needs to call an awaitable method which returns a Task. The Get method simulates a database lookup where we extract all customers from the data store.
The repository layer
We’ll follow a stripped down version of a full-fledged layered application with the domain being the centre of the project. In other words we’ll build an extremely simplified Domain Driven Design (DDD) app. I’ll use the ideas in this series later on where we’ll revisit DDD.
We currently only have a single domain, i.e. the Customer in the Model folder. Let’s add a repository interface to the same folder:
public interface ICustomerRepository { IEnumerable<Customer> FindAll(); }
This indicates what actions the Customer domain wants to enable for a concrete repository. We currently only allow a single action, i.e. the extraction of all customer objects.
Add a new folder to the project called Repository and add a class called CustomerRepository to it. It will implement ICustomerRepository:
public class CustomerRepository : ICustomerRepository { public IEnumerable<Customer> FindAll() { IList<Customer> customers = new List<Customer>(); customers.Add(new Customer() { Name = "Nice customer", Address = "USA", Telephone = "123345456" }); customers.Add(new Customer() { Name = "Good customer", Address = "UK", Telephone = "9878757654" }); customers.Add(new Customer() { Name = "Awesome customer", Address = "France", Telephone = "34546456" }); return customers; } }
This is the same code as above in the Get action.
The service layer
The service layer will be the connecting tissue between the controller and the repository. Add a new folder called Service with the following interface:
public interface ICustomerService { Task<GetCustomersResponse> GetCustomersAsync(); }
…where GetCustomersResponse looks as follows:
public class GetCustomersResponse { public IEnumerable<Customer> Customers { get; set; } public bool Success { get; set; } public Exception OperationException { get; set; } }
GetCustomersResponse comes from the simple RequestResponse pattern.
Note that GetCustomersAsync returns a Task, i.e. it’s a good candidate for an awaitable method. However, the repository method is not asynchronous so we’ll need to accommodate that. We could transform ICustomerRepository as well but let’s pretend that we have no access to that interface.
One way to resolve this problem is the following implementation of the ICustomerService interface – add this to the Services folder:
public class CustomerService : ICustomerService { private readonly ICustomerRepository _customerRepository; public CustomerService(ICustomerRepository customerRepository) { if (customerRepository == null) throw new ArgumentNullException("Customer repository!"); _customerRepository = customerRepository; } public async Task<GetCustomersResponse> GetCustomersAsync() { return await Task<GetCustomersResponse>.Run(() => GetCustomers()); } private GetCustomersResponse GetCustomers() { GetCustomersResponse response = new GetCustomersResponse(); try { IEnumerable<Customer> customers = _customerRepository.FindAll(); response.Customers = customers; response.Success = true; } catch (Exception ex) { response.Success = false; response.OperationException = ex; } return response; } }
CustomerService will need a customer repository to fulfil its job. The asynchronous GetCustomersAsync method delegates its task to GetCustomers on a different thread. GetCustomersAsync can therefore be awaited.
Just to recap we have the following folders and files in our project currently:
The controller
We’re now ready to modify CustomersController. We’ll delegate the retrieval of the customer objects to an ICustomerService object. Therefore the CustomersController will need a dependency of this type. For the time being we’ll construct the dependency in a parameterless CustomersController constructor but we’ll revisit this point in the next post as it’s not optimal from a dependency handling point of view:
public class CustomersController : ApiController { private readonly ICustomerService _customerService; public CustomersController() { _customerService = new CustomerService(new CustomerRepository()); } public async Task<IHttpActionResult> Get() { GetCustomersResponse response = await _customerService.GetCustomersAsync(); if (response.Success) { return Ok<IEnumerable<Customer>>(response.Customers); } return InternalServerError(response.OperationException); } }
So now even the Get method returns a Task and can be awaited.
That’s it, you can run the project and navigate to /customers. You should see the same XML response as before.
In the next post we’ll rectify the issue of constructing the ICustomerRepository object directly in the controller constructor.
View the list of MVC and Web API related posts here.