Click here to Skip to main content
Click here to Skip to main content

Simplest Possible ASP.NET Web API Project that Implements IoC/DI using Castle Windsor

, 15 Jan 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Create the simplest possible ASP.NET Web API app that uses Castle Windsor for DI in 23 pretty easy steps

On Your Mark

You have to walk before you can run; and sometimes, before you can walk, you have to crawl on your hands and knees for miles until you finally achieve some semblance of enlightenment.

As has been said, "Often those who start using DI quickly find themselves lost in a sea of confusion."

The last three weeks have seemed like a bloody-kneecapped pilgrimage for me to understand and implement Inversion of Control (IoC) and Dependency Injection (DI). In an attempt to write the article that I wished I had when I began this quest, I will try to make this step-by-step tutorial as basic and grokkable as possible, and we will build an As-Simple-as-Possible ASP.NET Web API project that uses Castle Windsor to implement basic DI.

Get Set / Get Your Mind Right

First, a little conceptual background as to why DI is not just another acronym or the latest snake oil sold to unsuspecting managers for them to foist upon their weary and wary developers: Dependency Injection is a way to write to an interface rather than an implementation – IOW, code to an interface or abstraction rather than to a concrete implementation (a class or “component”); This is a good thing, as it allows for extensibility - you’re not “locked in” to a particular class.

DI (as I’ll call Dependency Injection from now on) is closely related to Inversion of Control (which I’ll refer to as IoC from now on). What this means is that instead of a class instantiating a concrete class that it itself first declares, it uses a constructor that takes an interface type as an argument, and a concrete class that implements that interface is passed to it.

Flash Forward/Precap

To quickly give a rundown of what code needs to be added to a Web API project to "Windsorize" it, before going through it step by step, I will enumerate the basic process:

  • Add references (via NuGet): Castle.Core and Castle.Windsor
  • Change \App_Start\WebApiConfig.cs, so that it replaces the normal routing with DI routing (using WindsorCompositionRoot, a class to be added)
  • Add a Controller, in the DI style (constructor with an interface arg)
  • Add a DIInstallers folder, and a RepositoriesInstaller class below that. Inside that, register the classes you need (map interfaces to concrete implementations)
  • Add a DIPlumbing folder, and then add a WindsorCompositionRoot class beneath that
  • Add a Model folder; beneath it, add a model class, a repository interface, and a repository interface implementation (concrete class)
  • Modify Global.asax.cs to be DI-friendly
  • Add WindsorDependencyResolver beneath the root

Now we will step back and start from the beginning (which has been previously identified, by somebody somewhere, as a very good place to start).

Where DI And Web API Meet

For ASP.NET Web API in particular, a Controller (the “C” in MVC (Model View Controller, a pattern that Web API expects you to use)) normally has a default (implicit) no-arg constructor. With DI, though, there must be an explicit constructor with such a signature. In other words, rather than having no constructor or having an empty default constructor like this:

     private DuckbillRepository duckbillRepository;
    
        public DuckbillController()
        {
        }

...you would have something like this:

private IDuckbillRepository _duckbillRepository;

        public DuckbillController(IDuckbillRepository duckbillRepository)
        {
            if (duckbillRepository == null)
            {
                throw new ArgumentNullException("duckbillRepository is null");
            }
            _duckbillRepository = duckbillRepository;
        } 

Now it’s probably easy to see why that is beneficial – you can instantiate a Controller using any class that implements the IDuckbillRepository interface. Normally, the ASP.NET Web API ecosystem takes care of “automagically” instantiating the appropriate controller for you based on the URI passed in from the client. And it expects a no-arg constructor. So how do we “change the rules” and use an argful constructor instead of what’s expected - IOW, how do we instantiate the controller with a class that implements the desired interface?

