Thursday, 18 December 2014

Getting NServiceBus, SignalR and Autofac to work together

I’m currently working on an existing web app that we’re updating to add in NServiceBus and SignalR. A broad brush overview is shown below…

image

Here a request is sent from the browser which goes to a SignalR hub inside the website. That request is then sent onto the bus and some back end magic happens, which ultimately ends up in a response being sent to the bus. This is picked up by the website and a response is pushed down to the client courtesy of SignalR.

If we look into what’s happening inside the website we see the following…

image

The hub implements an interface that’s injected into the message handler, so that the handler can call back to the hub when the response message comes in, which ultimately then makes the hub call back to the browser to finish the loop.

Getting all this setup was a bit of a bother and I went down a few blind alleys before getting it all to work properly, hence documenting it so that I can hopefully short-circuit someone else’s work.

In pseudocode this is what happens…

  • The browser makes a request to the hub.
  • The hub code creates a unique Request Id, and stuffs this into a dictionary, mapping this Request Id to the SignalR Context.ConnectionId.
  • The hub then sends a message onto the bus, this contains the Request Id.
  • After the back-end has processed this message a response is placed on the bus, again containing the Request Id.
  • The message handler receives the message and uses the callback interface and Request Id to build a dynamic object that we can call the browser back with.
  • The message handler then makes the callback.

For this example I’m doing the callback within the message handler, but you could encapsulate this fully within the Hub too if you prefer. OK, enough explanation, on to some code…

    public interface ICallback
{
dynamic GetCallback(Guid requestId);
}



This interface is used to communicate between the message handler and the Hub. In addition I added a “registration” service that is used to map a Request Id to a client connection Id…

    public interface IRegistration
{
void Register(Guid id, string data);

string GetRegistration(Guid id);
}



The implementation of this service was a simple dictionary, this however should be beefed up as it needs a “tidy up” mechanism that will ensure that data is removed from this dictionary when the client disappears. The hub then is as shown below…

    public class WibbleHub : Hub, ICallback
{
public WibbleHub(IBus bus, IRegistration registration)
{
_bus = bus;
_reg = registration;
}

public void RequestWibble(string text)
{
Guid requestId = Guid.NewGuid();

_reg.Register(requestId, Context.ConnectionId);

_bus.Send(new WibbleMessage { RequestId = requestId, Text = text});
}

public dynamic GetCallback(Guid requestId)
{
dynamic callback = null;
string clientId = _reg.GetRegistration(requestId);

if (null != clientId)
callback = Clients.Client(clientId);

return callback;
}

private IBus _bus;
private IRegistration _reg;
}



The hub imports the bus and registration services, and the RequestWibble method is the one we’re calling from the web client. This registers the mapping between the Request Id and the client Connection Id, and then sends a message onto the bus.


The GetCallback method uses the registration service to find the client connection Id, then returns a dynamic object hooked to that client if the Id matches one that has been registered.


The message handler is fairly simple too…

    public class WibbleHandler : IHandleMessages<WibbleMessage>
{
public WibbleHandler(ICallback callback)
{
_callback = callback;
}

public void Handle(WibbleMessage message)
{
var cb = _callback.GetCallback(message.RequestId);

if (null != cb)
{
try
{
cb.wibbleSucceeded(message.Text);
}
catch (Exception ex)
{
int i = 0;
}
}
}

private ICallback _callback;
}



The handler imports the ICallback interface (exposed by the Hub), and when it handles the incoming message it calls the GetCallback() method to retrieve the dynamic object that allows us to call down to the web client.


The last piece of the puzzle is wiring this up, and that was the main thing that took time to get right, mainly down to my misunderstanding of how NServiceBus does its stuff. I created a DependenciesConfig file as follows…

    public static class DependencyConfig
{
public static void BuildDependencies()
{
var container = BuildContainer();
DependencyResolver.SetResolver(new AutofacDependencyResolver(container));
}

public static IContainer BuildContainer()
{
if (null == _container)
{
var builder = new ContainerBuilder();

builder.RegisterControllers(Assembly.GetExecutingAssembly())
.PropertiesAutowired();

builder.RegisterType<WibbleHub>()
.AsSelf()
.As<ICallback>()
.ExternallyOwned()
.SingleInstance();

builder.RegisterType<Registration>().As<IRegistration>().SingleInstance();

_container = builder.Build();

var config = new BusConfiguration();
config.UsePersistence<InMemoryPersistence>();
config.UseTransport<RabbitMQTransport>();
config.UseContainer<AutofacBuilder>(c => c.ExistingLifetimeScope(_container));

var bus = Bus.Create(config);
bus.Start();

}

return _container;
}

private static IContainer _container;
}



This has a static BuildContainer method that creates the AutoFac container, and there are a couple of things of note. First off is the registration of the hub, this is registered as a singleton, as is the registration service too.


Then (and this is the bit that I got wrong) is the configuration of the bus. First off, the container is built before the bus is constructed, so the bus itself is not constructed as part of the container itself, it lives “outside”. The bus is setup to hook to the Autofac container using the UseContainer() member, this links the bus to the registered components, and means that when a message comes in on the bus that it can resolve all dependencies such as the ICallback interface exposed by the hub.


Now to the voodoo magic. In the above there is nowhere that the bus itself is registered with the Autofac container, so conventional wisdom would dictate that any components requiring IBus wouldn’t be able to resolve it, so for example this controller shouldn’t work…

    public class DefaultController : Controller
{
public DefaultController(IBus bus)
{
_bus = bus;
}

public ActionResult Index()
{
return View();
}

private IBus _bus;
}



But it does work. How is a mystery. After a bit of searching I came across this post on Stack Overflow that indicates that NSB does this for you. That’s great, but also somewhat confusing for anyone who doesn’t know this beforehand, which presumably is everyone who uses NSB and Autofac, or any IOC container probably as people are used to registering dependencies themselves, rather than some magic happening. Personally I’m not a fan of this magic, it’ll most likely trip you up. However, with this in place it all works as expected – I can then pop some Javascript into my client and everything works as expected.


There’s one more class to add and that’s the startup for Owin, so that we can get SignalR up and running properly…

    public class Startup
{
public void Configuration(IAppBuilder app)
{
var container = DependencyConfig.BuildContainer();

GlobalHost.DependencyResolver = new AutofacDependencyResolver(container);
app.UseAutofacMiddleware(container);
app.MapSignalR(new HubConfiguration { EnableDetailedErrors = true });
}
}



The demo (attached) has a simple form where you can enter text, click a button and lo and behold a message is displayed on screen. That’s pretty useless, but at least is shows how NServiceBus, Autofac and SIgnalR can play nicely together. I’m using RabbitMQ as my messaging infrastructure, feel free to bring your own along if you would prefer.


Hope this helps someone!

2 comments:

acangel said...

Thanks, this was exactly what i needed to get my hubs being called from my business logic.

trustworthy essay writing service said...

So much coding in this post and quite hard to understand the coding part, also explain each block of coding with an explanation. Thanks