Autofac - SignalR with OWIN. getting reference to

2019-06-10 04:30发布

问题:

I have an existing MVC 4 application that uses Autofac and I have other assemblies that have autofac Modules in. Without SignalR/autofac integration, my application works perfectly as far as IOC is concerned. I have added in SignalR hubs which I am trying to inject dependencies into via the constructor. I have followed the documentation and implemented the code example from.

http://autofac.readthedocs.org/en/latest/integration/signalr.html

Which results in this following class:

public class SignalRConfig
{
    public void Configuration(IAppBuilder app)
    {
        var builder = new ContainerBuilder();
        var config = new HubConfiguration();

        Assembly thisAssembly = typeof(SignalRConfig).Assembly;

        builder.RegisterHubs(thisAssembly);

        var container = builder.Build();
        config.Resolver = new AutofacDependencyResolver(container);


        app.UseAutofacMiddleware(container);
        app.MapSignalR("/signalr", config);
    }
}

This is hooked up with this line which is added to my AssemblyInfo.cs

[assembly: OwinStartup(typeof(SignalRConfig))]

But its not working at runtime as my hub does not have a parameterless constructor and it cannot resolve the dependency. My assumption is that I am creating a new ContainerBuilder (as per docs) and that this instance has nothing registered on it. I actually want the reference to the ContainerBuilder that is passed around my modules that knows about all my registered types. I just dont know how to do it. The existing IOC code runs from the application_start on the global.asax and the SignalRConfig runs at the same time. I dont want to hold the ContainerBuilder in a singleton as it feels dirty but I cant find any other solution.

Here is a section of my nuget package config showing the version numbers etc

  <package id="Autofac" version="3.5.2" targetFramework="net451" />
  <package id="Autofac.Mvc4" version="3.1.0" targetFramework="net40" />
  <package id="Autofac.Owin" version="3.1.0" targetFramework="net451" />
  <package id="Autofac.SignalR" version="3.0.1" targetFramework="net451" />
  <package id="Autofac.WebApi" version="3.1.0" targetFramework="net40" />
  <package id="Microsoft.AspNet.Mvc" version="4.0.30506.0" targetFramework="net40" />
  <package id="Microsoft.AspNet.Razor" version="2.0.30506.0" targetFramework="net40" />
  <package id="Microsoft.AspNet.SignalR" version="2.2.0" targetFramework="net451" />
  <package id="Microsoft.AspNet.SignalR.Core" version="2.2.0" targetFramework="net451" />
  <package id="Microsoft.AspNet.SignalR.JS" version="2.2.0" targetFramework="net451" />
  <package id="Microsoft.AspNet.SignalR.SystemWeb" version="2.2.0" targetFramework="net451" />
  <package id="Microsoft.AspNet.WebApi" version="4.0.20710.0" targetFramework="net40" />
  <package id="Microsoft.AspNet.WebApi.Client" version="4.0.30506.0" targetFramework="net40" />
  <package id="Microsoft.AspNet.WebApi.Core" version="4.0.30506.0" targetFramework="net40" />
  <package id="Microsoft.AspNet.WebApi.WebHost" version="4.0.30506.0" targetFramework="net40" />
  <package id="Microsoft.AspNet.WebPages" version="2.0.30506.0" targetFramework="net40" />
  <package id="Microsoft.Owin" version="3.0.0" targetFramework="net451" />
  <package id="Microsoft.Owin.Host.SystemWeb" version="2.1.0" targetFramework="net451" />
  <package id="Microsoft.Owin.Security" version="3.0.0" targetFramework="net451" />
  <package id="Owin" version="1.0" targetFramework="net451" />

回答1:

It sounds like you're mixing legacy IIS hosting mechanisms (Application_Start) with new OWIN hosting (OwinStartup) and that's going to be hard to rectify.

If you're hosting your app using OWIN, I'd recommend switching the whole startup to OWIN (at least the startup logic) so you don't have to try to synchronize access to the container or pass things around. You can still keep other event handlers (Application_Error and such) in Global.asax if you like, just unify the startup logic in your OWIN startup class.

You can still keep things fairly tidy by separating "layers" into different startup classes that get called.

public static class Startup
{
  public static void Configuration(IAppBuilder app)
  {
    var container = DependencyConfiguration.Configure(app);
    SignalRConfiguration.Configure(app, container);
    MvcConfiguration.Configure(app, container);
  }
}

public static class DependencyConfiguration
{
  public static IContainer Configure(IAppBuilder app)
  {
    var builder = new ContainerBuilder();
    builder.RegisterHubs(typeof(SignalRConfiguration).Assembly);
    var container = builder.Build();
    app.UseAutofacMiddleware(container);
    return container;
  }
}

public static class SignalRConfiguration
{
  public static void Configure(IAppBuilder app, IContainer container)
  {
    var config = new HubConfiguration();
    config.Resolver = new AutofacDependencyResolver(container);
    app.MapSignalR("/signalr", config);
  }
}

...and so on. The important bit is that you've got all the dependencies registered at once and the Autofac middleware is added first. You can then pass the container around to be used in individual application type setups (like you see there with SignalR).

Then bootstrap the whole thing with the main startup class:

[assembly: OwinStartup(typeof(Startup))]

There's a lot of benefit to this. You sidestep the race condition you're running into; you don't need to try and synchronize or reorder things; and you have a lot of control. It also sets you up in a better place for ASP.NET vNext if you want to port, since everything's OWIN then.