Call a SignalR method from a different AppDomain

别等时光非礼了梦想. 提交于 2019-12-24 10:34:33

问题


I have a working web server written in C# using SignalR. It's a self-hosted Owin Application. Everything works fine.

Now I have to relocate my controllers in different AppDomains. This breaks the SignalR part, because GlobalHost remains the same only within one AppDomain, and is not serializable (thus I can't pass it along to other AppDomains as it is).

I've found a lot of examples/questions/tutorials about calling SignalR hubs methods from a Controller/an other class/whatever, but nothing from outside the Default AppDomain (the one where the Owin application is initialized).

How can I send a message to a client from a controller set in a different AppDomain than the Hub ?


回答1:


The solution I found is pretty simple: as for any inter-AppDomain communication, we need something that can cross the boundaries of an AppDomain, thus data or a proxy to a class.

Hence, the following works:

  1. Create a class extending MarshalByRefObject: this will automatically create a proxy to this class when we pass it to a other class in a different 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; }
    }
    

Make sure your parameters are Serializable: here, PublishParameter is obviously correct, BUT ChannelEvent has to be serializable too, and contain only Serializable members, etc...

  1. Create an instance of this class and pass it to the objects in different AppDomains (communicationChannel is an instance of InterAppDomainForSignalR):

    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. Store the communicationChannel in the ClassInOtherAppDomain instance, and use it at will ;) :

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

That's it =)

More documentation on how to implement inter-AppDomain communication can be found here and here.




回答2:


From outside:

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


来源:https://stackoverflow.com/questions/48593022/call-a-signalr-method-from-a-different-appdomain

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!