What are the "method names" in hub connections? - signalr

I've scoured through the .NET documentation and cannot find what these strings representing methods mean. For instance "ReceiveMessage" and "SendMessage" in:
hubConnection = new HubConnectionBuilder();
...
hubConnection.On<string, string>("ReceiveMessage", ..);
and
await hubConnection.SendAsync("SendMessage", userInput, messageInput);
are some examples. I realize in the Hub we have methods that can be these names, but sometimes not? For the toy example I'm using from the .NET documentation, a ChatHub class is defined as follows:
public class ChatHub : Hub
{
public async Task SendMessage(string user, string message)
{
await Clients.All.SendAsync("ReceiveMessage", user, message);
}
}
So here I can see the "SendMesage" method exists. But nowhere is there any "ReceiveMessage" method in the source code. I'm a bit disappointed the documentation doesn't actually explain what these strings representing functions mean in any detail. Do they represent javascript functions? Only locally defined functions in C# (then where is ReceiveMessage?)? Globally defined functions in SignalR? What are they?

They refer to methods on the client.
I guess the exact details vary between languages, but here is one simple example in Dart/Flutter using the signalr_netcore package:
In your server code
// Call the TestMethod method on the client
await Clients.Caller.SendAsync("TestMethod", "some arguments from the server");
In your client code
hub.on('TestMethod', (arguments) async {
print('TestMethod called from server with $arguments');
});
The above code will print TestMethod called from server with [some arguments from the server].

Related

Pushing message to groups in SignalR

Just trying to wrap my mind around handling pushing data to some users through SignalR.
Say, in a private chat situation, Person A sends a message to Person B. If I'm understanding the concept correctly, I need to create a Group for each user who logged into the system. This way, when the new message from Person A comes in, in my SignalR Hub, I'd send the message to the group for Person B which only has only one user in it i.e. Person B.
In this approach, I'd essentially create a unique Group for each user so that I have the flexibility to push data to each unique user. I could use the UserId as the Group Id.
Is this the way to handle pushing data selectively to each unique user?
You can grab client Ids manully like so and then send them using the hubcontext to avoid creating a lot of groups (you just need to implement a menthod which get you're connections from your resource in my case im using dtos)
I'm assuming your hubs have some type of connection manager, here's a sample of one from the docs
In my case I have a dynamic configuration which I use to configure my hubs using the Type of DTO, and I send hubs using a generic hub base here is a sample implementation:
Warning: things may slightly be different I'm using .Net-Core in this example
//NOTE: THub must be the one Registered with MapHubs in the Startup
//class or your connections won't be there because Hubs are not contravarient
public abstract class HubBase<TDTO, THub> : Hub
Where THub: HubBase<TDTO, THub>
{
protected IHubContext<THub> hubContext;
HubBase(IHubContext<THub> hubContext)
{
this._hubContext = hubContext;
}
protected abstract List<string> GetConnectionsByDto(TDTO dto)
protected async Task SendFilteredAsync(string eventName, TDTO dto)
{
var clientIds = await this.GetConnectionsByDto(dto);
if (false == clientIds.Any())
{
return;
}
var broadcastTasks = clientIds.Select(async clientId =>
await this._hubContext.Clients.Client(clientId).SendAsync(eventName, dto));
await Task.WhenAll(broadcastTasks);
}
}

SignalR - How to call server function with parameter from client using hubproxy

I have my signalr running on a separate domain. We will have multiple applications using it to send and receive messages. I created the connection and hub proxy using the following code
connection = $.hubConnection("https://someurl.com", { useDefaultPath: false });
chatHub = connection.createHubProxy('chatHub');
I can get messages from the server sent to the client using the following code which works fine
chatHub.on('receiveEntityMessage', function (chatMessage) {
if (chatMessage) {
console.log(chatMessage.Message);
}
});
Now I dont know how to call server functions with parameters from the client. Can anybody please help me with this?
chatHub.invoke("MethodName", param1, param2, param3).done(function(result) {
console.log(result);
});
Since i'm not sure what is the language of your server side, i am going to provide a C# example.
C# Hub method example:
public class chatHub: Hub {
public void YourHubMehotd(int firstParam, string secondParam){
//The action of your method
}
}
JS client side:
You can call your hub method like this:
{The variable with the hub connection}.server.{the method you want to call}( {the parameters} )
Example:
chatHub.server.YourHubMehotd(1,"Example");
I recomend to create a js function to call the hub method.
function callMyHubMethod(firstParam, secondParam){
chatHub.server.YourHubMehotd(firstParam, secondParam);
}

WCF service operation name

Is there a way to configure App Insights to collect the operation name when monitoring a WCF service? All requests get lumped together by URL (which are just POSTs that end in .svc), so there is no easy way to determine which particular operation was called on the service.
Does there need to be a custom Telemetry Initializer that can somehow determine which operation was actually called and set a custom property? if so, how do you determine the current WCF operation name?
Another option for collecting data on WCF operations is to use the Microsoft.ApplicationInsights.Wcf Nuget package. You can read more about this here.
Brett,
Operation name can be customized in two ways:
1) Using a custom telemetry initializer - that specifically sets operation name.
For more information about telemetry initializers: Custom Telemetry Initializers
2) From sdk version 2-beta3, auto-generated request telemetry is accessible though HttpContext extension method:
System.Web.HttpContextExtension.GetRequestTelemetry
Once the request telemetry is retrieved, operation name associated with it can be changed.
Please let me know if this addressed your question.
Thanks,
Karthik
If you want to get the name of the WCF method called from a client in application insight you can use the following ITelemetryInitializer
With .net 5.0, the httprequest object is stored in the raw object properties of the telemetry context.
public class SoapActionHeaderTelemetryInitializer : ITelemetryInitializer
{
private static readonly Regex _soapActionUri = new Regex("^\"(?<uri>.*)\"$", RegexOptions.Compiled);
public void Initialize(ITelemetry telemetry)
{
DependencyTelemetry httpDependency = telemetry as DependencyTelemetry;
if (httpDependency != null)
{
httpDependency.Context.TryGetRawObject("HttpRequest", out var request);
if (request is HttpRequestMessage httpRequest)
{
if (httpRequest.Headers.TryGetValues("SOAPAction", out var values) && values.Any())
{
// SOAP Action is contained within quote : https://www.w3.org/TR/2000/NOTE-SOAP-20000508/#_Toc478383528
var soapAction = _soapActionUri.Match(values.First()).Groups["uri"].Value;
telemetry.Context.GlobalProperties["SOAPAction"] = soapAction;
}
}
}
}
}

