TinyIoc MVC and WebApi Configuration

TinyIoc MVC and WebApi ConfigurationFound myself looking to wire up TinyIoC container with ASP.NET MVC today. I’d been meaning to have a look see at Tony Sneed’s Trackable Entities. After reviewing an ASP.NET MVC application attempting to leverage theses patterns I knew there was a cleaner approach. I began refactoring the implementation, then realized after reading Tony Sneed’s Trackable Entities now with Repository and Unit of Work patterns that I was rebuilding a similar implementation. Having a personal disdain for not reinventing the wheel, I decided to take a closer look at Tony’s implementation. I like his structure but feel it is lacking a service layer, and state management could be abstracted a bit more but overall liked what I was seeing. In his simple contrived example he already supported ASP.NET WebAPI but my current requirement is to support ASP.NET MVC. Tweaking the implementation was simple enough using the built in MCV scaffolding and tweaking the TinyIoC configuration. In my simple prototype I extended the TinyIoC configuration to support ASP.NET MVC too.

A quick note concerning TinyIoC core design principals.

  • Simplified Inclusion – No assembly to reference, no binary to worry about, just a single cs file you can include in your project and you’re good to go. It even works with both Mono and MonoTouch for iPhone development!
  • Simplified Setup – With auto-resolving of concrete types and an “auto registration” option for interfaces setup is a piece of cake. It can be reduced to 0 lines for concrete types, or 1 line if you have any interface dependencies!
  • Simple, “Fluent” API – Just because it’s “Tiny”, doesn’t mean it has no features. A simple “fluent” API gives you access to the more advanced features, like specifying singleton/multi-instance, strong or weak references or forcing a particular constructor.

Features

  • Wide platform support – actively tested on Windows, Mono, MonoTouch, PocketPC and Windows Phone 7. Also works just fine on MonoDroid.
  • Simple API for Register, Resolve, CanResolve and TryResolve.
  • Supports constructor injection and property injection. Constructors are selected automatically but can be overridden using a “fluent” API.
  • Lifetime management – including singletons, multi-instance and ASP.Net per-request singletons. see more features here

The Code

The core TinyIoC Nuget package, Install-Package TinyIoC is bare bones. However, to leverage ASP.Net per-request lifetime support you will need to also include TinyIoCAspNetExtensions.cs, and the TinyIoC namespace. This provides an extension method for supporting per-request registrations. It’s an extra file, but it’s preferable to taking a dependency on Asp.Net in the main file, which then requires users to setup #DEFINEs for non-asp.net platforms. Recommend installing TinyIoC.AspNetExtensions Nuget package Install-Package TinyIoC.AspNetExtensions too. I suggest you work through Tony Sneed’s simple Getting Started Guide for Trackable Entities with Repository and Unit of Work Patterns for more context.

Tony defines an IoCConfig.cs class to handle obtaining a reference to the TinyIoC container, registering types in the container and then setting ASP.NET WebApi Dependency Resolver to the TinyIoCDependencyResolver passing in the TinyIoC container. What you may have already realized is, the dependency resolver is specific to ASP.NET WebApi. Meaning ASP.NET MVC is not currently aware of the TinyIoC container with our new registrations.

2. IoCConfig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public static class IoCConfig
{
    public static void Register(HttpConfiguration config)
    {
        // Get IoC container
        var container = TinyIoCContainer.Current;

        // Register context, unit of work and repos with per request lifetime
        container.Register<INorthwindSlimContext, NorthwindSlimContext>().AsPerRequestSingleton();
        container.Register<INorthwindUnitOfWork, NorthwindUnitOfWork>().AsPerRequestSingleton();
        container.Register<ICustomerRepository, CustomerRepository>().AsPerRequestSingleton();
        container.Register<IOrderRepository, OrderRepository>().AsPerRequestSingleton();

        // Set Web API dep resolver
        config.DependencyResolver = new TinyIoCDependencyResolver(container);
    }
}

Create a new Api folder in WebApi project then move the ApiControllers to that folder updating the namespace appropriately. Then use ASP.NET MVC 5 scaffolding to create a new CustomerController in the default Controller folder, selecting Async, and Customer model. To quickly prove ASP.NET MVC has no idea about the TinyIoC container create a constructor for CustomerController show below that injects a INorthwindUnitOfWork dependency. Compile the application, hit F5 to run then navigate to /customer to blow up the controller.

3. CustomerController
1
2
3
4
5
6
7
8
9
10
public class CustomerController : Controller
{
    private readonly INorthwindUnitOfWork _unitOfWork;

    public CustomerController(INorthwindUnitOfWork unitOfWork)
    {
        _unitOfWork = unitOfWork;
    }
    ......
}

