Building a Web API 2 project from scratch using OWIN/Katana .NET Part 5: adding an IoC

Introduction

In the previous post we transformed the CustomersApi application a little bit. We added a customer repository and a customer service. We also transformed the Get action method of CustomersController into an asynchronous one.

We ended up having to construct the dependencies of CustomersController in the constructor like this:

public CustomersController()
{
	_customerService = new CustomerService(new CustomerRepository());
}

In this post we’ll get rid of this direct control over the controller dependencies. Instead, we’ll let an ICustomerService object be injected into the controller through its constructor.

Dependency injection

What’s wrong with the above code anyway? The short answer is that CustomersController will directly depend on concrete implementations of certain abstractions. The controller requires an ICustomerService so it builds one in its constructor. However, the concrete implementation of ICustomerService, i.e. CustomerService also requires a dependency of type ICustomerRepository. Therefore the controller must construct it too. So suddenly the constructor is made responsible for fetching concrete implementations for a dependency chain. CustomerRepository could in turn also have some dependency and then CustomersController would have to provide it too, e.g.:

public CustomersController()
{
	_customerService = new CustomerService(new CustomerRepository(new EntityFrameworkUnitOfWork()));
}

A long answer as to why this direct responsibility is not ideal requires a lot more words. There are 2 series devoted to the SOLID principles on this blog where the letter ‘D’, i.e. the dependency inversion principle is the most relevant to our discussion:

Our goal is to transform this…

public class CustomersController : ApiController
{
	private readonly ICustomerService _customerService;

	public CustomersController()
	{
		_customerService = new CustomerService(new CustomerRepository());
	}

	...
}

…into its constructor injection equivalent, i.e.:

public CustomersController(ICustomerService customerService)
{
	if (customerService == null) throw new ArgumentNullException("ICustomerService");
	_customerService = customerService;
}

In fact you can already now rewrite the CustomersController constructor in the CustomersApi like above. This follows the same pattern as we saw in the case of CustomerService:

private readonly ICustomerRepository _customerRepository;

public CustomerService(ICustomerRepository customerRepository)
{
	if (customerRepository == null) throw new ArgumentNullException("Customer repository!");
	_customerRepository = customerRepository;
}

We let the consumer of CustomerService provide an implementation of ICustomerRepository. We want to do the same for CustomersController. The consumer of the controller should provide an implementation of ICustomerService.

However, where do we provide that implementation? Where is the caller to CustomersController? It probably lies hidden within .NET and its MVC routing logic.

IoC containers

Fortunately for us this is not a new problem and has been solved using Inversion of Control (IoC) containers. IoC containers are typically quite complex mechanisms that can “magically” construct concrete implementation of abstract dependencies on the fly. There are many IoC containers out there for .NET and one of the most popular ones is StructureMap.

StructureMap preparations and limitations

StructureMap comes in many flavours. If you open the NuGet package manager and search for “structuremap” you’ll get a lot of different results. We’ll need the one specifically for Web API 2:

StructureMap installation from NuGet for Web API 2

It will add several libraries and new files to the project:

StructureMap related files added to project

These are all auto-generated files. In fact the installer also added MVC-related classes, such as StructuremapMvc.cs. We won’t need all of those.

Also, as we started with an empty application originally you’ll see a couple of build errors. The StructureMap package we’ve installed expects a couple of standard assemblies to be available in the list of references.

We have an additional problem. We installed Helios in this post through NuGet. Helios and StructureMap don’t work well together. I’ve tried to have both Helios and StructureMap in the demo API but haven’t found a good solution. Maybe there’s some kind of a solution out there. We can probably expect a StructureMap update later on when Helios goes live.

Therefore uninstall both pre-release packages that Helios installed:

Prerelease packages installed by Helios

Also, StructureMap won’t work with OWIN self-host so make sure you set IIS as the deployment platform. I’m not an expert of IoC containers so I’m not sure how to make StructureMap work with the OWIN self-host and Helios. If you know more then you’re welcome to provide your solution in the comments section below.

Next you can safely delete both classes in the App_Start folder. Delete the StructureMapScopeModule class from the DependencyResolution folder as well.

StructureMap-related code

We can now set up StructureMap to find our dependencies. The updated Configuration method in Startup.cs looks as follows:

public void Configuration(IAppBuilder appBuilder)
{			
	IContainer container = IoC.Initialize();
	container.AssertConfigurationIsValid();
	Debug.WriteLine(container.WhatDoIHave());			
	HttpConfiguration httpConfiguration = new HttpConfiguration();
	httpConfiguration.DependencyResolver = new StructureMapWebApiDependencyResolver(container);			
	WebApiConfig.Register(httpConfiguration);
	appBuilder.UseWebApi(httpConfiguration);			
}

We initialise the IoC container. We also want to be certain that all the abstract dependencies have been resolved to a certain concrete implementation.

The WhatDoIHave() method will return the resolved types. If you look in the debug window when running the application you’ll find the following:

ICustomerRepository CustomersApi.Model Transient CustomersApi.Repository.CustomerRepository (‘CustomersApi.Repository.CustomerRepository, CustomersApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’) CustomersApi.Repository.Cus… (Default)
————————————————————————————————————————————————————————————————————————————————————————–
ICustomerService CustomersApi.Service Transient CustomersApi.Service.CustomerService (‘CustomersApi.Service.CustomerService, CustomersApi, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null’) CustomersApi.Service.Custom… (Default)

So for ICustomerRepository StructureMap found an implementation called CustomerRepository. Similarly, ICustomerService was resolved to CustomerService.

How did StructureMap know all that? Open DefaultRegistry.cs in the DependencyResolution folder. You’ll find the following call:

scan.WithDefaultConventions();

The default convention for interface abstractions is that if StructureMap sees an interface whose name starts with an “I” then it will look for an implementation with the same name without the “I”: ICustomerService – CustomerServicer, ICustomerRepository – CustomerRepository.

It’s here that you can declare your concrete types for the abstract dependencies, e.g.:

public DefaultRegistry()
		{
			Scan(
				scan =>
				{
					scan.TheCallingAssembly();
					scan.WithDefaultConventions();
				});
			For<ICustomerRepository>().Use<BrilliantCustomerRepository>();
		}

Note the usage of For and Use in the DefaultRegistry constructor.

If you run the application now you’ll see that both ICustomerRepository and ICustomerService are resolved correctly by StructureMap and the customers list is retrieved from the repository.

This is the last post of this short intro to Web API 2 with OWIN. We’ll take up some security extension options in Web API 2 in the next series.

View the list of MVC and Web API related posts here.

Advertisement

About Andras Nemes
I'm a .NET/Java developer living and working in Stockholm, Sweden.

4 Responses to Building a Web API 2 project from scratch using OWIN/Katana .NET Part 5: adding an IoC

  1. tom says:

    Great and very insightful posts, thank you!

  2. Mayuresh Sawardekar says:

    Good read, is there a link to download the source ?

  3. sag1v says:

    nice post, i think you have a typo.
    in “DefaultRegistry” on the last line:
    “For().Use()”
    should be:
    “For().Use()”

    Where is the “Brilliant” came from? 🙂

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Elliot Balynn's Blog

A directory of wonderful thoughts

Software Engineering

Web development

Disparate Opinions

Various tidbits

chsakell's Blog

WEB APPLICATION DEVELOPMENT TUTORIALS WITH OPEN-SOURCE PROJECTS

Once Upon a Camayoc

Bite-size insight on Cyber Security for the not too technical.

%d bloggers like this: