25

I`m calling SignalR from inside a MVC Controller action

var context = GlobalHost.ConnectionManager.GetHubContext<MyHub>();
var ret = context.Clients.Client("Whatever").getValue(userId);

Is there anyway I can get a response from the getValue() method being called on the Client inside the action?

I've tried a few ways but simply can't get any value for ret. I'm pretty sure this doesn't work with a SignalR Hub but couldn't find documentation about this.

One solution I've considered is after receiving the getValue the Client would call a method in the Hub but then I would have to hack my way into getting the response from the Hub into the controller.

Felipe Leusin
  • 19,991
  • 2
  • 26
  • 19

5 Answers5

32

Calling SignalR Service from JS and having a return value is simple! (Sorry I didn't read the original question well, probably was very late at night). You can do it nicely:

<script type="text/javascript">
    $(function () {

        $.connection.hub.url = "http://localhost:8080/SignalR";

        // Declare a proxy to reference the hub.
        var myHub = $.connection.myHub;

        // Start the connection.
        $.connection.hub.start().done(function () {

            //Once connected, can call methods with return parameters:
            myHub.server
                .getValue("userId")
                .done(function (result) {
                    $('#resultText').val(result); //display in textbox the return value of a call to public string GetValue(string userId) 
                });
        });
    });
</script>

Works in SignalR 2.0

  • I'm looking to get the response from the client, not the server. So far SignalR doesn't support it but there is an issue open on GitHub if i'm not mistaken. I just embraced the async nature of communications and made the client add it's response to a message bus. Worked like a charm – Felipe Leusin Nov 02 '13 at 17:00
  • @FelipeLeusin Yeah I didn't read the question well. You are spot on! –  Nov 05 '13 at 01:05
  • 1
    This anwser is not related to the question, but it is what I was looking for. Thanks – Mihai Popescu Jun 28 '14 at 23:34
  • Exactly what i needed too. Because the call is asynchronous, it does not wait for the return value. Thanks. – Ronen Festinger Oct 26 '14 at 17:42
  • Yeah it really helped me even if it was not related to the asked question. – Muhammad Musavi Jan 24 '18 at 06:30
  • I;m not sure how for you guys .done() is working, for me not (connection.invoke(...).done is not a function), however knowing that invoke() returns a promise, I tried .then() and that worked fine. SignalR 5.0.8 for .Net Core – infografnet Jul 14 '21 at 19:38
24

SignalR is not an RPC mechanism as much as a message passing mechanism. Because of that, there's no real concept of return values.

If you want to do it, you're going to hack it. You could simply do something where, in the server, you call getValue() and then the client's response is to make a call to the server with the return value.

So, for example, in your javascript, you could have something like:

myHub.client.getValue = function(userId) {
  var retVal;
   ... set your retVal here ...
  myHub.returnValue(userId, retVal)
}

And then have the server implement a returnValue() method...

Pete
  • 6,585
  • 5
  • 43
  • 69
  • 1
    Yeah, I tought so. I've managed to hack a way using a multi-threaded approach but there's no built-in way using SignalR. Thanks – Felipe Leusin Dec 13 '12 at 17:46
  • 10
    Notice that the question and this answer relates only to Server->Client requests, we can have return values passed on the opposite direction. – pauloya May 28 '14 at 16:31
  • Besides the very true statement of signalR is not an RPC mechanism, this is not an answer because, this is not the return value semantics, just only "how to trigger the client to send something to the server". To have a remote call with return value semantics we need much more: 1) how to associate the return call with the initiating call (this would be a completion token), b) the hardest: how to wait a specific call to its specific answer, preferably with non blocking way, and async await support, – g.pickardou May 11 '20 at 04:25
9

On the server:

    public bool TestBoolRetVal()
    {
        return true;
    }

On the c# client:

 bool bres = HubProxy.Invoke< bool >("TestBoolRetVal").Result;

Of course you can have any signature defined.

wazz
  • 4,953
  • 5
  • 20
  • 34
roland roos
  • 107
  • 1
  • 2
  • 2
    Assuming `.Result` is `Task.Result`, then this code will block the thread and could deadlock. Use `await` instead. – Dai Nov 28 '20 at 11:28
5

In net6, the rpc is, sample: Server,

public class FnHub : Hub
{
    public double Hypotenuse(double x, double y)
    {
        return Math.Sqrt(x * x + y * y);
    }
}

Client,

var _hubConnection = new HubConnectionBuilder()
    .WithUrl(hubUrl)
    .Build();
await _hubConnection.StartAsync();
var x = 7.0;
var y = 11.0;
var h = await _hubConnection.InvokeAsync<double>("Hypotenuse", x, y);

// h is 13,038404810405298
Sith2021
  • 3,245
  • 30
  • 22
1

To have an RPC like infra we must implement it over the SignalR transport.

This involves to associate the particular "outgoing" server->client message with the other direction answer, the "incoming" message, which holds the return value.

To achieve this we must accomplish three things:

  • Give a unique id for the outgoing, so the incoming could refer to that. There are many name for this in the different design patterns, for example completion token
  • Because the web server will have many concurrent clients, so there could be many concurrent outgoing in the same time, we must store the completion tokens in a dictionary and associate a completion task with each of them, which practically does nothing just waiting a signal
  • When an incoming answer reaches the server, then get the completion token in it, and look for the dictionary, and signal the associated completion task and pass the result to it.

If anyone interested in a implementation see Tomasz Pęczek's repo in github: here

g.pickardou
  • 32,346
  • 36
  • 123
  • 268