I am using SingalR for my Chat Application. Wanted to play with Redis
and SignalR but I cannot find an working example where i can send msg to
specific connectionId. Below Code that works for a single server instance.
But when i make it a Web Garden with 3 process it stops working as my
server instance that gets the message cannot find the connectionId
for that destination UserId to send the message.
private readonly static ConnectionMapping<string> _connections = new ConnectionMapping<string>();
public void Send(string sendTo, string message, string from)
{
string fromclientid = Context.QueryString["clientid"];
foreach (var connectionId in _connections.GetConnections(sendTo))
{
Clients.Client(connectionId).send(fromclientid, message);
}
Clients.Caller.send(sendTo, "me: " + message);
}
public override Task OnConnected()
{
int clientid = Convert.ToInt32(Context.QueryString["clientid"]);
_connections.Add(clientid.ToString(), Context.ConnectionId);
}
I have used the example below to setup my box and code but none of
them have examples for sending from one client to specific client or
group of specific clients.
http://www.asp.net/signalr/overview/performance-and-scaling/scaleout-with-redis
https://github.com/mickdelaney/SignalR.Redis/tree/master/Redis.Sample
The ConnectionMapping instance in your Hub class will not synced across different SignalR server instances. You need to use permanent external storage such as a database or a Windows Azure table. Refer to this link for more details:
http://www.asp.net/signalr/overview/hubs-api/mapping-users-to-connections
Related
I have a Blazor app written in .NET6 which implements SignalR.
Here is an example of SignalR Hub in server side:
[HubName("ThreadHub")]
public class ThreadHub : Hub
{
public async Task SendMessage(Threading_Pair threading_Pair)
{
await Clients.All.SendAsync("ReceiveMessage", threading_Pair);
}
}
Here is an example of maphub in program.cs in server side:
app.MapHub<ThreadHub>("/threadhub");
Here is an example of SignalR initialization on razor component in client side:
private HubConnection? hubConnection;
private IList<string> messages = new List<string>();
protected override async Task OnInitializedAsync()
{
// Init Broadcast service with SignalR
hubConnection = new HubConnectionBuilder()
.WithUrl(navigationManager.ToAbsoluteUri("/threadhub"))
.Build();
hubConnection.On<string, string> ("ReceiveMessage", (id, message) => {
var encodedMsg = $"{id}: {message}";
messages.Add(encodedMsg);
StateHasChanged();
});
await hubConnection.StartAsync();
}
Here is an example of SignalR function to send message on razor component in client side:
if (hubConnection is not null)
{
if (hubConnection.State == HubConnectionState.Connected)
{
await hubConnection.SendAsync("SendMessage", "Admin", "Hellow to all users! Starting the heavy job!");
}
}
The application now is working fine between client and server and can send and retrieve messages across all open windows.
The question is how the application itself can send messages from server side?
For example this application is generating some threads and i want to know start and stop of their process. In this case the call will happen from the server side, so how i can efficiently sent message from server?
So far the only though is to open a hubConnection like in the razor component in client side"
hubConnection = new HubConnectionBuilder()
.WithUrl(navigationManager.ToAbsoluteUri("/threadhub"))
.Build();
In the above example the navigationManager.ToAbsoluteUri() translates for my my running url application in the client and adds the "/threadhub"
but in this case, I do not have the Navigation manager to get the url of the application, any ideas?
I am using OAuth2.0 to connect to Exchange webservices. Everything else seems to work ok for me . However when i try to subscribe one of the room resource by using grouping info and providing the anchor mailbox as one of the primary mail box it throws an error.
"Request failed because EWS could not contact the appropriate CAS server for this request."
So for example i am trying to subscribe nitroom1 and one the primary mailbox associated with the group is nitroom2 which i am using as X-AnchorMailbox then i got the above error.
public static ExchangeService GetExchangeService(string exchangeURL, string userName, string password, string resourceEmail, string primaryMailbox, string clientID, string tenantID, string clientSecret, string certName)
{
ExchangeService service;
service = new ExchangeService(setTZtoUTC);
service.Url = new Uri(exchangeURL);
if (!string.IsNullOrWhiteSpace(clientID) && !string.IsNullOrWhiteSpace(tenantID))
{
string oAuthToken = multiExchangeManager.getOAuthTokenFromCache(clientID, tenantID, clientSecret, certName);
service.Credentials = new OAuthCredentials(oAuthToken);
}
else
{
service.Credentials = new WebCredentials(userName, password);
}
service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, resourceEmail);
service.HttpHeaders.Add("X-AnchorMailbox", primaryMailbox);
service.HttpHeaders.Add("X-PreferServerAffinity", "true");
return service;
}
However if i connect ews using impersonate account then do same thing it works fine.
Also, if i use resourceMailbox same as primary mailbox then it works ok as well.so in my example it will look like this.
service.ImpersonatedUserId = new ImpersonatedUserId(ConnectingIdType.SmtpAddress, "nitroom1");
service.HttpHeaders.Add("X-AnchorMailbox", "nitroom1");
This is how i am trying to use subscription.
exchangeService.SubscribeToStreamingNotifications(
new FolderId[] { WellKnownFolderName.Calendar, WellKnownFolderName.DeletedItems },
EventType.Created, EventType.Deleted, EventType.Modified, EventType.Moved, EventType.Copied);
Does anyone have any idea why its happening or what i am doing wrong here?
one more thing to add, i tried EWSEditor tool which provides subscription info and both above mentioned resources sharing same grouping info.
I think i found a solution for this issue, i just need to set
X-BackEndOverRideCookie with any service used for subscribing child mailbox.
For more info read this article
https://learn.microsoft.com/en-us/exchange/client-developer/exchange-web-services/how-to-maintain-affinity-between-group-of-subscriptions-and-mailbox-server
I am very new with orleans and trying to grasp everything with grains and so forth.
What i got is that in my startup.cs file i add the SignalR like this
public IServiceProvider ConfigureServices(IServiceCollection services)
{
Program.WriteConsole("Adding singletons");
services
.AddSingleton(achievementManager)
.AddMvc();
services.AddSingleton(SignalRClient);
return services.BuildServiceProvider();
}
So far everything is fine i can start my host/application and it connects to SignalR as it should. But what i cant wrap my head around is how do i get this down to my grain? if i had a controller i would simply send it down in the constructor on startup but how do i do this with a grain? Or can i even do it like this. Any guidance is appreciated.
In the grain then i want to do something like this
[StatelessWorker]
[Reentrant]
public class NotifierGrain : Grain, INotifierGrain
{
private HubConnection SignalRClient { get; }
public NotifierGrain(HubConnection signalRClient)
{
SignalRClient = signalRClient;
SignalRClient.SendAsync(Methods.RegisterService, Constants.ServiceName);
}
public Task NotifyClients(object message, MessageType type)
{
var registerUserNotification = (RegisterUserNotificationModel)message;
SignalRClient.SendAsync(Methods.RegisterUserToMultipleGroups, registerUserNotification.UserId, registerUserNotification.InfoIds);
}
return Task.CompletedTask;
}
Then i try to call the Notify method from another grain like this
var notifier = GrainFactory.GetGrain<INotifierGrain>(Constants.NotifierGrain);
await notifier.NotifyClients(notification, MessageType.RegisterUser);
But trying to do this ends up with an error like this
InvalidOperationException: Unable to resolve service for type 'Microsoft.AspNetCore.SignalR.Client.HubConnection' while attempting to activate 'User.Implementation.Grains.NotifierGrain'.
Orleans supports constructor injection, so you can inject the SignalRClient into your grain constructor. In your code you are already correctly registering the client using services.AddSingleton(SignalRClient), so I will focus on how to inject the type into your grain.
I do not know what the type the SignalR client object is, but in this example I assume that the type is "SignalRClient":
[StatelessWorker]
[Reentrant]
public class NotifierGrain : Grain, INotifierGrain
{
private readonly SignalRClient signalRClient;
public NotifierGrain(SignalRClient signalRClient)
{
this.signalRClient = signalRClient;
}
public async Task NotifyClients(object message, MessageType type)
{
var registerUserNotification = (RegisterUserNotificationModel)message;
await this.signalRClient.SendAsync(
MessageMethods.RegisterUserToMultipleGroups,
registerUserNotification.UserId,
registerUserNotification.infoIds);
}
}
Depends how you're thinking to use SignalR Server, if you're going to host your SignalR server with Microsoft Orleans for sure you need to have backplane to handle the Orleans cluster communications.
You can use SignalR Orleans which has everything done out of the box for you :)
Also if you need a reactive SignalR library for the frontend, you can use Sketch7 SignalR Client
PS I m one of the authors of both libraries.
I am making an xmpp webchat application in Grails. I have a message listener that can log the recieved messages in console. But how can I do this dinamycally in a website ? I am new to webapp development so please dont down vote. I am guessing Ajax but not sure.
My listener:
private MessageListener messageListener = new MessageListener() {
#Override
public void processMessage(Chat chat, Message message) {
// 'from' and 'to' fields contains senders ids, e.g.
// 17792-1028#chat.quickblox.com/mac-167
// 17744-1028#chat.quickblox.com/Smack
String from = message.getFrom().split("#")[0];
String to = message.getTo().split("#")[0];
org.jivesoftware.smack.packet.Message.Type type = message.getType();
String tajp = type.toString();
println String.format(">>> Message received (from=%s, to=%s, type=%s): %s", from, to, tajp, message.getBody())
if (onMessageReceivedListener != null) {
onMessageReceivedListener.onMessageReceived(message);
}
}
}
I suggest you to take a look on the Events Push plugin. With that you can propagate your messages and notify your clients.
You will need:
Service that notify a new message
Controller method to call this service
Ajax request to the controller, sending the user message
JavaScript to handle incoming messages
I am new to webapp development
Web development is pretty different from desktop, so I suggest you to slow down to something more easy. There's other topics on StackOverflow that will introduce you to Grails.
Also, for webdev, it's essential to know about JavaScript, CSS and HTML for your front end.
We are currently implementing private messaging in our asp.net mvc app and we have come to the conclusion that we would like to make it work like github does... meaning we would like the user to receive the message both through our site and to his email inbox(this is somewhat easy to achieve with mvcmailer nuget).. but we would also like the option to have the user reply to the email (through his email client) and have that email be sent to our app (if you have used guthubs private messaging you should know what I mean)..... what is a common approach to implementing this feature?
See my comment regarding how to read emails from a mailbox.
I would use a message bus so that you are not limited to just internal and email notifications, for example you may wish to add SMS notifications in the future.
You may want to check out MassTransit or NServiceBus although for something like this it may be easier to just create your own.
public class Message {
public string Title {get;set;}
public string Body {get;set;}
}
public interface IMessageBus {
void Send(Message message);
}
public interface IMessageHandler {
void Handle(Message message);
}
public class InternalMessageHander : IMessageHandler {
public void Handle(Message message) {
// send internal message
}
}
public class EmailMessageHandler : IMessageHandler {
public void Handle(Message message) {
// send email
}
}
Your IMessageBus implementation would need to locate all the handlers (I would use an IoC container for this).
If you need to process large volumes of messages then I would recommend handing these off to a queue and processing these asynchronously (both MassTransit and NServiceBus support message queues but you could just as easily use a simple database table).
As far as reading emails, I would have background task that connects to the mailbox, downloads emails, parses them and updates your content.
Usually you put either something in the subject line or at the end of the body to track the email. Any UID will do.
Hope that helps.