Friday, January 25, 2013

Using NServiceBus with ASP.NET Web API

For those of you out there using NServiceBus, and are either converting from a framework like WCF or building from scratch, here's some nifty code that will let you inject the IBus dependency (as well as others you configure) on your ApiController implementations. This tutorial is an update to a previously written article by Karl Nilsson, found here.

To make this work, we'll be creating 3 new classes:
  • NServiceBusWebApiConfig
  • NServiceBusWebApiDependencyResolverAdapter
  • NServiceBusHttpControllerActivator
First, we'll create the NServiceBusHttpControllerActivator class. This class will make a little more sense once we get to dependency injection using NSB's container.
 
    using System;
    using System.Net.Http;
    using System.Web.Http;
    using System.Web.Http.Controllers;
    using System.Web.Http.Dispatcher;

    public class NServiceBusHttpControllerActivator : IHttpControllerActivator
    {
        public IHttpController Create(HttpRequestMessage request, HttpControllerDescriptor controllerDescriptor, Type controllerType)
        {
            return GlobalConfiguration.Configuration.Services
                 .GetService(controllerType) as IHttpController;
        }
    }


Next, we'll create the NServiceBusWebApiDependencyResolverAdapter class. This class is an implementation of IDependencyResolver, and will be used to string everything together between NServiceBus and the Web API framework. For more information on implementing IDependencyResolver, check out this article.

 
using System;
using System.Collections.Generic;
using System.Web.Http.Controllers;
using System.Web.Http.Dependencies;
using NServiceBus.ObjectBuilder;

public class NServiceBusWebApiDependencyResolverAdapter : IDependencyResolver
{
    private IBuilder builder;

    public NServiceBusWebApiDependencyResolverAdapter(IBuilder builder)
    {
        this.builder = builder;
    }

    public object GetService(Type serviceType)
    {
        if(typeof(IHttpController).IsAssignableFrom(serviceType))
        {
            return builder.Build(serviceType);
        }
         
        return null;
    }
 
    public IEnumerable<object> GetServices(Type serviceType)
    {
        if (typeof(IHttpController).IsAssignableFrom(serviceType))
        {
            return builder.BuildAll(serviceType);
        }
        else
        {
            return new List<object>();
        }
    }

    public IDependencyScope BeginScope()
    {
        return this;
    }

    public void Dispose() { /*No-op*/ }
}

Finally, we'll create the NServiceBusWebApiConfig class that will allow us to tie all this in to NServiceBus' normal startup. This class will register our new controller activator with NServiceBus, and will set the framework's dependency resolver to our newly created one.

  
using System;
using System.Linq;
using System.Web.Http;
using System.Web.Http.Controllers;
using System.Web.Http.Dispatcher;
using NServiceBus;

public static class NServiceBusWebApiConfig
{
    public static Configure ForWebApi(this Configure configure)
    {
        // Register our http controller activator with NSB
        configure.Configurer.RegisterSingleton(typeof(IHttpControllerActivator),
            new NServiceBusHttpControllerActivator());
 
        // Find every http controller class so that we can register it
        var controllers = Configure.TypesToScan
            .Where(t => typeof(IHttpController).IsAssignableFrom(t));
 
        // Register each http controller class with the NServiceBus container
        foreach (Type type in controllers)
            configure.Configurer.ConfigureComponent(type, DependencyLifecycle.InstancePerCall);

        //Configure any other dependencies you need here

        // Set the WebApi dependency resolver to use our resolver
        GlobalConfiguration.Configuration.DependencyResolver = new NServiceBusWebApiDependencyResolverAdapter(configure.Builder);
         
        // Required by the fluent configuration semantics
        return configure;
    }
}

Once these classes have been created, in the Global.asax.cs file of your Web API project, add the required NSB initialization code to the Application_Start method, taking care to note the use of "ForWebApi" rather than "WithWeb".

  
protected void Application_Start()
{
    AreaRegistration.RegisterAllAreas();

    WebApiConfig.Register(GlobalConfiguration.Configuration);
    FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
    RouteConfig.RegisterRoutes(RouteTable.Routes);
    BundleConfig.RegisterBundles(BundleTable.Bundles);

    Configure.With()
        .DefaultBuilder()
        .ForWebApi()   // <------ here is the line that registers everything
        .Log4Net()
        .XmlSerializer()
        .MsmqTransport()
            .IsTransactional(false)
            .PurgeOnStartup(false)
        .UnicastBus()
            .ImpersonateSender(false)
        .SendOnly();
}

That's it! Now, simply add a public property or a constructor argument of type IBus to your controllers that use NServiceBus to send messages, and you're all set!

No comments:

Post a Comment