SignalR OnConnected Not Hitting But Still Able to Invoke Client Side Functions From Server

I have the following TestHub that does nothing but invoke a client side sayHello() function.
public class TestHub:Hub
{
public void SayHello()
{
Context.Clients.All.sayHello();
}
public override Task OnConnected()
{
//do some custom stuff
return base.OnConnected();
}
}
On the client, I have the following:
var connection = $.hubConnection('http://localhost:12345/signalr');
var proxy = connection.createHubProxy('TestHub');
proxy.on('sayHello',function(){
console.log('sayHello fired');
})
connection.start().done(function(){
console.debug('Connected');
})
When I call SayHello() on my TestHub the client prints out the following perfectly fine
sayHelloFired
and when the proxy first loads, it prints the following to the console perfectly fine.
Connected
However, if I throw a breakpoint in the OnConnected() method on TestHub, then it does not hit.
All the posts discussing similar problems suggest that their is no handlers being subscribed on the client side, but that is not the case here. How could this be working and OnConnected() is never fired?
Your code didn't compile for me because of the
Context.Clients.All.sayHello();
Line. Context here tries to refer to Hub.Context which is an HubCallerContext class. I think you want to refer to Hub.Clients which is an IHubCallerConnectionContext.
I replaced that line to
Clients.All.sayHello();
And my breakpoint got invoked.
However I am surprised you made it run and got messages on console.
BTW You are right about that, when using javascript clients, if there is no subscription to any event in the hub, the OnConnected methond won't be invoked.
But that is not the case here.

Web API Service - How to use "HttpContext.Current" inside async task

