sethreidnz
sethreidnz

Reputation: 655

Autofac injection of IHubContext SignalR MVC

I am trying to get SignalR working with Autofac. I have a repo of a stripped back version of what I have done here:

https://github.com/justsayno/signalr-autofac

This is adapted from which works using GlobalHost:

https://github.com/sstorie/experiments/tree/master/angular2-signalr

That works just fine using hte GlobalHost object. I have attempted to follow the documentation on Autofac's site about how to inject SignalR services but I cannot get it to work. This is my config file for registering my dependencies:

public static IContainer RegisterDependencies()
    {
        // Create your builder.
        var builder = new ContainerBuilder();

        //register controllers in DI
        builder.RegisterControllers(Assembly.GetExecutingAssembly());
        builder.RegisterApiControllers(Assembly.GetExecutingAssembly());

        // Register SignalR hubs
        builder.RegisterHubs(Assembly.GetExecutingAssembly());

        return builder.Build();
    }

And I call this from startup.cs:

public class Startup
{
    public static IContainer Container { get; set; }
    public void Configuration(IAppBuilder app)
    {
        var config = GlobalConfiguration.Configuration;

        // configure IoC container
        Container = AutofacConfiguration.RegisterDependencies();

        //set dependency resolver from WebAPI and MVC
        config.DependencyResolver = new AutofacWebApiDependencyResolver(Container);
        DependencyResolver.SetResolver(new Autofac.Integration.Mvc.AutofacDependencyResolver(Container));

        //register Autofac Middleware
        app.UseAutofacMiddleware(Container);

        app.Map("/signalr", a =>
        {
            a.UseCors(CorsOptions.AllowAll);
            var hubConfiguration = new HubConfiguration
            {
                Resolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(Container)
            };
            a.RunSignalR(hubConfiguration);
        });

        // This server will be accessed by clients from other domains, so
        //  we open up CORS
        //
        app.UseCors(CorsOptions.AllowAll);

        // Build up the WebAPI middleware
        //
        var httpConfig = new HttpConfiguration();
        httpConfig.MapHttpAttributeRoutes();
        app.UseWebApi(httpConfig);
    }

}

And I have a controller that has this injected into its contstructor:

 public TaskController(IHubContext hubContext)
    {
        // Commented out hard coded global host version
        //
        //_context = GlobalHost.ConnectionManager.GetHubContext<EventHub>();

        // using DI
        _context = hubContext;
    }

This however gives me an error about the controller not having a default constructor (so I assume this is a problem with my IHubContext not being found).

Any help would be great. I have made a repo of what I am talking about that can be found here for a complete solution:

https://github.com/justsayno/signalr-autofac

Upvotes: 3

Views: 2461

Answers (1)

Sam Storie
Sam Storie

Reputation: 4564

I've been able to do this, but it's not as clean as I would normally like. The primary issue here is that IHubContext does not reflect what specific type of hub it is...it's just a generic handle. So what I've done is to create a named registration within Autofac to register IHubContext using a specific SignalR hub:

builder.Register(ctx => 
    ctx.Resolve<IDependencyResolver>()
       .Resolve<IConnectionManager>()
       .GetHubContext<EventHub>())
       .Named<IHubContext>("EventHub");

Then I set up specific registrations for the objects I'll be injecting this hub into. This could be either an ApiController, or perhaps some other service that is then injected into controllers using the standard Autofac/WebApi integration. This "specific" registration is the piece I don't like, but I don't know of a cleaner way around it. Here's what that would look like:

builder.RegisterType<TaskController>()
       .WithParameter(
           new ResolvedParameter(
               (pi, ctx) => pi.ParameterType == typeof(IHubContext),
               (pi, ctx) => ctx.ResolveNamed<IHubContext>("EventHub")
       )
 );

Now Autofac should recognize that you want to inject IHubContext into the TaskController and provide the specific registration named EventHub when injecting it.

Upvotes: 5

Related Questions