How to handle multiple events concurrently in C# client - c#

I have a SignalR client application, written in C#, that subscribe an event from the Hub. When the Event is "fired" the application executes a method.
The question is that all events of that kind, to that client, are processed in sequentially.
Use Case / Flow:
In a Browser, an user clicks a button
The button triggers a call to a SignalR Hub
The SignalR Hub send the message to a specific SignalR client
The SignalR Client receives the event/message
The SignalR Client invoke a method that will store the message in a Database
The SignalR Client invoke a method in the Hub to return the result
The Hub deliver to the user the result
If another user clicks the button at the same time, the SignalR Client will only store one message at a time.
Client Example:
myHub.On<ExecuteRequestModel, string>("Execute", (request, to) =>
{
logger.Info("Execute request received for connectionID: " + connection.ConnectionId);
myMethod();
myHub.Invoke("ExecuteResult", response, to);
logger.Info("Execute request processed");
logger.Info("...");
});
There's any recommended practice to handle multiple events in a concurrent way?

That can be solved using Async/Await. The Async function will be responsible to reply to the Hub the result.
myHub.On<ExecuteRequestModel, string>("Execute", async (request, to) =>
await onExecute(request, to)
);

Related

SignalR: How to disconnect clients from server?

We currently have a Web Application, a SignalR Server and a RabbitMQ Server. The Web application receives events from SignalR based on the events received via RabbitMQ. Our Web Application has a fallback which, in case the connection to SignalR closes and can't be established, has a polling mechanism to try to keep the data updated.
The problem: If, instead of the SignalR server, the RabbitMQ is having issues, then, from the perspective of teh WebApplication it is still connected to SignalR server, which prevents it from polling data.
The possible solution: On the event of the RabbitMQ Server has issues connecting to SignalR, we would like to the SignalR server to actively refuse connections on a specified Hub and terminate all the connections it has.
Based on that i tried the following approach (inside the specified Hub), but no success:
public async override Task OnConnectedAsync()
{
var feature = Context.Features.Get<IConnectionHeartbeatFeature>();
feature.OnHeartbeat(state =>
{
try
{
if (rabbitMQWorker.IsConnected == false)
Context.Abort();
}
catch
{
}
}, Context.ConnectionId);
await base.OnConnectedAsync();
}
Even if it worked, I would still need to refuse any connections. Is this something feasable or am I heading towards the wrong direction?

Implement notification from Application Service

I'm trying to implement "Notification System" from my Application Service.
I would like to alert a user using a toast via abp.notify.info() when the required processing (managed by Application Service) has been successfully completed.
Server side:
public async Task<bool> EsitoAllestimento(EsitoAllestimento data)
{
await _notiticationPublisher.PublishAsync(
"message"",
null,
null,
NotificationSeverity.Info
);
}
Client side:
abp.event.on('abp.notifications.received', function (userNotification) {
abp.notify.info('message', 'text');
});
UPDATE
At the moment, I use a different strategy based on the use of email messages from application layer.
In fact, EsitoAllestimento (WebApi method) is invoked by an external application to update some data in the web application (and informs the connected user of the processing result).
Now I'm planning to use SignalR communication, because email communication sometimes fails.
I have defined the hub class like ABP's SignalR Integration example and everything works fine, except communication from SERVER to CLIENT.
How can I use myChatHub at the Application layer or WebApi layer ?
From the documentation on Notification System:
There are two ways of sending notifications to users:
The user subscribes to a specific notification type. Then we publish a notification of this type which is delivered to all subscribed users. This is the pub/sub model.
We can directly send a notification to target user(s).
The Abp.AspNetCore.SignalR package implements IRealTimeNotifier to send real-time notifications to clients automatically, so you don't have to implement and call a custom Hub .
In your case, you should specify the target user.
The first argument is the notification name.
The second argument is where your "message" should go.
The userIds argument is where you should specify the target user.
For example, this shows how to send a message to the default tenant admin and the host admin:
var message = "message";
var defaultTenantAdmin = new UserIdentifier(1, 2);
var hostAdmin = new UserIdentifier(null, 1);
await _notificationPublisher.PublishAsync(
"App.SimpleMessage",
new MessageNotificationData(message),
severity: NotificationSeverity.Info,
userIds: new[] { defaultTenantAdmin, hostAdmin }
);
You don't have to implement the event listener on the client side as abp.notify.info is the default.