The answer is that we must intercept Web API MVC’s “normal” way of routing controller requests. You replace the default routing mechanism with a DI-specific one. But that’s not enough -- you also have to map concrete classes/”components” to the abstractions/interfaces (such as the IDuckbillRepository above). The DI ecosystem lets you map which classes you want to be in play, by mapping them to the interface type the Controller’s constructor expects. You can use what Mark Seemann (henceforth referred to as “The DI Whisperer”) calls “Poor Man’s DI,” but he recommends that you instead use a DI Container (a DI framework) to help automate some of the otherwise tedious steps. There are many such IoC/DI frameworks, but the one I will discuss here is Castle Windsor (the inverting of the phrase “Windsor Castle” is apparently a reference to the “Inversion of Control” that CW provides), henceforth referred to as “CW” – to be confused with “Country Western” only if that floats your boatload. Without going into further background or theory, I will now simply commence with the step-by-step tutorial on how to create the simplest possible Web API/MVC project that makes use of DI via Castle Windsor.

For background details, see The DI Whisperer’s blog, particularly this one and this one

If you need or want an in-depth understanding of DI in .NET (along with material on not just Castle Windsor but many of the other DI/IoC Container frameworks), get the DI Whisperer’s book here

Go – Add the MRC Pieces

  1. In Visual Studio (preferably VS 2013, but the following should work very similarly in slightly older versions, too), select File > New Project... > Installed > Templates > Visual C# > Web > ASP.NET Web Application > OK. In the “New ASP.NET Project” dialog that is invoked, select the “Web API” template. Perhaps Web API apps should actually be referred to as using the MRC pattern instead of MVC, with the “V” (for “View”) being replaced by “R” (for “Repository”).
  2. At any rate, now add a Model by right-clicking the Models folder and selecting Add > Class… Name the class “DPlatypus” (or something else, if you’re not feeling the love for the poison-toed mammal).
  3. Give this new class a few properties, such as:
    namespace WebApplication974.Models
    {
        public class DPlatypus
        {
            public int Id { get; set; }
            public string Name { get; set; }
        }
    }
  4. Create a Repository where the related data will be stored, from which the not-yet-created Controller will query the data it needs. Right-click again on the Models folder, and select Add > New Item… > Installed > Interface, and name it IDPlatypusRepository (or whatever you want, but, to make life easier for yourself, something that goes along with whatever you named the Model class, as in “I” + <Whatever> + “Repository.”
  5. Add code to it that looks similar to this:
        public interface IDPlatypusRepository
        {
            IEnumerable<DPlatypus> GetAll();
            String Get(int id);
            DPlatypus Add(DPlatypus platypus);
        } 

    Note: Normally, to implement the entire gamut of CRUD methods, you would have more methods than these few (specifically, you would also have Remove and Update methods), but remember - we're making this as simple as possible to show the bare bones ASP.NET Web API DI CW extravaganza.

  6. Right-click again on the Models folder, and select Add > Class..., naming it "DPlatypusRepository."
  7. Add code to it like so:
        public class DPlatypusRepository : IDPlatypusRepository
        {
            private List<DPlatypus> platypi = new List<DPlatypus>();
            private int _nextId = 1;
    
            public DPlatypusRepository()
            {
                Add(new DPlatypus { Name = "Donald" });
                Add(new DPlatypus { Name = "GoGo" });
                Add(new DPlatypus { Name = "Patty" });
                Add(new DPlatypus { Name = "Platypup" });
                Add(new DPlatypus { Name = "Platypop" });
                Add(new DPlatypus { Name = "Platymop" });
                Add(new DPlatypus { Name = "Platydude" });
                Add(new DPlatypus { Name = "Platydudette" });
            }
    
            public IEnumerable<DPlatypus> GetAll()
            {
                return platypi;
            }
    
            public String Get(int id)
            {
                var platypus = platypi.Find(p => p.Id == id);
                return platypus == null ? string.Empty : platypus.Name;
            }
    
            public DPlatypus Add(DPlatypus platypus)
            {
                if (platypus == null)
                {
                    throw new ArgumentNullException("platypus");
                }
                platypus.Id = _nextId++;
                platypi.Add(platypus);
                return platypus;
            }
        }
  8. Now, add the Controller code; right-click the Controllers folder and select Add > Controller… In the "Add Scaffold" dialog, select "Web API 2 Controller - Empty" and mash the "Add" button. This invokes the "Add Controller" dialog. Name it "DPlatypusController" (or... etc.)
  9. In your Controller class, add this using statement (replacing “WebApplication974” with the appropriate namespace (the name of your project):
    using WebApplication974.Models;

Keep Going – Add the IoC/DI (CW) Pieces

  1. Add a field that holds an IDPlatypusRepository instance.
    public class DPlatypusController : ApiController
    {
        private readonly IDPlatypusRepository _duckbillRepository;
    } 
  2. Add a constructor that takes the IDPlatypusRepository type:
            public DPlatypusController(IDPlatypusRepository duckbillRepository)
            {
                if (duckbillRepository == null)
                {
                    throw new ArgumentNullException("duckbillRepository is null");
                }
                _duckbillRepository = duckbillRepository;
            } 
  3. Add a method to the Controller that will return the count of Platypus items:
    [Route("api/DPlatypus/{id}")]
    public string GetDPlatypusNameById(int Id)
    {
        return _duckbillRepository.Get(Id);
    }

    Now this is set up so that a call to: http://<IPAddress>:<port>/api/<ControllerName>/<IdVal> (such as, when testing on my local machine: "http://localhost:33181/api/DPlatypus/4") ...should return the appropriate value, such as "Platypup".

    However - we still have to replace the normal ASP.NET Web API routing with the DI/CW routing. So, we have to "infect" a couple of files first before this will work. Even prior to that, though (following the FTF (“First Things First”) pattern that I just made up), we need to install the necessary Castle packages into our project. So:

  4. Select Tools > Library Package Manager > Manage NuGet Packages for Solution > Online, and enter “Castle.Core” and install that package; then, after Castle.Core installs, do the same thing with “Castle.Windsor”.
  5. Now, change Global.asax.cs so that it looks like this:
    using System;
    using System.Reflection;
    using System.Web.Http;
    using System.Web.Mvc;
    using System.Web.Optimization;
    using System.Web.Routing;
    using Castle.Windsor;
    using Castle.Windsor.Installer;
    using Castle.MicroKernel.Resolvers.SpecializedResolvers;
    
    namespace WebApplication974
    {
        public class WebApiApplication : System.Web.HttpApplication
        {
            private static IWindsorContainer _container;
            protected void Application_Start()
            {
                ConfigureWindsor(GlobalConfiguration.Configuration);
                GlobalConfiguration.Configure(c => WebApiConfig.Register(c, _container));
    
                FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
                RouteConfig.RegisterRoutes(RouteTable.Routes);
                BundleConfig.RegisterBundles(BundleTable.Bundles);
            }
    
            public static void ConfigureWindsor(HttpConfiguration configuration)
            {
                _container = new WindsorContainer();
                _container.Install(FromAssembly.This());
                _container.Kernel.Resolver.AddSubResolver(new CollectionResolver(_container.Kernel, true));
                var dependencyResolver = new WindsorDependencyResolver(_container);
                configuration.DependencyResolver = dependencyResolver;
            }
    
            protected void Application_End()
            {
                _container.Dispose();
                base.Dispose();
            }
        }
    }
  6. Change App_Start\WebApiConfig.cs to look like this:
    using System.Web.Http;
    
    namespace WebApplication974
    {
        using System.Web.Http.Dispatcher;
        using Castle.Windsor;
        using DIPlumbing;
    
        public static class WebApiConfig
        {
            public static void Register(HttpConfiguration config, IWindsorContainer container)
            {
                MapRoutes(config);
                RegisterControllerActivator(container);
            }
    
            private static void MapRoutes(HttpConfiguration config)
            {
                config.MapHttpAttributeRoutes();
    
                config.Routes.MapHttpRoute(
                    name: "DefaultApi",
                    routeTemplate: "api/{controller}/{id}",
                    defaults: new { id = RouteParameter.Optional }
                    );
            }
    
            private static void RegisterControllerActivator(IWindsorContainer container)
            {
                GlobalConfiguration.Configuration.Services.Replace(typeof(IHttpControllerActivator),
                    new WindsorCompositionRoot(container));
            }
        }
    }
  7. Add a new class to the root of your project (right-click the project and select Add > Class...), naming it "WindsorDependencyResolver"
  8. Use this code:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web.Http.Dependencies;
    using Castle.Windsor;
    using Castle.MicroKernel.Registration;
    using System.Web.Http;
    using Castle.MicroKernel.Lifestyle;
    
    namespace WebApplication974
    {
        public class WindsorDependencyResolver : System.Web.Http.Dependencies.IDependencyResolver
        {
            private readonly IWindsorContainer _container;
    
            public WindsorDependencyResolver(IWindsorContainer container)
            {
                _container = container;
            }
    
            public IDependencyScope BeginScope()
            {
                return new WindsorDependencyScope(_container);
            }
    
            public object GetService(Type serviceType)
            {
                return _container.Kernel.HasComponent(serviceType) ? _container.Resolve(serviceType) : null;
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                if (!_container.Kernel.HasComponent(serviceType))
                {
                    return new object[0];
                }
    
                return _container.ResolveAll(serviceType).Cast<object>();
            }
    
            public void Dispose()
            {
                _container.Dispose();
            }
        }
    
        public class WindsorDependencyScope : IDependencyScope
        {
            private readonly IWindsorContainer _container;
            private readonly IDisposable _scope;
    
            public WindsorDependencyScope(IWindsorContainer container)
            {
                this._container = container;
                this._scope = container.BeginScope();
            }
    
            public object GetService(Type serviceType)
            {
                if (_container.Kernel.HasComponent(serviceType))
                {
                    return _container.Resolve(serviceType);
                }
                else
                {
                    return null;
                }
            }
    
            public IEnumerable<object> GetServices(Type serviceType)
            {
                return this._container.ResolveAll(serviceType).Cast<object>();
            }
    
            public void Dispose()
            {
                this._scope.Dispose();
            }
        }
    
        public class ApiControllersInstaller : IWindsorInstaller
        {
            public void Install(Castle.Windsor.IWindsorContainer container, 
            Castle.MicroKernel.SubSystems.Configuration.IConfigurationStore store)
            {
                container.Register(Classes.FromThisAssembly()
                 .BasedOn<ApiController>()
                 .LifestylePerWebRequest());
            }
        }
    }
  9. Right-click your project, select Add > New Folder, and name it DIPlumbing.
  10. Right-click that new folder, select Add > Class…, name it “WindsorCompositionRoot”.
  11. Add this code to it:
    using System;
    using Castle.Windsor;
    using System.Net.Http;
    using System.Web.Http.Controllers;
    using System.Web.Http.Dispatcher;
    
    namespace WebApplication974.DIPlumbing
    {
        public class WindsorCompositionRoot : IHttpControllerActivator
        {
            private readonly IWindsorContainer _container;
    
            public WindsorCompositionRoot(IWindsorContainer container)
            {
                _container = container;
            }
    
            public IHttpController Create(
                HttpRequestMessage request,
                HttpControllerDescriptor controllerDescriptor,
                Type controllerType)
            {
                var controller =
                    (IHttpController)_container.Resolve(controllerType);
    
                request.RegisterForDispose(
                    new Release(
                        () => _container.Release(controller)));
    
                return controller;
            }
    
            private sealed class Release : IDisposable
            {
                private readonly Action _release;
    
                public Release(Action release)
                {
                    _release = release;
                }
    
                public void Dispose()
                {
                    _release();
                }
            }
        }
    }
  12. Right-click your project and select Add > New Folder; name it "DIInstallers"
  13. Right-click the DIInstallers folder and select Add > Class..., naming it "RepositoriesInstaller"
  14. Add this code to RepositoriesInstaller:
    using Castle.MicroKernel.Registration;
    using Castle.MicroKernel.SubSystems.Configuration;
    using Castle.Windsor;
    using WebApplication974.Models;
    
    namespace WebApplication974.DIInstallers
    {
        public class RepositoriesInstaller : IWindsorInstaller
        {
            public void Install(IWindsorContainer container, IConfigurationStore store)
            {
                container.Register(
                    Component.For<IDPlatypusRepository>().ImplementedBy<DPlatypusRepository>().LifestylePerWebRequest());
            }
        }
    }

As the Germans say: "Und damit basta!" Or, as Kurt Vonnegut might have written: "And so it goes."

Note: There is a related tip dealing with swapping out the mapping of concrete implementations to interfaces here.

Photo Finish/Freeze Frame

For your edification and reading pleasure, here is the order in which the various pieces of the puzzle are executed (I put a breakpoint in each of them to find out the runtime chronology):

Prior to entering the "query string" in the browser:

  • Global.asax.cs
  • DIInstallers\RepositoriesInstaller
  • WindsorDependencyResolver
  • App_Start\WebApiConfig.cs
  • DIPlumbing\WindsorCompositionRoot

After entering the "query string" in the browser:

  • Models\DPlatypusRepository
  • Controllers\DPlatypusController

Riding Off Into the Pale Afterglow

Well, that about does it; it seems like a lot of “crazy” code, maybe, but believe it or not, the Castle Windsor and ASP.NET Web API frameworks are doing most of the work for you “behind the scenes.” You can now run the project; to verify that it actually works. Once the project’s Home Page opens in your browser, open another tab and enter: http://localhost:33181/api/DPlatypus/N (replacing the port number with whichever one Visual Studio has automatically assigned your project, and “N” with a number between 1 and 8 inclusive (corresponding to the 8 values added in the Repository). You will then see something like this if you replace N with everybody's favorite number, 7:

<string xmlns="http://schemas.microsoft.com/2003/10/Serialization/">Platydude</string>

So: Since the Platypus item with an Id of 7 is "Platydude", it works!

Although there may be millions of people who are dying to know the names of the eight canonical platypi, there are countless billions who really don’t give a darn. So this project serves no purpose in and of itself; as a starting point for “real” Web API apps that incorporate DI using Castle Windsor, though – there it shines! You can add controllers and repositories where desired, and expand this idea as far as you need to.

Now you know how to create an ASP.NET Web API app using DI/IOC with CW. Anybody for a bowl of alphabet soup?

My contention is that this is the most concise article anywhere showing how to do all of that. What we have lost along the way is a lot of the Whys and Wherefores – but remember, providing such was decidedly not my intention. Also, the ordering of the steps may not be the most logical - but that doesn't matter too much, as the project won't run right until all the code is added, anyway. At any rate, my intent (with or without an appended ion) was to break the world’s record for the shortest article on C# ASP.NET Web API MVC/MRC IOC/DI w. CW. I’m hoping to win the Golden Platypup (figurine of a Platypus baby*), which, if I do (why wouldn’t I?) will be proudly displayed at my domicile.

For the real scoop and skinny on DI in ASP.NET, I again refer you to The DI Whisperer’s book and his blog articles, particularly this one and this one.

The entire bare-bones (that’s a technical term, dating back to the prehistoric days of stone coding, when early developers would gnaw on sabre-tooth tiger bones while sitting bare in their dens, AKA caves**) project is added to this article as a download.

* Note: It's really so that a baby duckbilled platypus is called a platypup.

** The correct and proper terminology “bare bones” is sometimes confused by hearers of the term as “bear bones”*** but there is no irrefutable evidence that bruins were on the diet of the giants on whose shoulders we are now precariously perched.

*** A classic case of IPIMS (Illocution/Perlocution Impedence Mismatch Syndrome) run amok.

Kudos

Special thanks to Adam Connelly. Why? See his answer here for helping me to get my DI/CW project straightened out, which was catawamptuously chawed up before he came riding in to the accompaniment of The William Tell Overture (AKA "The Theme from the Lone Ranger").

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

B. Clay Shannon
Founder "Across Time & Space"
United States United States
Ideaman and Coder at Across Time & Space, creator of the Windows Store App "Photrax", which can be downloaded as a trial (7 days) from http://apps.microsoft.com/windows/en-us/app/photrax/75c18e6c-96bd-4607-ac43-531aab098ab4
 
Peripatetic and picaresque, I have lived in eight states; specifically, besides my native California (where I was born and where I now again reside) in chronological order: New York, Montana, Alaska, Oklahoma, Wisconsin, Idaho, and Missouri.
 
I am also a writer of both fiction (for which I use a nom de plume, "Blackbird Crow Raven", as a nod to my Native American heritage - I am "½ Cowboy, ½ Indian") and nonfiction: http://www.lulu.com/spotlight/blackbirdcraven
Follow on   Twitter   Google+   LinkedIn

Comments and Discussions

 
-- There are no messages in this forum --
| Advertise | Privacy | Terms of Use | Mobile
Web04 | 2.8.141223.1 | Last Updated 15 Jan 2014
Article Copyright 2014 by B. Clay Shannon
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid