从不同的 AppDomain 调用 SignalR 方法

Posted

技术标签:

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

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

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

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

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

【问题讨论】:

【参考方案1】:

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

因此,以下工作:

    创建一个扩展 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 成员等...

    创建该类的实例,并将其传递给不同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);
    

    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和here找到。

【讨论】:

【参考方案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 中存在的类。

以上是关于从不同的 AppDomain 调用 SignalR 方法的主要内容,如果未能解决你的问题,请参考以下文章

jquery ajax响应+不同AppDomain中的函数调用流程

远程处理:从服务器 AppDomain 查找客户端 AppDomain / Assembly

如何从另一个 appDomain 调用类的方法

signalR的一些细节

从 SignalR 调用特定客户端

是否可以从 Postman 调用 SignalR Hub