OWIN + SignalR + Autofac

摘自: http : //docs.autofac.org/en/latest/integration/signalr.html :

“OWIN集成中的一个常见错误是使用GlobalHost。在OWIN中,您可以从头开始创建配置。在使用OWIN集成时,不应该在任何地方引用GlobalHost。”

这听起来很合理。 但是,如何从ApiController解析IHubContext ,就像通常的(非OWIN):

GlobalHost.ConnectionManager.GetHubContext()

我无法在任何地方找到这个参考,我现在唯一的方法是在同一个容器中注册HubConfiguration实例并执行以下操作:

 public MyApiController : ApiController { public HubConfiguration HubConfig { get; set; } // Dependency injected by // PropertiesAutowired() public IHubContext MyHubContext { get { return HubConfig .Resolver .Resolve() .GetHubContext(); } } // ... } 

但是,这对我来说似乎很啰嗦。 这样做的正确方法是什么? 更具体一点,是否有一种注册IConnectionManager的简洁方法?

编辑:

我最终做的是:

 var container = builder.Build(); hubConfig.Resolver = new AutofacDependencyResolver(container); app.MapSignalR("/signalr", hubConfig); var builder2 = new ContainerBuilder(); builder2 .Register(ctx => hubConfig.Resolver.Resolve()) .As(); builder2.Update(container); 

但我觉得必须有一种更简单的方法让IConnectionManager注入控制器。

这个答案有点迟了但是这里有。

  • 我推荐强类型集线器。
  • 您需要为强类型集线器添加特定注册。
  • 我不使用GlobalHost
    • 相反,我使用为OWIN注册创建的配置。

枢纽声明

 public interface IMyHub { // Any methods here for strongly-typed hubs } [HubName("myHub")] public class MyHub : Hub 

集线器注册

来自您的Autofac注册

 // SignalR Configuration var signalRConfig = new HubConfiguration(); var builder = // Create your normal AutoFac container here builder.RegisterType().ExternallyOwned(); // SignalR hub registration // Register the Hub for DI (THIS IS THE MAGIC LINE) builder.Register(i => signalRConfig.Resolver.Resolve().GetHubContext()).ExternallyOwned(); // Build the container var container = builder.Build(); // SignalR Dependency Resolver signalRConfig.Resolver = new Autofac.Integration.SignalR.AutofacDependencyResolver(container); app.UseAutofacMiddleware(container); app.MapSignalR("/signalr", signalRConfig); 

在后台代码中解析集线器

使用AutoFacs AutowiredProperties()扩展方法然后它可以解析正确的上下文(如果你愿意,也可以在构造函数中)。

 public IHubContext InstanceHubContext { get; [UsedImplicitly] set; } 

你可以做的是将一些重复的代码(我假设IHubContext也用于其他一些类,并以相同的方式提取)进入容器注册。

首先是注册IHubContext实例,我假设你在项目中有多个集线器。 在这种情况下,服务必须注册为命名服务 。

 builder .Register(c => c.Resolve().GetHubContext()) .Named("MyHub"); 

想要使用IHubContext类现在可以将其作为构造函数参数或属性接收。 但是我们必须告诉容器它应该注入哪个实例。 这可以通过多种方式在容器配置中完成

构造函数可以使用ResolvedParameter来正确选择IHubContext实现

 // example class public class SampleClass { public SampleClass(IHubContext context) { } } // and registration for this class builder.RegisterType() .WithParameter(new ResolvedParameter((pi, ctx) => { // only apply this to parameters of IHubContext type return pi.ParameterType == typeof(IHubContext); }, (pi, ctx) => { // resolve context return ctx.ResolveNamed("MyHub"); })); 

物业注入,也有点棘手。 需要在OnActivated回调中解析正确的实例,例如:

 // example class public class SampleClass2 { public IHubContext Context { get; set; } } // registration for this case builder.RegisterType() .PropertiesAutowired() .OnActivated(e => e.Instance.Context = e.Context.ResolveNamed("MyHub")); 

我做了类似于你自己,让它在我的Owin工作

 builder.RegisterInstance(config.Resolver).As(); builder.Update(container); 

然后使用它来获取我的中心

 Resolve().Resolve().GetHubContext(); 

希望这有助于其他人

我能找到的最简单的解决方案是这里的答案之间的混合,但对我来说似乎是处理这个并保持SignalR和Autofac SignalR集成的最佳实践的最佳方法:

在我想要集线器上下文的类中,我有一个属性

  public IConnectionManager ConnectionManager { get; set; } 

我注册如下:

  newBuilder.RegisterInstance(resolver.Resolve()); 

resolver是一个new AutofacDependencyResolver(container);

然后,我基本上使用与GlobalHost非常相似的ConnectionManager

 var context = ConnectionManager.GetHubContext(); 

然后我调用context.Clients.All.clientMethod();

通过这种方式,我可以轻松地从集线器外部更新客户端,具有易于维护的代码并遵循最佳实践(我认为并希望:D)。

我还考虑过在Startup上注册和解决它们,但这似乎是一项非常困难的任务,只有很少的好处(除了在成功时感觉良好)。

希望这可以帮助! 祝你好运!

我在关于如何在MVC 5应用程序中配置Autofac和SignalR的问题上做了类似的回答 。

由于我在IIS站点或自托管站点中运行,我遇到了另一个问题。 我在共享dll中创建了所有集线器和控制器,然后引用了该dll。 这导致Autofac的SignalR IAssemblyLocator无法恢复所需的程序集。 所以我在容器中注册了DefaultAssemblyLocator

其余的是确保:

  • Autofac.Integration.SignalR.AutofacDependencyResolver被解析为单元并用作HubConfiguration.Resolver
  • Microsoft.AspNet.SignalR.Infrastructure.ConnectionManager被解析为单个并在需要时注入 – 在此示例中的MessageService

以下是关于所需NuGet Package安装的完整必需文件和注释的要点

工作示例如下:

 public class ServiceModule : Module { protected override void Load(ContainerBuilder builder) { // What ever registrations you need here // IMessageService interacts with the hub builder.RegisterType().As(); // Register the controllers and the hubs builder.RegisterApiControllers(typeof(ServiceModule).Assembly); builder.RegisterHubs(typeof(ServiceModule).Assembly); // Register the default Assembly Locator since otherwise the hub will no be created by Signalr correctly IF it is NOT in the entry executables assembly. builder.RegisterType().As(); // Register Autofac resolver into container to be set into HubConfiguration later builder.RegisterType() .As() .SingleInstance(); // Register ConnectionManager as IConnectionManager so that you can get // hub context via IConnectionManager injected to your service builder.RegisterType() .As() .SingleInstance(); } } public class Startup { ///  /// Perform the configuration ///  /// The application builder to configure. public void Configuration(IAppBuilder app) { var builder = new ContainerBuilder(); builder.RegisterModule(new ServiceModule()); var container = builder.Build(); var config = new HttpConfiguration { DependencyResolver = new AutofacWebApiDependencyResolver(container), #if DEBUG IncludeErrorDetailPolicy = IncludeErrorDetailPolicy.Always, #endif }; app.UseAutofacMiddleware(container); app.UseAutofacWebApi(config); app.UseWebApi(config); var hubConfig = new HubConfiguration() { #if DEBUG EnableDetailedErrors = true, #endif }; hubConfig.Resolver = container.Resolve(); app.UseCors(CorsOptions.AllowAll); app.MapSignalR("/signalr", hubConfig); } } public interface IMessageService { void BroadcastMessage(string message); } public class MessageService : IMessageService { private readonly IHubContext _hubContext; public MessageService(IConnectionManager connectionManager) { _hubContext = connectionManager.GetHubContext(); } public void BroadcastMessage(string message) { _hubContext.Clients.All.Message(message); } } public interface IMessage { void Message(string message); } public class MessageHub : Hub { private readonly ILifetimeScope _scope; public MessageHub(ILifetimeScope scope) { _scope = scope; } public void Message(string message) { Clients.Others.Message(message); } public override Task OnConnected() { Clients.Others.Message("Client connected: " + Context.ConnectionId); return base.OnConnected(); } public override Task OnDisconnected(bool stoppedCalled) { Clients.Others.Message("Client disconnected: " + Context.ConnectionId); return base.OnDisconnected(stoppedCalled); } }