Using controllers from an external assembly in ASP.NET Web API - StrathWeb

Strath

June 20th, 2012

Using controllers from an external assembly in ASP.NET Web API

Because external controllers mean cleaner web application

In general, in my day-to-day job, I am working with large enterprise web applications, where clean projects, noise reduction and test driven development are holy. Therefore I have always been a big fan of placing MVC controllers outside the actual web application project, in a separate class library (or libraries).

With MVC it was very simple, but recently I faced this challenge in Web API, and turns out neither passing the assembly name through MapHttpRoute (not supported) or using something like ControllerBuilder.Current.DefaultNamespaces is not a viable option. So I turned to Henrik Nielsen for advice, and sure enough, he helped immediately. Let’s explore the solution.

The solution

The solution to our problem is to write a custom class implementing IAssemblyResolver, hook it up to the GlobalConfiguration and add a list of our additionally required assemblies there.

Let’s assume we have our controllers in a library called ControllersLibrary, and it is a regular class library, located at c:/libs/controllers/ControllersLibrary.dll.

If you are not used to that concept, think about that for a second. Controllers don’t really need Global.asax or anything else the web application provides; they might as well live elsewhere, and be loaded from there – think of it as plugging them into your application.

To achieve all this, following Henrik’s advice, you need a class implementing IAssemblyResolver, for example, your own class derived from System.Web.Http.Dispatcher.DefaultAssembliesResolver:

So effectively you are asking the framework to get its default assemblies (located under web app’s /bin/ folder), and merge your custom assembly in there as well.

There is a kicker though. It only works with the latest Web API source (nighly Nuget packages or build from Codeplex), not with RC.

Making this work with ASP.NET Web API RC

Why? The issue here is that the class from which we inherited from, DefaultAssembliesResolver, is internal in RC, and was only made public after the RC was released.

However, there is no reason why you wouldn’t write your own class implementing IAssembliesResolver. Let’s modify the above code to get there:

So this looks very similar to the bit above. Instead of inheriting from DefaultAssembliesResolver, we implement IAssembliesResolver directly, and read the assemblies from AppDomain ourselves (that’s what DefaultAssembliesResolver does), and merge in a List with our custom assembly.

Now we are ready to plug this in, and as expected this has to be done in Global.asax, or any other class you have to manipulate GlobalConfiguration.

Trying it out

Now assuming that:
ValuesController is located under main web application project
ExternalController is located in C:\libs\controllers\ControllersLibrary.dll

You can see that it’s easy to navigate to both of them:

Summary

It is really easy to separate your controllers from your web application. While this might seem like an overkill in small projects, in large enterprise scale applications, it is an absolute lifesaver. I hope I don’t have to convince anyone who much it helps maintainability, testability and mess/noise reduction.

Enjoy!

Be Sociable, Share!

  • Pingback: Dew Drop – June 21, 2012 (#1,349) | Alvin Ashcraft's Morning Dew

  • Pingback: | TechBlog

  • Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1131

  • http:///www.alexsandro.com.br Alexsandro (@alexsandro_xpt)

    Did you have same sample using MEF?

    Thz!

  • Victor

    What is needed for a controller in a class library besides inheriting from ApiController class?

    I created the following web api controller as a class library

    public class ExternalController : ApiController
    {
    // GET api/values
    public IEnumerable Get()
    {
    return new string[] { “value1 from library”, “value2 from library” };
    }

    // GET api/values/5
    public string Get(int id)
    {
    return “value from libary”;
    }
    }

    but it is not working I am getting the following error:

    No HTTP resource was found that matches the request URI ‘http://localhost:58452/api/external’.No type was found that matches the controller named ‘external’.

    I created a AssembliesResolver class as you described in your article and I added
    GlobalConfiguration.Configuration.Services.Replace(typeof(IAssembliesResolver), new AssembliesResolver());

    in the global.asax file

    Thanks in advance for your help

    Victor

    • Jeff L8

      I am getting the same error as you, did you ever get this working?

      No HTTP resource was found that matches the request URI ‘http://localhost:2524/api/external’.

      No type was found that matches the controller named ‘external’.

      Is there a way to do this with an assembly added as a reference in your Web API project or do you have to add an Assembly.LoadFrom(fileLocation);?

  • Victor

    I got it to work. it seems that I needed the implementation for ASP.NET Web API RC instead. It is strange because recently I upgraded my solution to the latest MVC 4 (RTM)

  • Daniel Silva

    You said that “Controllers don’t really need Global.asax or anything else the web application provides”. But what about Dependency Injection and other stuff like this that needs to be initialized for the API?

    • http://www.strathweb.com/ Filip W

      The typical IoC setup in any application is to have a single composition root – and have all dependencies defined there, and then the IoC container simply propagates them through all the layers of your application, regardless of in which DLL the dependencies are needed. See more about this here – http://stackoverflow.com/questions/6277771/what-is-a-composition-root-in-the-context-of-dependency-injection

      You definitely do not have to set up dependency injection into the controllers in the same project/DLL as the controllers.

  • http://twitter.com/carlclark267 Carl Clark

    Thanks for this post it helped me get my dynamic Controller loading working. I used you RTM code version but had to revise it to return baseAssemblies to get it to work.

    Thanks again
    Carl

  • Michael Hand

    Thank you!

    BTW line 7 in the first listing should be assemblies.Add(controllersAssembly);

  • Tyson Nero

    I would also check out Autofac WebApiIntegration for resolving dependencies: https://code.google.com/p/autofac/wiki/WebApiIntegration

  • Algis

    Hi, could someone share working sample of this supposedly simple stuff, but I can’t get it working…

  • Algis

    Sorry, got it working, but had to use “RC” way as well…

  • Jon Pawley

    Filip, many thanks for this blog post. I ended up not needing to use what you had described, as I have wired up Autofac as my WebAPI “dependency resolver”, but this great blog post pointed me in the right direction. Awesome work.

  • Mike Cattle

    Great solution, but could you not also use plain ol’ inheritance to do the trick? As in, write “empty” controller classes in your Web API assembly that inherit from the controller classes in the other assembly?