【问题标题】:OWIN + SignalR + AutofacOWIN + SignalR + Autofac
【发布时间】:2015-06-29 07:18:08
【问题描述】:

取自: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 注入控制器。

【问题讨论】:

  • 看来您也有 Web API。是这样吗?
  • 请问您为什么不进行构造函数注入?在这里,您对容器有很大的依赖性。
  • @TravisIllig:是的,很抱歉,如果我没有明确说明这一点。当我说 ApiController 时,我指的是 WebAPI 控制器。
  • @Anders 我通常会这样做,Web API 控制器除外。我更喜欢让它们带有无参数的构造函数,以允许更清晰的子类化。
  • @itim 你最后采用了什么解决方案?

标签: c# signalr owin autofac


【解决方案1】:

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

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

剩下的就是确保:

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

Here 是一个 gist,其中包含所需的整个文件并评论所需的 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);
    }
}

【讨论】:

  • 如果我的服务在另一个项目中,相同的解决方案,这将如何工作?
【解决方案2】:

这个答案有点晚了,但这里有。

  • 我推荐强类型集线器。
  • 您需要添加特定的 强类型集线器的注册。
  • 我不使用 全球主机
    • 相反,我使用为 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; }

【讨论】:

  • 谢谢。杰出的。我使用稍微修改的解决方案:builder.RegisterType&lt;AutofacDependencyResolver&gt;().As&lt;Microsoft.AspNet.SignalR.IDependencyResolver&gt;().SingleInstance();builder.Register(context =&gt; context.Resolve&lt;Microsoft.AspNet.SignalR.IDependencyResolver&gt;().Resolve&lt;IConnectionManager&gt;().GetHubContext&lt;MyHub, IMyHub&gt;()).ExternallyOwned(); 并使用 autofac 来解析解析器:hubConfiguration.Resolver = container.Resolve&lt;Microsoft.AspNet.SignalR.IDependencyResolver&gt;(); httpConfiguration 相同以保持一致性。
【解决方案3】:

我能找到的最简单的解决方案是以某种方式混合此处的答案,但对我来说似乎是处理此问题并保持 SignalR 和 Autofac SignalR 集成的最佳实践的最佳方法:

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

 public IConnectionManager ConnectionManager { get; set; }

我注册如下:

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

其中resolvernew AutofacDependencyResolver(container);

然后,我基本上用ConnectionManagerGlobalHost很像:

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

然后我打电话给context.Clients.All.clientMethod();

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

我也想过在 Startup 注册并解决它们,但这似乎是一项非常困难的任务,几乎没有什么好处(除了成功时感觉很好)。

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

【讨论】:

    【解决方案4】:

    我和你自己做过类似的事情,这让它在 Owin 中为我工作

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

    然后用它来获取我的集线器

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

    希望这可以帮助其他人

    【讨论】:

      【解决方案5】:

      您可以做的是将其中一些重复代码(我假设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"));
      

      【讨论】:

      • 这看起来更简洁,但仍然有点过于冗长,我觉得可能有更简单的方法。我的示例有些简化,但我真正想要的是 IConnectionManager 实例如果可能注入到控制器中(现在我无法在构建 Autofac 容器后不更新它,例如:var connManager = hubConfig.Resolver.Resolve&lt;IConnectionManager&gt;(); builder.Register(ctx =&gt; connManager); builder.Update(container)。尝试:builder.Register(ctx =&gt; ctx.Resolve&lt;IConnectionManager&gt;()) 没有为我工作。
      • 啊,我错误地认为,所有 SignalR 内部都注册在 Autofac 容器中。事实上,AutofacDependencyResolver 将内部 SignalR 组件的构造委托给来自 SignalR 本身的DefaultDependencyResolver。因此,您的方法看起来有效并且与 Microsoft 的示例一致(此方法适用于 Ninject,但看起来与您正在做的类似 asp.net/signalr/overview/advanced/dependency-injection)。另一种选择是在容器中注册内部 SignalR 服务,但这可能会在 SignalR 更新期间带来维护问题。
      猜你喜欢
      • 2019-02-09
      • 2020-04-18
      • 1970-01-01
      • 2017-10-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-12-18
      相关资源
      最近更新 更多