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.
Related
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)
);
This question is not about Scaleout with SignalR and Azure Service Bus. I want to build a Service Bus listener (e.g. OnMessage) into my SignalR web socket app that then distributes the message accordingly to connected users. Messages will be posted from various separately running services into the centralized Service Bus and the UI/browser connected to the web socket servers should receive these.
Option 1: I can add an async Task into a hub method to subscribe to a Service Bus and filter by the connected user. The problem with this is it uses an extra thread from the thread pool and will do this for every socket connection the user has started. Our app can easily start 5-10 or more sockets for every tab open.
Option 2: I can add a single task to the SignalR Startup.Configuration method that then receives all messages and distributes them to the proper connected users. The problem I've encountered here is that I don't have access to the Clients object used for sending to the browser.
I feel like SignalR and Service Bus are a good complement to each other to enable near real-time communications but I can find very little to implement a scenario like this. And I feel like this should be a common enough scenario. Perhaps I'm missing some obvious design patterns that would be a better solution.
I was able to figure this out. In the SignalR Startup.Configuration method I added a method to start the listener and in that method I called GlobalHost.ConnectionManager.GetHubContext. Currently this doesn't send to individual users but I'll add a connection manager of some sort to handle that.
public void startServiceBusListener()
{
// setup subcsription
var namespaceManager = NamespaceManager.CreateFromConnectionString(connectionString);
if (!namespaceManager.SubscriptionExists("myTopic", Environment.MachineName))
namespaceManager.CreateSubscription("myTopic", Environment.MachineName);
SubscriptionClient busClient = SubscriptionClient.CreateFromConnectionString(connectionString, "myTopic", Environment.MachineName);
// Configure the callback options.
OnMessageOptions options = new OnMessageOptions();
options.AutoComplete = false;
options.AutoRenewTimeout = TimeSpan.FromMinutes(1);
receiveTask = Task.Run(() =>
{
// handle new messages
busClient.OnMessage((message) =>
{
try
{
Notification note = message.GetBody<Notification>();
string notification = JsonConvert.SerializeObject(note);
GlobalHost.ConnectionManager.GetHubContext<DispatchHub>().Clients.All.notify(notification);
// Remove message from subscription.
message.Complete();
}
catch (Exception)
{
// Indicates a problem, unlock message in subscription.
message.Abandon();
}
}, options);
}, cts.Token);
}
I am building a c#/wpf project.
It's architecture is this:
A console application which will be on a virtual machine (or my home computer) that will be the server side.
A wpf application that will be the client app.
Now my problem is this - I want the server to be able to send changes to the clients. If for example I have a change for client ABC, I want the server to know how to call a service on the clients computer.
The problem is, that I don't know how the server will call the clients.
A small example in case I didn't explain it well:
The server is on computer 1, and there are two clients, on computers 2 and 3.
Client 2 has a Toyota car and client 3 has a BMW car.
The server on computer 1 wants to tell client 2 that it has a new car, an Avenger.
How do I keep track and call services on the clients?
I thought of saving their ip address (from calling ipconfig from the cmd) in the DB - but isn't that based on the WI-FI/network they are connected to?
Thanks for any help!
You could try implementing SignalR. It is a great library that uses web sockets to push data to clients.
Edit:
SignalR can help you solve your problem by allowing you to set up Hubs on your console app (server) that WPF application (clients) can connect to. When the clients start up you will register them with a specified Hub. When something changes on the server, you can push from the server Hub to the client. The client will receive the information from the server and allow you to handle it as you see fit.
Rough mockup of some code:
namepsace Server{}
public class YourHub : Hub {
public void SomeHubMethod(string userName) {
//clientMethodToCall is a method in the WPF application that
//will be called. Client needs to be registered to hub first.
Clients.User(userName).clientMethodToCall("This is a test.");
//One issue you may face is mapping client connections.
//There are a couple different ways/methodologies to do this.
//Just figure what will work best for you.
}
}
}
namespace Client{
public class HubService{
public IHubProxy CreateHubProxy(){
var hubConnection = new HubConnection("http://serverAddress:serverPort/");
IHubProxy yourHubProxy = hubConnection.CreateHubProxy("YourHub");
return yourHubProxy;
}
}
}
Then in your WPF window:
var hubService = new HubService();
var yourHubProxy = hubService.CreateHubProxy();
yourHubProxy.Start().Wait();
yourHubProxy.On("clientMethodToCall", () => DoSometingWithServerData());
You need to create some kind of subscription model for the clients to the server to handle a Publish-Subscribe channel (see http://www.enterpriseintegrationpatterns.com/patterns/messaging/PublishSubscribeChannel.html). The basic architecture is this:
Client sends a request to the messaging channel to register itself as a subscriber to a certain kind of message/event/etc.
Server sends messages to the channel to be delivered to subscribers to that message.
There are many ways to handle this. You could use some of the Azure services (like Event hub, or Topic) if you don't want to reinvent the wheel here. You could also have your server application track all of these things (updates to IP addresses, updates to subscription interest, making sure that messages don't get sent more than once; taking care of message durability [making sure messages get delivered even if the client is offline when the message gets created]).
In general, whatever solution you choose is plagued with a common problem - clients hide behind firewalls and have dynamic IP addresses. This makes it difficult (I've heard of technologies claiming to overcome this but haven't seen any in action) for a server to push to a client.
In reality, the client talks and the server listens and response. However, you can use this approach to simulate a push by;
1. polling (the client periodically asks for information)
2. long polling (the client asks for information and the server holds onto the request until information arrives or a timeout occurs)
3. sockets (the client requests server connection that is used for bi-directional communication for a period of time).
Knowing those terms, your next choice is to write your own or use a third-party service (azure, amazon, other) to deliver messages for you. I personally like long polling because it is easy to implement. In my application, I have the following setup.
A web API server on Azure with and endpoint that listens for message requests
A simple loop inside the server code that checks the database for new messages every 100ms.
A client that calls the API, handling the response.
As mentioned, there are many ways to do this. In your particular case, one way would be as follows.
Client A calls server API to listen for message
Server holds onto call, waiting for new message entry in database
Client B calls server API to post new message
Server saves message to database
Server instance from step 2 sees new message
Server returns message to Client A.
Also, the message doesn't have to be stored in a database - it just depends on your needs.
Sounds like you want to track users à la https://www.simple-talk.com/dotnet/asp.net/tracking-online-users-with-signalr/ , but in a desktop app in the sense of http://www.codeproject.com/Articles/804770/Implementing-SignalR-in-Desktop-Applications or damienbod.wordpress.com/2013/11/20/signalr-a-complete-wpf-client-using-mvvm/ .
I am building a simple duplex wcf service. In this service clients send messages to the server and the server distributes the message to all connected clients. However, despite the fact that I defined the ServiceBehavior attribute as
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)], only the client who sent the message receives it back from the server, while the other clients do not. I verified that there is just one instance of the server running.
What did I do wrong? I looked at other similar questions on the web, and they all say that I should define InstanceContextMode = InstanceContextMode.Single, which I already did.
Do you have a callback Contract. So that server will reply back to client.
Check the below tutorial for Implementing Callback Contract
Click here
Also check the below Project Event Notification server. This project is doing similar things what you want.
CodeProject Link
Feel free to ask me if you need any more clarification
You need to maintain the clistList as shown in the code snippet.
List<IMessageServiceCallback> clientList = new List<IMessageServiceCallback>();
public void Register()
{
IMessageServiceCallback callback = OperationContext.Current.GetCallbackChannel<IMessageServiceCallback>();
clientList.add(callback);
}
When you want to broadcast this message. You can iterate through the list and call the callback function to send message to clients.
Hi
Though it's a simple thing, I don't know how to do it.
I'm trying to sign the dll to events happening on the server side.
the clients has an instance of this dll and they have to receive notifications from the server.
on the service I defined (c#):
public delegate ActionResult ActionComplitedHandler(object sender,ActionComplitedEventArgs e);
.
public event ActionComplitedHandler ActionComplited;
public void OnActionComplited(ActionResult actionResult)
{
if (ActionComplited != null)
{
ActionComplitedEventArgs args = new ActionComplitedEventArgs();
args.ActionResult = actionResult;
ActionComplited(this, args);
}
}
But when trying to sign the dll to the event I cant's see it.
mySeriveInstance.ActionComplited+=... //(I don't get the eventHandler after the dot)
I prefer not to use WCF callback.
The WCF service instance is Single.
What do I do wrong and is there other method to do that?
Thanks.
There are two basic ways you can do this:
1.) You can self-host a WCF service in your client application, and in the call to your server pass in the ip address of the client machine. The server can then construct a proxy and call back to the client when it wants to send it an event
2.) You can poll for events from the server every so often. Give every client an identifier in their service response, and then they can call the server with that identifier every so often. You can use caching on the server side to store a list of undelivered events, and then when a client hits that link you return the oldest event for the client.