Order of messages in SingalR - how to ensure it

I'm using SingalR in an application that sends alot messages in a short period of time.
let's say i have client A and client B.
Client A just sends messages and client B just listening to messages.
Client A sends the following messages in the following order: A->B->C->D
What i'm seeing is that Client B sometimes receives the messages in a different order, for example: B->A->C->D
It is important for maintain the same order i sent the messages.
I've looked online and i found people saying i should use async-await on the function on the hub that handles those messages.
public async Task hubMethod(msgObject msg)
{
await Clients.All.message(msg);
}
I'm not sure how that helps since each time i make a call from client A , singalR should create a new instance of the hub.
The only thing it does is wait for the singalR that it finished doing all it can do on the server in order to send the message to the other client and notifies it to client A.
So my question is this - is there a singalR or asp.net mechanism that make sure i receive the messages in the correct order on the other client or do i need to write my own mechanism (server or client) that reorders the messages if they are out of order - and if so, is there a library that already does it?
You need to write your own mechanism. SignalR in client B has no way to know in which order the clients messages were sent by client A because there is many things that could delay a specific message arrival, like network delay, the only thing SignalR can guarantee is the order in which the messages arrived.
If you really need to know the original order of the messages you could put a count inside the message and let client B sort them out. However i suggest you try another approach, because guaranteeing the order of delivery is not a easy task.

When are SignalR hubs created?

My understanding is that a new server-side Hub instance is created each time a client calls a hub method (via its hub proxy). But what if I wanted a Hub that would only be used (by server business logic) to send messages to clients, using this syntax:-
Clients.All.Notify(data);
I would be using a .Net client by the way, which would receive such messages using this sort of syntax:-
_myHubProxy.On<SomeData>("Notify", data => ...);
If the client never calls any hub methods then would the hub ever be instantiated?
Edit
The article provided by Nikola.Lukovic (see comment below) says:
SignalR creates a new instance of your Hub class each time it needs to handle a Hub operation such as when a client connects, disconnects, or makes a method call to the server.
While I (kind of) understand the need to instantiate a hub each time a method is called, I'm curious as to why it also has to do this when connecting (and disconnecting). I wonder if one of its tasks is to set up the "link" between the Hub and any On<T> event handlers specified on the client's hub proxy?

OnConnected method not called SignalR when I use shared connection in multiple hubs

We can create multiple hubs for different things, and to connect to each hub we can create multiple client side hubs with sharing connection so that, one connection being made to all hubs. Now, the problem arises that the hub onconnected method is not raising on each hub server side code.
public class Hub1 : Hub
{
public override Task OnConnected()
{
return base.OnConnected();
}
}
public class Hub2 : Hub
{
public override Task OnConnected()
{
return base.OnConnected();
}
}
let say, on the client side i create the hub1 and hub2 with client side methods defined on both the hubs, then only one of the hubs onConnected method gets called on server side. If I create the hubs on the client side with separate connections then the OnConnected method gets called. So, there any other work around if I want to use the same connection for each hub but also would like to raise the on Connected event of separate hubs.
I tested it by putting debug point on both hubs, and the OnConnected does get invoked on both hubs, as long as you have any subscriptions to both hubs.
See here: Can I debug OnConnected method in SignalR Hub?
Long story short:
By design, if you don't have any subscriptions to the hub, then the javascript client can't get any messages from server so the OnConnected won't get called.
EDIT
See here the Note part:
Note: For JavaScript clients you have to register at least one event
handler before calling the Start method to establish the connection.
See more at the link.

Categories

Resources