【问题标题】:Call a SignalR method from a different AppDomain从不同的 AppDomain 调用 SignalR 方法
【发布时间】:2018-02-03 01:19:37
【问题描述】:

我有一个使用 SignalR 用 C# 编写的工作 Web 服务器。这是一个自托管的 Owin 应用程序。一切正常。

现在我必须在不同的 AppDomain 中重新定位我的控制器。这破坏了 SignalR 部分,因为 GlobalHost 仅在一个 AppDomain 中保持不变,并且不可序列化(因此我无法将其传递给其他 AppDomain)。

我找到了很多关于从控制器/其他类/其他类中调用 SignalR 集线器方法的示例/问题/教程,但在默认 AppDomain(初始化 Owin 应用程序的地方)之外没有任何内容。

如何从与 Hub 不同的 AppDomain 中设置的控制器向客户端发送消息?

【问题讨论】:

    标签: c# signalr owin appdomain


    【解决方案1】:

    我找到的解决方案非常简单:对于任何 AppDomain 间的通信,我们需要能够跨越 AppDomain 边界的东西,即数据或类的代理。

    因此,以下工作:

    1. 创建一个扩展 MarshalByRefObject 的类:当我们将它传递给不同 AppDomain 中的另一个类时,这将自动创建该类的代理

      public class InterAppDomainForSignalR : MarshalByRefObject
      {
          public void Publish(PublishParameter param) {
              var clients = GlobalHost.ConnectionManager.GetHubContext<TradeHub>().Clients;
              dynamic chan;
              if (param.group != null && param.group.Length > 0)
              {
                  chan = clients.Group(param.group, param.ids);
              }
              else
              {
                  if(param.ids == null || param.ids.length = 0) {
                      return; //not supposed to happen
                  }
                  chan = clients.Client(param.ids[0]);
              }
              chan.OnEvent(param.channelEvent.ChannelName, param.channelEvent);
          }
      }
      
      [Serializable]
      public class PublishParameter
      {
          public string group { get; set; }
          public string[] ids { get; set; }
          public ChannelEvent channelEvent { get; set; }
      }
      

    确保你的参数是Serializable:这里,PublishParameter 显然是正确的,但是ChannelEvent 也必须是可序列化的,并且只包含Serializable 成员等...

    1. 创建该类的实例,并将其传递给不同AppDomains中的对象(communicationChannelInterAppDomainForSignalR的实例):

      AppDomain domain = AppDomain.CreateDomain(myDomainName);
      
      Type type = typeof(ClassInOtherAppDomain);
      ClassInOtherAppDomain startpoint = (ClassInOtherAppDomain)domain.CreateInstanceAndUnwrap(
              type.Assembly.FullName,
              type.FullName) as ClassInOtherAppDomain;
      
      var session = startpoint.initialize(communicationChannel);
      
    2. communicationChannel 存储在ClassInOtherAppDomain 实例中,并随意使用它;):

      public class ClassInOtherAppDomain {
          private InterAppDomainForSignalR communicationChannel { get; set; }
      
          public void initialize(InterAppDomainForSignalR communicationChannel) {
              this.communicationChannel = communicationChannel;
          }
      
          public void Publish(PublishParameter param) {
              this.communicationChannel.Publish(param);
          }
      }
      

    就是这样=)

    更多关于如何实现跨AppDomain通信的文档可以在herehere找到。

    【讨论】:

      【解决方案2】:

      从外面:

      var context = GlobalHost.ConnectionManager.GetHubContext<YOURHUBCLASS>();
      context.Clients.All.yourHubTask();
      

      【讨论】:

      • 我假设YOURCLASSNAME 指的是集线器类。这是行不通的:集线器已经在与 Owin 应用程序相同的 AppDomain 中启动。我遇到的问题是:如何从运行在不同 AppDomain 中的服务调用 Owin 应用程序的 AppDomain 中存在的内容。
      • GlobalHost 是一个静态变量,因此它仅在 AppDomain 内具有相同的“值”。因此,如果我的 Owin 应用程序在 AppDomain 1 中启动,并且我在 AppDomain 2 中调用 GlobalHost.ConnectionManager.GetHubContext&lt;YOURHUBCLASS&gt;().Clients.All,我将看不到任何客户端:它们都在 AppDomain 1 中注册...... Owin 应用程序,但这不是我的用例。
      • 为什么不能在AppDomain 2中调用AppDomain.CurrentDomain.Load(typeof(YOURHUBCLASS).Assembly.FullName)
      • 我无法访问客户端:它们连接到 AppDomain 1 中存在的类。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-02-16
      • 2018-04-04
      • 1970-01-01
      • 2012-10-01
      • 1970-01-01
      相关资源
      最近更新 更多