从不同的 AppDomain 调用 SignalR 方法

Call a SignalR method from a different AppDomain

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

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

我发现了很多 examples/questions/tutorials 关于从 Controller/an 其他 class/whatever 调用 SignalR 集线器方法的内容,但在默认 AppDomain(Owin 应用程序所在的那个)之外没有任何内容已初始化)。

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

从外面:

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

我找到的解决方案非常简单:对于任何 AppDomain 间通信,我们需要可以跨越 AppDomain 边界的东西,因此数据或 class.[=22= 的代理]

因此,以下工作:

  1. 创建一个 class 扩展 MarshalByRefObject:当我们将它传递给另一个 class 在不同的 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. 创建这个class的一个实例并传递给不同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 间通信的更多文档 here and here