Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SignalR calling client method from outside hub using GlobalHost.ConnectionManager.GetHubContext doesn't work. But calling from within the hub does

I'm trying to call a client method from within a .net Web API controller action.

Can I do this?

The only post I can find that comes close to what I am looking to do is this one:

SignalR + posting a message to a Hub via an action method

In there a message is sent from within an asp.net MVC controller action using GlobalHost.ConnectionManager.GetHubContext.

When I try that inside my Web API action no errors are thrown, but the method "methodInJavascript" is never invoked on the client side.

    Public ActionResult MyControllerMethod()
    {
        var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
        context.Clients.All.methodInJavascript("hello world");
        // or
        context.Clients.Group("groupname").methodInJavascript("hello world");
    }

When I set a break point inside that action, I see that the code is being reached and executed. Nothing happens on the javascript client side though.

Why? Is Web API so different under the hood that this won't work? Has anyone else tried it and had success?

When I call the "methodInJavascript" from "within" my hub, it works perfectly. Just won't work when called from within a .net Web API controller action.

UPDATE:

After researching this issue I have no solution. I can only assume there is something missing from examples like this Server to client messages not going through with SignalR in ASP.NET MVC 4 and this calling SignalR hub from WebAPI controller issues like maybe there is an additional configuration step to enable calling from a HubContext or something. The code I initially posted here is like that which appears in those examples has not been demonstrated to be flawed in any way. Can anyone see a flaw in the code? Calling from html works. I do it extensively in my apps and never experience an issue. I have never seen a call from the HubContext in an API controller work. No errors. Just no results on the client.

SOLVED (kind of):

Code above does indeed work as is when published. Does not work in Visual Studio dev environment via localhost though. No errors but no result on the client end. Publishing the code as is to a real server on the web does indeed work. I never thought there'd be a difference so I never tried. Figured if it didn't work locally it wouldn't work published. It's working live now but I'm wondering why it doesn't work via localhost in the dev environment. Can't test locally with breakpoints and such.

I have a feeling it's that signalr virtual directory. Something is different when run locally vs published. Not sure what but I see lots of posts like http://www.bitwisejourneys.com/signalr-hosting-in-iis-a-nasty-gotcha/. Reading now to see if there's a way to have it work both locally and published.

like image 210
Robert Avatar asked Dec 13 '13 07:12

Robert


People also ask

Is SignalR asynchronous?

SignalR is an asynchronous signaling library for ASP.NET that our team is working on to help build real-time multi-user web application.

What is hubName in SignalR?

SignalR Hubs are a way to logically group connections as documented. Take an example of a chat application. A group of users could be part of the same hub which allows for sending messages between users in the group. The hubName here can be any string which is used to scope messages being sent between clients.

What is hub proxy SignalR?

The SignalR Hubs API enables you to make remote procedure calls (RPCs) from a server to connected clients and from clients to the server. In server code, you define methods that can be called by clients, and you call methods that run on the client.

What is Hub class SignalR?

A Hub is a class used for the communication. In javascript you can use a hub like this: $(function() { var myConnection = $.connection.myHub; $.connection.hub.start(); }); In ASP.NET you do this: public class Chat : Hub { public void Distribute(string message) { Clients.receive(Caller.name, message); } }


2 Answers

I came across with same issue couple days ago. That took my 2 days to find solution and resolve it. After some serious investigate the problems root cause was the signalr dependency resolver that I set customly.

At the end I found this link and that was saying this:

Replacing the DependencyResolver

You can change the DependencyResolver to use your DI container of choice by setting GlobalHost.DependencyResolver.

NOTE: DO NOT override the global resolver in PreApplicationStart, it will not work, or it'll work only sometimes. Do it in PostApplicationStart (using WebActivator) or in Global.asax.

The important place here the NOTE. Of course after signalr 2.0 this documentation become deprecated. So I mixed some of here with the new SignalR API. In new SignalR API not using WebActivatorEx anymore. OwinStartup preferred instead of WebActivator.

[assembly: OwinStartupAttribute(typeof(YourNamespace.Startup))]
namespace YourNamespace
{
    public partial class Startup
    {
        public void Configuration(IAppBuilder app)
        {
            //IoC container registration process
            UnityConfig.RegisterComponents();

            UnityConfig.Container.RegisterType<AHub, AHub>();

            HubConfiguration config = new HubConfiguration();
            config.EnableJavaScriptProxies = true;


            //You should remove your dependency resolver code from here to Global.asax Application_Start method. Before setting the MVC properties.
            //config.Resolver = new SignalrDefaultDependencyResolver(UnityConfig.Container); // your dependency resolver
            //


            app.MapSignalR(config);
        }
    }
}

And in your global.asax

namespace YourNamespace
{
    public class MvcApplication : System.Web.HttpApplication
    {
        protected void Application_Start()
        {
            //Here your SignalR dependency resolver
            GlobalHost.DependencyResolver = new SignalrDefaultDependencyResolver(UnityConfig.Container);


            //other settings goes on
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);

            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);
        }
    }
}

I dont want to send all the code here, for showing up the real problem.

So for me everything works fine for now. Dependency injection also works too. But the bad part is everywhere that I searched David Fowler was saying "Its by design". I started to think is this design really a necessary or a mistake.

Hope it helps somebody else who makes research for same problem.

like image 161
Yusuf Uzun Avatar answered Oct 23 '22 02:10

Yusuf Uzun


I had the same issue, and it is related to IoC (with whatever such as ninject or castle). If you set the global dependency resolver to your IoC manager, it will also replace the SignalR inner pipeline resolution. This makes your SingleTon client hub, not work correctly.

I solved it by only having the Server Hubs being IoC-ed The code below requires SignalHubActivator (you can find it on the internet)

Now, GlobalHost.ConnectionManager.GetHubContext will return the single instance AND client methods will be called correctly again!

 //configuration.Resolver = signalrDependency ; dont, this will cause GlobalHost.ConnectionManager to be intercepted by Castle


 configuration.Resolver.Register(typeof(IHubActivator),
                                      () => new SignalHubActivator(container));
like image 2
Egbert Nierop Avatar answered Oct 23 '22 02:10

Egbert Nierop