5

I am trying to configure an MVC 5 application to use SignalR 2.2.x and inject a service into my NotificationsHub. We are using Autofac for MVC but I am not sure on how to correctly configure this. Autofac documentation exists for NuGet Autofac.Integration.SignalR (3.0.2) and Autofac.Integration.Mvc (3.3.4).

What I am doing so far is register the hubs via:

ContainerBuilder builder = new ContainerBuilder();

// Register MVC controllers.
builder.RegisterControllers(typeof(MvcApplication).Assembly);

builder.RegisterType<ServiceForSignalRHub>().AsImplementedInterfaces();
builder.RegisterType<...>().AsImplementedInterfaces();

builder.RegisterHubs(Assembly.GetExecutingAssembly());

IContainer container = builder.Build();

DependencyResolver.SetResolver(new AutofacDependencyResolver(container));

However the call to .SetResolver(...) is ambigious as it exists in both MVC and SignalR integration packages. Now what? I am unable to confirm if the contructor injection of my service works for the Hub I am using.

EDIT

I can configure MVC5 with Autofac just fine. Where I get lost is how to do the same with SignalR, using the Autofac Signal integration NuGet. Please see below.

This is my startup class:

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        var defaultFactory = LogManager.Use<DefaultFactory>();
        defaultFactory.Directory("@D:\\NServiceBus\\ );
        defaultFactory.Level(LogLevel.Debug);

        var container = ConfigureAutofac(app);

        ConfigureServiceBus(container, app);

        app.UseAutofacMiddleware(container); // REGISTER WITH OWIN
        app.UseAutofacMvc();

        ConfigureAuth(app);

        GlobalConfiguration.Configuration.UseSqlServerStorage("hangfire");

        ConfigureSignalR(app);
    }
}

This is the part where I configure SignalR:

public partial class Startup
{
    public void ConfigureSignalR(IAppBuilder app)
    {
        var builder = new ContainerBuilder();

        builder.RegisterHubs(Assembly.GetExecutingAssembly()).PropertiesAutowired();
        builder.RegisterType<MaintenanceService>().As<IMaintenanceService>().PropertiesAutowired();

        var container = builder.Build();
        //var container = new AutofacContainer().Container;

        var resolver = new AutofacDependencyResolver(container);

        // Any connection or hub wire up and configuration should go here
        app.MapSignalR(new HubConfiguration
        {
            Resolver = resolver,
            EnableJSONP = true,
            EnableDetailedErrors = true,
            EnableJavaScriptProxies = true
        });

        builder.RegisterInstance(resolver.Resolve<IConnectionManager>());
    }
}

How can this be correctly done?

Lauraducky
  • 674
  • 11
  • 25
John
  • 3,591
  • 8
  • 44
  • 72

3 Answers3

3

You should tell SignalR explicitly to use the AutofacDependencyResolver when you are mapping it. I assume that you know that in your Startup class you have to call app.MapSignalR();

When you are mapping it, you should tell it to use the custom dependency resolver (the AutofacDependencyResolver).

Here's how I do it:

var resolver = new AutofacDependencyResolver(container);

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

This way, you are telling SignalR directly which dependency resolver to use.

I have a GitHub repo for SignalR Dependency Injection, but it's not configured to use MVC. Still, I think it will give you a hint in how to create your configuration.

Note: If you are using the OWIN Middleware, be sure not to use the GlobalHost static property AT ALL since it will have massive inconsistencies.

A common error in OWIN integration is use of the GlobalHost. In OWIN you create the configuration from scratch. You should not reference GlobalHost anywhere when using the OWIN integration.

Again, check the repo I gave you to see how to do this.

Hope this helps:) Best of luck!

Lauraducky
  • 674
  • 11
  • 25