I'm using a "Post" async method of webApi rest service:
public async Task<object> Post([FromBody]string data)
{
object response = ExecuteServerLogics(data);
return response;
}
This above code worked good but in some of the client's calls, we experienced performance issues.
After reading some articles here, i've noticed that our webApi rest service,
is not really working asynchronously with its incoming web requests,
because we forgot to use async/await pattern :
public async Task<object> Post([FromBody]string data)
{
object response = await Task<object>.Run( () =>
{
return ExecuteServerLogics(data);
});
return response;
}
After this fix we noticed the performance got better,
but we found another critic problem:
when accessing HttpContext.Current - it returns Null reference:
public async Task<object> Post([FromBody]string data)
{
object response = await Task<object>.Run( () =>
{
var currentContext = HttpContext.Current; // Returns Null!
return ExecuteServerLogics(data);
});
return response;
}
We tried to found a solution for it, and in most posts we found that we should pass the
worker thread's HttpContext reference into the inner Task that executes the server logics.
The problem with this solution is that the server's logics methods, use many static classes that use
"HttpContext.Current" such as -
Loggers calls.
static security classes that retrieves the user.identity
static security classes that retrives the incoming request's session data, etc.
Therefore, passing the "HttpContext.Current" reference of the worker thread won't solve it.
When we tried the next solution:
public async Task<object> Post([FromBody]string data)
{
// Save worker context:
var currentContext = HttpContext.Current;
object response = await Task<object>.Run( () =>
{
// Set the context of the current task :
HttpContext.Current = currentContext ; // Causes the calls not to work asynchronously for some reason!
// Executes logics for current request:
return ExecuteServerLogics(data);
});
return response;
}
for some reason, we noticed the performance got worse again, like it had returned working synchronously again.
Our problems are:
1. Why in the last example, setting the "HttpContext.Current" inside the await task,
causes the requests to return the same bad performance results which similar to the synchronous results?
2. Is there another way we can use "HttpContext.Current" inside the inner task that call - "ExecuteServerLogics",
and in all the static classes which also call "HttpContext.Current"?
am I doing the entire design wrong somehow?
Thanks!
From the beginning:
public async Task<object> Post([FromBody]string data)
{
object response = ExecuteServerLogics(data);
return response;
}
Don't ignore compiler warnings; the compiler will generate a warning for this method that specifically states it will run synchronously.
Moving on:
in some of the client's calls, we experienced performance issues.
Asynchronous code on the server will not be faster for a single call in isolation. It only helps you scale your server.
In particular, Task.Run will negate all the performance benefits of async and then degrade performance a bit beyond that. I believe the improvement in performance that you measured was coincidental.
in most posts we found that we should pass the worker thread's HttpContext reference into the inner Task that executes the server logics.
Those posts are wrong. IMHO. You end up using the HttpContext object from a background thread, when that object is specifically designed to be only accessed from a request thread.
am I doing the entire design wrong somehow?
I do recommend you take a step back and think about the big picture. When a request comes in, it has a certain amount of work to do. Whether that work is done synchronously or asynchronously is immaterial to the client; both approaches will take about the same amount of time.
If you need to return early to the client, then you'll need a completely different architecture. The usual approach is to queue the work to a reliable queue (e.g., Azure queue), have a separate backend (e.g., Azure WebRole), and proactively notify the client when the work is completed (e.g., SignalR).
That's not to say that async is useless, though. If ExecuteServerLogics is an I/O bound method, then it should be made asynchronous rather than blocking, and then you can use asynchronous methods as such:
public async Task<object> Post([FromBody]string data)
{
object response = await ExecuteServerLogicsAsync(data);
return response;
}
This will enable your server to be more responsive and scalable overall (i.e., not get overwhelmed by many requests).
If your task is inside your ApiController-derived class, you can use:
var ctx = this.Request.Properties["MS_HttpContext"] as System.Web.HttpContextWrapper;
This will give you an HttpContext wrapper with all the usual properties.
Amir I think you're looking for something like this below. I've been dealing with the same issue, trying to optimize a series of calls. It needs to be async all the way through, which means your ExecuteServerLogics() would need to be async, and you'd have to mark the containing lamda as async as well.
I believe following that pattern you can probably eliminate most of your performance issues. Nice passing the context through like that.
public async Task<object> Post([FromBody]string data)
{
// Save worker context:
var currentContext = HttpContext.Current;
object response = await Task<object>.Run(async () =>
{
// Set the context of the current task :
HttpContext.Current = currentContext ;
// Executes logics for current request:
return await ExecuteServerLogics(data);
});
return response;
}

Resources