OWIN + SignalR + Autofac

时间:2015-04-21 21:57:45

标签: c# signalr owin autofac

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

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

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

GlobalHost.ConnectionManager.GetHubContext<MyHub>()

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

public MyApiController : ApiController {
  public HubConfiguration HubConfig { get; set; } // Dependency injected by
                                                  // PropertiesAutowired()

  public IHubContext MyHubContext { 
    get { 
      return HubConfig
        .Resolver
        .Resolve<IConnectionManager>()
        .GetHubContext<MyHub>(); 
     } 
  }

  // ...

}

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

编辑:

我最终做的是:

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

app.MapSignalR("/signalr", hubConfig);

var builder2 = new ContainerBuilder();
builder2
  .Register(ctx => hubConfig.Resolver.Resolve<IConnectionManager>())
  .As<IConnectionManager>();

builder2.Update(container);

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

5 个答案:

答案 0 :(得分:15)

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

  • 我推荐使用强类型集线器。
  • 您需要添加具体内容 强类型集线器的注册。
  • 我不使用 GlobalHost
    • 而是使用为OWIN创建的配置 注册

集线器声明

public interface IMyHub
{
    // Any methods here for strongly-typed hubs
}

[HubName("myHub")]
public class MyHub : Hub<IMyHub>

集线器注册

来自您的Autofac注册

// SignalR Configuration
var signalRConfig = new HubConfiguration();

var builder = // Create your normal AutoFac container here

builder.RegisterType<MyHub>().ExternallyOwned(); // SignalR hub registration

// Register the Hub for DI (THIS IS THE MAGIC LINE)
builder.Register(i => signalRConfig.Resolver.Resolve<IConnectionManager>().GetHubContext<MyHub, IMyHub>()).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<IMyHub> InstanceHubContext { get; [UsedImplicitly] set; }

答案 1 :(得分:7)

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

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

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

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

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

// example class 
public class SampleClass {
    public SampleClass(IHubContext context) { }
}

// and registration for this class
builder.RegisterType<SampleClass>()
    .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<IHubContext>("MyHub");
    }));

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

// example class
public class SampleClass2
{
    public IHubContext Context { get; set; }
}

// registration for this case
builder.RegisterType<SampleClass2>()
    .PropertiesAutowired()
    .OnActivated(e => e.Instance.Context = e.Context.ResolveNamed<IHubContext>("MyHub"));

答案 2 :(得分:2)

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

builder.RegisterInstance(config.Resolver).As<IDependencyResolver>();
builder.Update(container);

然后用它来获取我的中心

Resolve<IDependencyResolver>().Resolve<IConnectionManager>().GetHubContext<MyHub>();

希望这有助于其他人

答案 3 :(得分:2)

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

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

 public IConnectionManager ConnectionManager { get; set; }

我注册如下:

 newBuilder.RegisterInstance(resolver.Resolve<IConnectionManager>());

其中resolvernew AutofacDependencyResolver(container);

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

var context = ConnectionManager.GetHubContext<WorkshopsHub>();

然后我致电context.Clients.All.clientMethod();

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

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

希望这有帮助!祝你好运!

答案 4 :(得分:1)

我在问题this answer上做了与How to configure Autofac and SignalR in a MVC 5 application类似的事情。

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

剩下的就是确保:

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

Heregist,其中包含所需的全部文件,并说明所需的NuGet软件包安装情况

工作示例如下:

public class ServiceModule : Module
{
    protected override void Load(ContainerBuilder builder)
    {
        // What ever registrations you need here

        // IMessageService interacts with the hub
        builder.RegisterType<MessageService>().As<IMessageService>();

        // 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<DefaultAssemblyLocator>().As<IAssemblyLocator>();

        // 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();
    }
}

public class Startup
{
    /// <summary>
    /// Perform the configuration 
    /// </summary>
    /// <param name="app">The application builder to configure.</param>
    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<Microsoft.AspNet.SignalR.IDependencyResolver>();

        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<MessageHub>();
    }

    public void BroadcastMessage(string message)
    {
        _hubContext.Clients.All.Message(message);
    }
}

public interface IMessage
{
    void Message(string message);
}

public class MessageHub : Hub<IMessage>
{
    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);
    }
}