radu-matei
  • 3,469
  • 1
  • 29
  • 47
  • thanks for the great answer. I have not gotten it to work on my end. Since I am using MVC5 my project setup is slightly different. I do use OWIN. One thing: I grabbed you repo and did a package-update to match the version number I use. Now the demo won't run. – John Nov 05 '15 at 16:15
  • Yes, it is because of the SignalR backplane which uses Redis. Remove all Redis related things from it. I will commit the changes as soon as I have the window, but until that, remove all things you find about Redis: the app.UseRedis from Startup and the Microsoft.AspNet.SignalR.Redis NuGet package. It will work:) Ask if any problems, I will try to update it:) Best of luck! – radu-matei Nov 05 '15 at 20:15
  • Thanks that did it. I am still unsure as to how to configure MVC Autofac alongside SignalR Autofac, but the next problem is that I get a SignalR JS error: "SignalR: Connection has not been fully initialized. Use .start().done() or .start().fail() to run logic after the connection has started." Any advice? Thanks again! – John Nov 06 '15 at 15:37
  • Please share the Startup class, the Autofac configuration and the client code if possible:) – radu-matei Nov 06 '15 at 15:39
  • well the error above is happening in the repo of yours. I had to update the JQuery JS reference to v2.1.4 in the index.html, as that didn't happen after the package-update. It runs, but that error is what I get in Chrome. Is there a way to confirm the TestHub had the ITest service inejcted? Debugging won't hit my breakpoint. – John Nov 06 '15 at 15:43
  • Still no luck here. I updated my question. Maybe you can see something that I don't. I appreciate any help. Thanks again!! – John Nov 07 '15 at 08:41
  • Sorry it took so long. Updated the repo, now it works properly for me. – radu-matei Nov 10 '15 at 22:26
  • My mistake was to use the MVC container and not a separate container for SignalR. Last question to ease my mind: in your sample you create an Autofac container, register ITest, and then you register SignalR's IConnectionManager at the end in yet another Autofac container, then you call update.Update(container). Why do you do this in that particular way? Thanks for your help. – John Nov 11 '15 at 18:14
  • 1. In my `AutofacContainer` class I register `ITest` and my hubs. 2. In order to tell SignalR which resolver to use instead of the default one, I need to create a new `AutofacDependencyResolver` which takes a `container` as a parameter. 3. In order to register `IConnectionManager`, I need to use both a container and a resolver ====> in `AutofacContainer` I didn't have a resolver so I couldn't resolve `IConnectionManager`, so I used a new container and updated the main one. Hope this helps:) – radu-matei Nov 11 '15 at 18:24
  • Do you have example to inject service or repository in Startup.cs? – Shalom Dahan Nov 26 '17 at 17:57
2

I ended up with this (pay attention to comments):

public partial class Startup
{
    public void Configuration(IAppBuilder app)
    {
        ConfigureAuth(app);

        IContainer container = AutofacConfig(app);
        app.UseAutofacMiddleware(container);
        app.UseAutofacMvc();
        DependencyResolver.SetResolver(new Autofac.Integration.Mvc.AutofacDependencyResolver(container));
        GlobalConfiguration.Configuration.DependencyResolver = new AutofacWebApiDependencyResolver(container);
        app.UseAutofacWebApi(GlobalConfiguration.Configuration);

        var hubConfiguration = new Microsoft.AspNet.SignalR.HubConfiguration()
        {
            // Resolve presolver from container
            Resolver = container.Resolve<Microsoft.AspNet.SignalR.IDependencyResolver>(),
        };
        app.MapSignalR(hubConfiguration);

        AreaRegistration.RegisterAllAreas();
        GlobalConfiguration.Configure(WebApiConfig.Register);

        FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
        RouteConfig.RegisterRoutes(RouteTable.Routes);
        BundleConfig.RegisterBundles(BundleTable.Bundles);
    }
}

_

private static IContainer AutofacConfig(IAppBuilder app)
{
    var builder = new ContainerBuilder();

    RegisterSignalR(builder);

    return builder.Build();
}

_

private static void RegisterSignalR(ContainerBuilder builder)
{
    // Register Autofac resolver into container to be set into HubConfiguration later
    builder.RegisterType<Autofac.Integration.SignalR.AutofacDependencyResolver>()
           .As<Microsoft.AspNet.SignalR.IDependencyResolver>()
           .SingleInstance();

    // Register ConnectionManager as IConnectionManager so that you can get
    // hub context via IConnectionManager injected to your service
    builder.RegisterType<Microsoft.AspNet.SignalR.Infrastructure.ConnectionManager>()
           .As<Microsoft.AspNet.SignalR.Infrastructure.IConnectionManager>()
           .SingleInstance();

    // Register hubs one by one
    builder.RegisterType<MessageNotificationsHub>().ExternallyOwned();
    builder.RegisterType<SystemNotificationsHub>().ExternallyOwned();
}

Thanks to @Nathan and his answer (originally found here) and logical thinking to figure out to register ConnectionManager as IConnectionManager

Lauraducky
  • 674
  • 11
  • 25
Andrii
  • 1,081
  • 1
  • 11
  • 24
0

Install nuget package Autofac.SignalR, for example:

<package id="Autofac.SignalR" version="3.0.2" targetFramework="net471" />

Register your hubs

// during creation of the IContainer, register signalr hubs automatically
var executingAssembly = Assembly.GetExecutingAssembly();
builder.RegisterHubs(executingAssembly);

// alternatively register hubs individually
// ExternallyOwned() ensures SignalR is allowed to control disposal of the hubs rather than Autofac.
builder.RegisterType<NotificationHub>().ExternallyOwned();

Set signalr service locator

// replace the Signalr dependency resolver once your IContainer 'container' is ready
GlobalHost.DependencyResolver = new AutofacDependencyResolver(container);

// or this can alternatively be set in the HubConfiguration instance when using OWIN IAppBuilder map.RunSignalR(hubConfiguration);
var hubConfiguration = new HubConfiguration
{
    Resolver = new AutofacDependencyResolver(container),
    //...
}

For more information:

https://autofaccn.readthedocs.io/en/latest/integration/signalr.html

https://learn.microsoft.com/en-us/aspnet/signalr/overview/advanced/dependency-injection

CRice
  • 12,279
  • 7
  • 57
  • 84