The good news is updating the configuration to support both ASP.NET WebApi and MVC with same TinyIoC container takes a couple straight forward steps.

  • Create a new TinyIocMvcDependencyResolver class that implements System.Web.Mvc.IDependencyResolver
  • Update IoCRegister method
    • Remove the parameter
    • Set ASP.NET MVC Dependency Resolver by adding the line System.Web.Mvc.DependencyResolver.SetResolver(new TinyIocMvcDependencyResolver(container))
    • Set ASP.NET WebApi Dependency Resolver by adding the line System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new TinyIocWebApiDependencyResolver(container);
  • Update Global.asax.cs to register TinyIoC using IoCConfig.Register() instead of GlobalConfiguration.Configure(IoCConfig.Register)

Before continuing, assuming you are following along with Tony’s example, I suggest renaming TinyIoCDependencyResolver class to show intent and be more readable using ReSharper; rename from TinyIoCDependencyResolver to TinyIocWebApiDependencyResolver.

1. Tiny Ioc Web Api Dependency Resolver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
public class TinyIocWebApiDependencyResolver : IDependencyResolver
{
    private bool _disposed;
    private TinyIoCContainer _container;

    public TinyIocWebApiDependencyResolver(TinyIoCContainer container)
    {
        if (container == null)
            throw new ArgumentNullException("container");

        _container = container;
    }

    public IDependencyScope BeginScope()
    {
        if (_disposed)
            throw new ObjectDisposedException("this", "This scope has already been disposed.");

        return new TinyIocWebApiDependencyResolver(_container.GetChildContainer());
    }

    public object GetService(Type serviceType)
    {
        if (_disposed)
            throw new ObjectDisposedException("this", "This scope has already been disposed.");

        try
        {
            return _container.Resolve(serviceType);
        }
        catch (TinyIoCResolutionException)
        {
            return null;
        }
    }

    public IEnumerable<object> GetServices(Type serviceType)
    {
        if (_disposed)
            throw new ObjectDisposedException("this", "This scope has already been disposed.");

        try
        {
            return _container.ResolveAll(serviceType);
        }
        catch (TinyIoCResolutionException)
        {
            return Enumerable.Empty<object>();
        }
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }

    protected virtual void Dispose(bool disposing)
    {
        if (_disposed)
            return;

        if (disposing)
            _container.Dispose();

        _disposed = true;
    }
}

Leverage Resharper shortcut keys, put cursor on DependencyResolution folder and use Ctrl+Shift+A to add a new class called TinyIocMvcDependencyResolver then copy the code below. This class will be used by ASP.NET MVC to resolve the custom TinyIoC container.

2. Tiny Ioc Mvc Dependency Resolver
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
public class TinyIocMvcDependencyResolver : IDependencyResolver
{
     private TinyIoC.TinyIoCContainer _container;
     public TinyIocMvcDependencyResolver(TinyIoC.TinyIoCContainer container)
     {
         _container = container;
     }
     public object GetService(Type serviceType)
     {
         try
         {
             return _container.Resolve(serviceType);
         }
         catch (Exception)
         {
             return null;
         }
     }
     public IEnumerable<object> GetServices(Type serviceType)
     {
         try
         {
             return _container.ResolveAll(serviceType, true);
         }
         catch (Exception)
         {
             return Enumerable.Empty<object>();
         }
     }
}

Now update the IoCConfig.cs class as seen below.

  • Remove the parameter
  • Set ASP.NET MVC Dependency Resolver by adding the line System.Web.Mvc.DependencyResolver.SetResolver(new TinyIocMvcDependencyResolver(container))
  • Set ASP.NET WebApi Dependency Resolver by adding the line System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new TinyIocWebApiDependencyResolver(container);
3. IoCConfig
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public static void Register()
{
    // Get IoC container
    var container = TinyIoCContainer.Current;

    // TODO: Register context, unit of work and repos with per request lifetime
    // For example:
    container.Register<INorthwindSlimContext, NorthwindSlimContext>().AsPerRequestSingleton();
    container.Register<INorthwindUnitOfWork, NorthwindUnitOfWork>().AsPerRequestSingleton();
    container.Register<ICustomerRepository, CustomerRepository>().AsPerRequestSingleton();
    container.Register<IOrderRepository, OrderRepository>().AsPerRequestSingleton();

    // Set MVC dep resolver
    System.Web.Mvc.DependencyResolver.SetResolver(new TinyIocMvcDependencyResolver(container));

    // Set Web API dep resolver
    System.Web.Http.GlobalConfiguration.Configuration.DependencyResolver = new TinyIocWebApiDependencyResolver(container);
}

Finally the line registering the TinyIoC with ONLY the ASP.NET WebApi GlobalConfiguration.Configure(IoCConfig.Register) can be replaced with a global registration, IoCConfig.Register() to register TinyIoC with both ASP.NET MVC and WebApi. Recompile, hit F5, then navigate to /api/customer to ensure ASP.NET WebApi finds TinyIoC container, then navigate to /customer to ensure ASP.NET MVC now can find TinyIoC container too.

X. Global.asax.cs
1
2
3
4
5
6
7
8
9
10
11
protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();
    GlobalConfiguration.Configure(WebApiConfig.Register);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    // Register IoC config
    IoCConfig.Register();
}

Reference

Comments