31
votes

I'm trying to use OWIN, SignalR and Autofac in a single project.

I'm setting things up with regards to signalR as follows:

       // Create the AutoFac container builder:
        var builder = new ContainerBuilder();

        // ...[Register various other things in here]...

        // register signalR Hubs
        builder.RegisterHubs(Assembly.GetExecutingAssembly());

        // Build the container:
        var container = builder.Build();

        // Configure SignalR with the dependency resolver.
        app.MapSignalR(new HubConfiguration
        {
            Resolver =  new AutofacDependencyResolver(container)
        });

My issue is that when I use the Autofac SignalR integration, I can no longer get a signalR Hub Context on the server (in a webapi controller for example) and so can't send messages from server side to the connected clients. Something like the following is how I do this when I'm not using the Autofac signalR integration:

var hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
hubContext.Clients.All.notification("Test Message");

But this doesn't work when I add Autofac into the mix - I don't get any error message and I do seem to get a hubContext, but calls on it don't actually seem to get to the clients.

If I comment out the use of the dependency resolver for signalR in the call to MapSignalR, the call to GetHubContext works again and messages reach the signalR clients sucessfully, but of course I can no longer use IoC on my hubs. e.g.

        // Configure SignalR with the dependency resolver.
        app.MapSignalR(new HubConfiguration
        {
            // Resolver =  new AutofacDependencyResolver(container)
        });

Can anybody tell me why using the AutofacDependencyResolver stops GlobalHost.ConnectionManager.GetHubContext from working correctly??

NOTE: One other thing I have tried is instead of using GlobalHost.ConnectionManager.GetHubContext() I tried injecting an IConnectionManager into the webapi controller from which I want to send a message to clients, then calling GetHubContext on that, but Autofac couldn't resolve the IConnectionManager.

I did find the following article by Piotr Szmyd which apparently allows this:

http://www.szmyd.com.pl/blog/wiring-signalr-with-autofac

but this appears to be based on obsolete signalR builds, and while there seems to be a nuget package for it here:

http://www.nuget.org/packages/SignalR.AutoFac/

it also seems well out of date.

3
these are starting to feel like duplicatesLars Höppner

3 Answers

33
votes

If you use a custom dependency resolver with SignalR, you can no longer use GlobalHost unless you modify it:

GlobalHost.DependencyResolver = new AutofacDependencyResolver(container);
IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();

// A custom HubConfiguration is now unnecessary, since MapSignalR will
// use the resolver from GlobalHost by default.
app.MapSignalR();

If you don't want to modify GlobalHost, you will have to manually resolve your IConnectionManager:

IDependencyResolver resolver = new AutofacDependencyResolver(container);
IHubContext hubContext = resolver.Resolve<IConnectionManager>().GetHubContext<MyHub>();

app.MapSignalR(new HubConfiguration
{
    Resolver = resolver
});
2
votes

For a complete answer, with SignalR, Autofac, and OWIN, I did the following:

// register IPersistantConnectionContext for access to SignalR connection
// and IDependencyResolver to enable inection of the container into its
// construction for the config object.
var builder = new ContainerBuilder();
builder.RegisterType<Autofac.Integration.SignalR.AutofacDependencyResolver>()
    .As<IDependencyResolver>()
    .SingleInstance();
builder.Register((context, p) =>
        context.Resolve<IDependencyResolver>()
            .Resolve<Microsoft.AspNet.SignalR.Infrastructure.IConnectionManager>()
            .GetConnectionContext<SignalRConnection>());

// ... other registrations

var container = builder.Build();

var signalrConfiguration = new ConnectionConfiguration
{
    Resolver = container.Resolve<IDependencyResolver>(),
};

app.UseAutofacMiddleware(container);

app.MapSignalR<SignalRConnection>("/signalr", signalrConfiguration);

// ... other middleware

In my controllers, I included a parameter of the type IPersistentConnectionContext and the correct instance is injected.

I was using a PersistentConnection, but it should be similar for Hubs.

-1
votes

To expand on Nathan's answer, I am using something similar with the key line being

builder.RegisterHubs(Assembly.GetExecutingAssembly()).SingleInstance();

in Startup.cs.

The line "SingleInstance()" ensures that only a single "instance" of the hub is used throughout the application.

Then I just use straightforward dependency injection into the constructor of the controller to get a pointer to the hub.