I've implemented ng-chat https://github.com/rpaschoal/ng-chat (SignalR).
I have 3 users: User1, User2 and User3
If I send a message from User1 to User2 it works well User2 receives the message, but if I create a group (with User1 I open User2's chat and then Add the User3) a new group is created with Users (User2 and User3).
So, when I send a message from this new chat, the users (User2 and User3) doesn't receive any message
Here is my SingalR Hub:
using AdvansysOficina.Api._Core.Infraestructura;
using AdvansysOficina.Api.Generales.Servicios.UsuarioNs;
using Microsoft.AspNetCore.SignalR;
using NgChatSignalR.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace AdvansysOficina.Api.Desarrollo.Servicios.ConversacionPuntoNs.HubNs
{
public class ConversacionHub : Hub
{
private static List<ParticipantResponseViewModel> AllConnectedParticipants { get; set; } = new List<ParticipantResponseViewModel>();
private static List<ParticipantResponseViewModel> DisconnectedParticipants { get; set; } = new List<ParticipantResponseViewModel>();
private readonly object ParticipantsConnectionLock = new object();
private ISesion _sesion;
private IUsuarioServicio _usuarioServicio;
public ConversacionHub(ISesion sesion, IUsuarioServicio usuarioServicio)
{
_sesion = sesion;
_usuarioServicio = usuarioServicio;
}
public static IEnumerable<ParticipantResponseViewModel> ConnectedParticipants(string currentUserId)
{
return AllConnectedParticipants
.Where(x => x.Participant.Id != currentUserId);
}
public void Join(string userName, dynamic grupo)
{
lock (ParticipantsConnectionLock)
{
AllConnectedParticipants.Add(new ParticipantResponseViewModel()
{
Metadata = new ParticipantMetadataViewModel()
{
TotalUnreadMessages = 0
},
Participant = new ChatParticipantViewModel()
{
DisplayName = userName,
Id = Context.ConnectionId,
}
});
// This will be used as the user's unique ID to be used on ng-chat as the connected user.
// You should most likely use another ID on your application
//Clients.Caller.SendAsync("generatedUserId", Context.ConnectionId);
Clients.Caller.SendAsync("generatedUserId", Context.ConnectionId);
Clients.All.SendAsync("friendsListChanged", AllConnectedParticipants);
}
}
public void SendMessage(MessageViewModel message)
{
var sender = AllConnectedParticipants.Find(x => x.Participant.Id == message.FromId);
if (sender != null)
{
Clients.Client(message.ToId).SendAsync("messageReceived", sender.Participant, message);
}
}
public override Task OnDisconnectedAsync(Exception exception)
{
lock (ParticipantsConnectionLock)
{
var connectionIndex = AllConnectedParticipants.FindIndex(x => x.Participant.Id == Context.ConnectionId);
if (connectionIndex >= 0)
{
var participant = AllConnectedParticipants.ElementAt(connectionIndex);
AllConnectedParticipants.Remove(participant);
DisconnectedParticipants.Add(participant);
Clients.All.SendAsync("friendsListChanged", AllConnectedParticipants);
}
return base.OnDisconnectedAsync(exception);
}
}
public override Task OnConnectedAsync()
{
lock (ParticipantsConnectionLock)
{
var connectionIndex = DisconnectedParticipants.FindIndex(x => x.Participant.Id == Context.ConnectionId);
if (connectionIndex >= 0)
{
var participant = DisconnectedParticipants.ElementAt(connectionIndex);
DisconnectedParticipants.Remove(participant);
AllConnectedParticipants.Add(participant);
Clients.All.SendAsync("friendsListChanged", AllConnectedParticipants);
}
return base.OnConnectedAsync();
}
}
}
}
My signalR Adapter (Angular)
import { ChatAdapter, Message, ParticipantResponse, Group, IChatController } from 'ng-chat';
import { map, catchError } from 'rxjs/operators';
import { HttpClient } from '#angular/common/http';
import * as signalR from '#aspnet/signalr';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { AlertasHelper } from '../../../shared/helpers/alertas.helper';
import { PushNotificationHelper } from './notifications/push-notification';
export class SignalRAdapter extends ChatAdapter {
public static serverBaseUrl = 'http://192.168.16.51:5021/'; // if running locally
public userId: string;
private grrupo;
private hubConnection: signalR.HubConnection;
constructor(private username: string, private http: HttpClient, private notification: PushNotificationHelper
) {
super();
this.initializeConnection();
}
private initializeConnection(): void {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(`${SignalRAdapter.serverBaseUrl}chat`, { transport: signalR.HttpTransportType.LongPolling })
.build();
this.hubConnection
.start()
.then(() => {
this.joinRoom();
this.initializeListeners();
})
.catch(err => console.log(`Error while starting SignalR connection: ${err}`));
}
private initializeListeners(): void {
this.hubConnection.on('generatedUserId', (userId) => {
// With the userId set the chat will be rendered
this.userId = userId;
});
this.hubConnection.on('messageReceived', (participant, message) => {
// Handle the received message to ng-chat
console.log(message);
this.notification.notify('Nuevo mensaje de: ' + participant.displayName, message);
this.onMessageReceived(participant, message);
});
this.hubConnection.on('friendsListChanged', (participantsResponse: Array<ParticipantResponse>) => {
// Handle the received response to ng-chat
this.onFriendsListChanged(participantsResponse.filter(x => x.participant.id !== this.userId));
});
}
joinRoom(): void {
if (this.hubConnection && this.hubConnection.state === signalR.HubConnectionState.Connected) {
this.hubConnection.send('join', this.username, '');
}
}
listFriends(): Observable<ParticipantResponse[]> {
// List connected users to show in the friends list
// Sending the userId from the request body as this is just a demo
// return this.http
// .post(`${SignalRAdapter.serverBaseUrl}listFriends`, { currentUserId: this.userId })
// .pipe(
// map((res: any) => res),
// catchError((error: any) => Observable.throw(error.error || 'Server error'))
// );
return of([]);
}
getMessageHistory(destinataryId: any): Observable<Message[]> {
// This could be an API call to your web application that would go to the database
// and retrieve a N amount of history messages between the users.
return of([]);
}
sendMessage(message: Message): void {
if (this.hubConnection && this.hubConnection.state === signalR.HubConnectionState.Connected) {
console.log(message);
this.hubConnection.send('sendMessage', message);
}
}
groupCreated(group: Group): void {
console.log( group);
}
}
Use of component
<ng-chat #chat *ngIf="signalRAdapter && signalRAdapter.userId"
[adapter]="signalRAdapter"
[userId]="signalRAdapter.userId"
[groupAdapter]="signalRAdapter"
(onParticipantChatOpened)="chatOpened($event)"
[historyEnabled]="false">
</ng-chat>
I've downloaded the example of github's creator page, but he doesn't have an example with signalr using groups, I hope you can help me.
ng-chat treats groups as individual participants. You will have to join your room when this event gets invoked:
groupCreated(group: Group): void {
console.log( group);
// Invoke your SignalR hub and send the details of the newly created group
}
ng-chat will generate unique ids every time a group is created so you can track which group is which whenever one gets created from a running ng-chat instance. How you will handle the persistence of these groups is up to your application.
You might want to push a notification to involved users from your SignalR adapter that their friends list has changed (They'll be able to see the group at this stage). You could also decide not to do so and only push a notification if the user who has created the group send an initial message (Once again, up to your application requirements and needs).
You might also want to implement IChatGroupAdapter on your adapter to make the contract more explicit.
Hope this helps!
Related
I'm creating an ASP.NET Core Application running on 4.7 with an Angular front-end. As I need to notify the users about status changes I'm trying to add SignalR. I'm using following versions:
AspNetCore 2.0.1
Angular 5.x
SignalR 1.0.0-alpha2-final
Below you can see my code. If I run this app I can see in the logs that both hub connections get started. If I open a second browser tab with the same URL, only one connection is successful. For the second hub I always get this error:
Error: Not Found
at XMLHttpRequest.xhr.onload [as __zone_symbol__ON_PROPERTYload] (http://localhost:51616/dist/estructuring_main.js?v=fBx34vrzWnqBIPjAm71NaWQz0TEeQZWcx2ixKnjADWI:36168:28)
at XMLHttpRequest.wrapFn (http://localhost:51616/dist/vendor.js?v=rp1X6LsN5SJnsTJrd-749xunuUaKAA_WINwoqzbaeqE:120085:39)
at ZoneDelegate.invokeTask (http://localhost:51616/dist/vendor.js?v=rp1X6LsN5SJnsTJrd-749xunuUaKAA_WINwoqzbaeqE:119340:31)
at Object.onInvokeTask (http://localhost:51616/dist/vendor.js?v=rp1X6LsN5SJnsTJrd-749xunuUaKAA_WINwoqzbaeqE:5013:33)
at ZoneDelegate.invokeTask (http://localhost:51616/dist/vendor.js?v=rp1X6LsN5SJnsTJrd-749xunuUaKAA_WINwoqzbaeqE:119339:36)
at Zone.runTask (http://localhost:51616/dist/vendor.js?v=rp1X6LsN5SJnsTJrd-749xunuUaKAA_WINwoqzbaeqE:119107:47)
at ZoneTask.invokeTask [as invoke] (http://localhost:51616/dist/vendor.js?v=rp1X6LsN5SJnsTJrd-749xunuUaKAA_WINwoqzbaeqE:119415:34)
at invokeTask (http://localhost:51616/dist/vendor.js?v=rp1X6LsN5SJnsTJrd-749xunuUaKAA_WINwoqzbaeqE:120436:14)
at XMLHttpRequest.globalZoneAwareCallback (http://localhost:51616/dist/vendor.js?v=rp1X6LsN5SJnsTJrd-749xunuUaKAA_WINwoqzbaeqE:120462:17)
The error says 404 not found even though I can see the request in my request-middleware.
What do I need to change that the hub connections also work with multiple tabs?
Startup.cs
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddSignalR();
services.AddSingleton(typeof(IUserTracker<>), typeof(InMemoryUserTracker<>));
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime appLifetime)
{
app.UseSignalR(routes =>
{
routes.MapHub<StatusHub>("status", (options) =>
{
options.Transports = TransportType.LongPolling;
});
routes.MapHub<AutopricerHub>("autopricer", (options) =>
{
options.Transports = TransportType.LongPolling;
});
});
}
InMemoryUserTracker.cs
public class InMemoryUserTracker<THub> : IUserTracker<THub>
{
private readonly ConcurrentDictionary<HubConnectionContext, UserDetails> usersOnline
= new ConcurrentDictionary<HubConnectionContext, UserDetails>();
public IEnumerable<UserDetails> UsersOnline()
=> usersOnline.Values.AsEnumerable();
public Task AddUser(HubConnectionContext connection, UserDetails userDetails)
{
usersOnline.TryAdd(connection, userDetails);
return Task.CompletedTask;
}
public Task RemoveUser(HubConnectionContext connection)
{
usersOnline.TryRemove(connection, out var userDetails);
return Task.CompletedTask;
}
}
BaseHubWithPresence.cs
public abstract class BaseHubWithPresence : Hub
{
protected readonly IUserTracker<BaseHubWithPresence> userTracker;
protected BaseHubWithPresence(IUserTracker<BaseHubWithPresence> userTracker)
{
this.userTracker = userTracker;
}
public virtual async Task Join(long userId)
{
await userTracker.AddUser(Context.Connection, new UserDetails(Context.ConnectionId, userId));
await Task.CompletedTask;
}
public virtual async Task Leave()
{
await userTracker.RemoveUser(Context.Connection);
await Task.CompletedTask;
}
}
AutopricerHub.cs
public class AutopricerHub : BaseHubWithPresence
{
public AutopricerHub(IUserTracker<AutopricerHub> userTracker)
: base(userTracker) {}
public async Task SendToAll()
{
await Clients.All.InvokeAsync("All", "data");
}
public async Task SendUpdateAsync(long userId)
{
foreach (var user in userTracker.UsersOnline().Where(o => o.UserId == userId))
{
await Clients.Client(user.ConnectionId).InvokeAsync("Update", "data");
}
}
}
StatusHub.cs
public class StatusHub : BaseHubWithPresence
{
public StatusHub(IUserTracker<BaseHubWithPresence> userTracker)
: base(userTracker)
{
}
public async Task SendSingleAsync(long listener)
{
foreach (var user in userTracker.UsersOnline().Where(o => o.UserId == listener))
{
await Clients.Client(user.ConnectionId).InvokeAsync("Update", "data");
}
}
public async Task SendMultipleAsync(IReadOnlyCollection<long> listeners)
{
foreach (var listener in listeners)
{
foreach (var user in userTracker.UsersOnline().Where(o => o.UserId == listener))
{
await Clients.Client(user.ConnectionId).InvokeAsync("Update", "data");
}
}
}
}
price-notification.service.ts
#Injectable()
export class PriceNotificationService {
private hubConnection: HubConnection;
private destroy$ = new Subject<boolean>();
constructor(
private appContextService: AppContextService,
private messageService: MessageService) {
this.init();
}
private init(): void {
this.hubConnection = new HubConnection('/autopricer', { transport: TransportType.LongPolling });
this.hubConnection.start()
.then(() => {
this.appContextService.getAppContext().takeUntil(this.destroy$).subscribe(appContext => {
this.hubConnection.invoke('join', appContext.FirmuserId);
});
DEBUG.init && debug('Price Notification Hub connection started');
})
.catch(err => {
DEBUG.init && debug('Error while establishing Price Notification Hub connection');
});
this.hubConnection.onclose = e => {
DEBUG.functionCalls && debug('On close Price Notification Hub connection');
this.hubConnection.invoke('leave');
};
this.hubConnection.off('leave', null);
}
private showUpdateNotification(data: string) {
}
private showAllNotification(data: string) {
}
}
status-notification.service.ts
#Injectable()
export class StatusNotificationService {
private hubConnection: HubConnection;
private destroy$ = new Subject<boolean>();
constructor(
private appContextService: AppContextService,
private messageService: MessageService) {
this.init();
}
private init(): void {
this.hubConnection = new HubConnection('/status', { transport: TransportType.LongPolling });
this.hubConnection.start()
.then(() => {
this.appContextService.getAppContext().takeUntil(this.destroy$).subscribe(appContext => {
this.hubConnection.invoke('join', appContext.FirmuserId);
});
DEBUG.init && debug('Status Notification Hub connection started');
})
.catch(err => {
DEBUG.init && debug('Error while establishing Status Notification Hub connection');
});
this.hubConnection.onclose = e => {
DEBUG.functionCalls && debug('On close Status Notification Hub connection');
this.hubConnection.invoke('leave');
};
this.hubConnection.off('leave', null);
}
private showUpdateNotification(data: string) {
}
}
The service provider for each of these Angular service is in a separate component.
Browser network tab:
I want to create Live Chat application using SignalR in ASP.NET MVC. I have created this but the problem is it is sending message to all the users who are connected to that server. I only want to have private chat between two users. So please help me out. Here is my Code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.AspNet.SignalR;
//this is ChatHub.cs file
namespace LiveChat
{
public class ChatHub : Hub
{
public override System.Threading.Tasks.Task OnConnected()
{
Clients.Caller.user(Context.User.Identity.Name);
return base.OnConnected();
}
public void send(string message)
{
Clients.Caller.message("You:" + message);
Clients.Others.message(Context.User.Identity.Name + ": " + message);
}
}}
//This is Startup class
using Microsoft.Owin;
using Owin;
[assembly: OwinStartupAttribute(typeof(LiveChat.Startup))]
namespace LiveChat
{
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
app.MapSignalR();
}
}
}
//This is my js
<script>
var hub = $.connection.chatHub;
hub.client.message = function (msg) {
$("#message").append("<li>" + msg + "</li>")
}
hub.client.user = function (msg) {
$("#user").append("<li>" + msg + "</li>")
}
$.connection.hub.start(function () {
$("#send").click(function () {
if ($("#txt").val() == "") {
return;
}
hub.server.send($("#txt").val());
$("#txt").val(" ");
});
$("#txt").keyup(function (event) {
if ($("#txt").val() == "") {
return;
}
if (event.keyCode == 13) {
hub.server.send($("#txt").val());
}
});
});
</script>
you can use a static list , the list contains all client that connected to the hub and their connection id,
on send message, you have to the hub the text and the destination member. and than send to the specific memeber.
add the member to the list in the function OnConnected, the client id :
Context.clientID
send to specific client in function send
var clientId = ""; // get from the static list by id you got
var scbscriber = Clients.Client(clientId);
scbscriber.message(text);
public class ChatHub : Hub
{
private static List<Users> ConnectedUsers;
public ChatHub()
{
ConnectedUsers = new List<Users>();
}
public override System.Threading.Tasks.Task OnConnected()
{
Clients.Caller.user(Context.User.Identity.Name);
ConnectedUsers.Add(new Users(){
UserName = Context.User.Identity.Name,
ClientId = Context.clientID;
});
return base.OnConnected();
}
public void send(string message, string UserName)
{
//Clients.Caller.message("You:" + message);
var clientId = ConnectedUsers.FirstOrDefulat(x=>x.UserName == UserName).ClientId; // get from the static list by id you got
var scbscriber = Clients.Client(clientId);
scbscriber.message(Context.User.Identity.Name + ": "message);
//Clients.Others.message(Context.User.Identity.Name + ": " + message);
}
}
public class Users
{
public string UserName{get;set;}
public string ClientId {get;set;}
}
Here you go mate:
public void SendPrivateMessage(Messaging objMessaging)
{
var fromNurse = objConnectedUserList.FirstOrDefault(x => x.NurseId == objMessaging.FromNurseId);
var toNurse = objConnectedUserList.FirstOrDefault(x => x.NurseId == objMessaging.ToNurseId);
var chatObject = new { MessageThreadId = 0, Name = fromNurse.NurseName, Message = objMessaging.Message, DTStmp = DateTime.Now, frmNurseId = fromNurse.NurseId };
Result objResult = objMessagingDAL.InsertMessage(objMessaging);
if (toNurse != null)
{
Clients.Client(toNurse.ConnectionId).ReceivePrivateMessage(chatObject);
}
Clients.Caller.ReceivePrivateMessage(chatObject);
}
i'm having problem using events in my servicestack application.
I'm creating an SOA applicatin based on ServiceStack. I've had no problem creating a simple GET/POST manager within the host.
Now i would like to add events
I'm trying using an example, but the event is not received by the client
Does someone have an idea about that?
This is my server:
ServiceStack.Text.JsConfig.EmitCamelCaseNames = true;
ServerEventsFeature serverEventsFeature = new ServerEventsFeature()
{
LimitToAuthenticatedUsers = false,
NotifyChannelOfSubscriptions = true,
OnPublish = (res, msg) =>
{
//fired after ever message is published
res.Write("\n\n\n\n\n\n\n\n\n\n");
res.Flush();
},
OnConnect = (eventSubscription, dictionary) =>
{
},
OnSubscribe = (eventSubscription) =>
{
}
};
Plugins.Add(serverEventsFeature);
container.Register<IServerEvents>(c => new MemoryServerEvents());
container.Register(c => new FrontendMessages(c.Resolve<IServerEvents>()));
container.Register<IWebServiceEventManager>(c => new WebServiceEventManager(DeviceManager, macroManager));
SetConfig(new HostConfig
{
DefaultContentType = MimeTypes.Json,
EnableFeatures = Feature.All.Remove(Feature.Html),
});
public class FrontendMessage
{
public string Level { get; set; }
public string Message { get; set; }
}
public class FrontendMessages
{
private readonly IServerEvents _serverEvents;
private Timer _timer;
public FrontendMessages(IServerEvents serverEvents)
{
if (serverEvents == null) throw new ArgumentNullException(nameof(serverEvents));
_serverEvents = serverEvents;
}
public void Start()
{
var ticks = 0;
_timer = new Timer(_ => {
Info($"Tick {ticks++}");
_timer.Change(500, Timeout.Infinite);
}, null, 500, Timeout.Infinite);
}
public void Info(string message, params object[] parameters)
{
var frontendMessage = new FrontendMessage
{
Level = "success",
Message = message
};
Console.WriteLine("Sending message: " + frontendMessage.Message);
_serverEvents.NotifyChannel("messages", frontendMessage);
}
This is my client:
public async void Connect()
{
try
{
Task.Delay(2000).Wait();
clientEvents = new ServerEventsClient("http://127.0.0.1:20001/", "messages");
clientEvents.OnConnect = (msg) =>
{
};
clientEvents.OnHeartbeat = () =>
{
};
clientEvents.OnCommand = (msg) =>
{
};
clientEvents.OnException = (msg) =>
{
};
clientEvents.OnMessage = (msg) =>
{
};
Dictionary<string, ServerEventCallback> handlers = new Dictionary<string, ServerEventCallback>();
handlers.Add("messages", (client, msg) =>
{
});
clientEvents.RegisterHandlers(handlers);
await clientEvents.Connect();
client = (IServiceClient)(clientEvents.ServiceClient);
}
catch (Exception e)
{
}
}
I'd first recommend looking at ServerEvents Examples and the docs for the C# ServerEventsClient for examples of working configurations.
Your extra ServerEventsFeature configuration isn't useful as you're just specifying the defaults and the Publish() new-line hack is not needed when you disable buffering in ASP.NET. So I would change it to:
Plugins.Add(new ServerEventsFeature());
Second issue is that you're use of Message Event handlers is incorrect, your C# ServerEventsClient is already connected to the messages channel. Your handlers is used to listen for messages sent to the cmd.* selector (e.g. cmd.FrontendMessage).
Since you're publishing a DTO to a channel, i.e:
_serverEvents.NotifyChannel("messages", frontendMessage);
You can use a Global Receiver to handle it, e.g:
public class GlobalReceiver : ServerEventReceiver
{
public void Any(FrontendMessage request)
{
...
}
}
client.RegisterReceiver<GlobalReceiver>();
Thanks mythz!
It works correectly.
Next step is to replicate the same behaviour on javascript client (events and get/post request). Do you have something to suggest me?
Thanks a lot!
Leo
I've been trying to implement a chat room by following the "Asp.Net SignalR Chat Room" tutorial on CodeProject (http://www.codeproject.com/Articles/562023/Asp-Net-SignalR-Chat-Room). However, I'm getting the error "Hubs.ChatHubs.OnDisconnected(): no suitable method found to override"
ChatHub class:
public class ChatHub : Hub
{
#region Data Members
static List<UserDetail> ConnectedUsers = new List<UserDetail>();
static List<MessageDetail> CurrentMessage = new List<MessageDetail>();
#endregion
#region Methods
public void Connect(string userName)
{
var id = Context.ConnectionId;
if (ConnectedUsers.Count(x => x.ConnectionId == id) == 0)
{
ConnectedUsers.Add(new UserDetail { ConnectionId = id, UserName = userName });
// send to caller
Clients.Caller.onConnected(id, userName, ConnectedUsers, CurrentMessage);
// send to all except caller client
Clients.AllExcept(id).onNewUserConnected(id, userName);
}
}
public void SendMessageToAll(string userName, string message)
{
// store last 100 messages in cache
AddMessageinCache(userName, message);
// Broad cast message
Clients.All.messageReceived(userName, message);
}
public void SendPrivateMessage(string toUserId, string message)
{
string fromUserId = Context.ConnectionId;
var toUser = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == toUserId);
var fromUser = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == fromUserId);
if (toUser != null && fromUser != null)
{
// send to
Clients.Client(toUserId).sendPrivateMessage(fromUserId, fromUser.UserName, message);
// send to caller user
Clients.Caller.sendPrivateMessage(toUserId, fromUser.UserName, message);
}
}
public override System.Threading.Tasks.Task OnDisconnected()
{
var item = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
if (item != null)
{
ConnectedUsers.Remove(item);
var id = Context.ConnectionId;
Clients.All.onUserDisconnected(id, item.UserName);
}
return base.OnDisconnected();
}
#endregion
#region private Messages
private void AddMessageinCache(string userName, string message)
{
CurrentMessage.Add(new MessageDetail { UserName = userName, Message = message });
if (CurrentMessage.Count > 100)
CurrentMessage.RemoveAt(0);
}
#endregion
}
Any clues as to why this is happening?
For version 2.1.1+ change
public override Task OnDisconnected()
to
public override Task OnDisconnected(bool stopCalled)
You have a missmatch in your signalR depedency, they changed the signature for OnDisconnected in 2.1.1
So upgrade all projets to 2.1.1 or downgrade all projects to 2.1.0 and it should work
I think perhaps that I do not fully understand the correct way to implement groups in SignalR :)
I am using a SignalR hub coupled with some JS.
The relevant code looks as follows:
public class NotificationHub : Hub
{
public void RegisterUser()
{
if (Context.User.Identity.IsAuthenticated)
{
var username = Context.User.Identity.Name;
Groups.Add(Context.ConnectionId, username);
//check roles
var roles = Roles.GetRolesForUser(username);
foreach (var role in roles)
{
Groups.Add(Context.ConnectionId, role);
}
}
}
public override Task OnConnected()
{
RegisterUser();
return base.OnConnected();
}
//rejoin groups if client disconnects and then reconnects
public override Task OnReconnected()
{
RegisterUser();
return base.OnReconnected();
}
}
Stepping through this code suggests that it works as intended.
When I actually come to send a message however, broadcasting to ALL works. If I try and broadcast to a particular user through their username (their own specific group) nothing happens.
public void BroadcastNotification(List<string> usernames, Notification n)
{
var context = GlobalHost.ConnectionManager.GetHubContext<NotificationHub>();
foreach (var username in usernames)
{
context.Clients.Group(username).broadcastMessage(new NotificationPayload()
{
Title = n.Title,
Count = UnitOfWork.NotificationRepository.GetCount(),
Notification = n.Body,
Html = RenderPartialViewToString("_singleNotification", n)
});
}
}
It would appear that groups do not work as I had thought. Is there a step that I am missing here?
I don't see your client code, but I think you have to explicitly start the hub, and "join" the "group" before you receive the "notifications". So in your client code, something like
$.connection.hub.start()
.done(function() {
chat.server.join();
});
and in your hub, a "Join" method something like what you already have:
public Task Join()
{
if (Context.User.Identity.IsAuthenticated)
{
var username = Context.User.Identity.Name;
return Groups.Add(Context.ConnectionId, username);
}
else
{
// a do nothing task????
return Task.Factory.StartNew(() =>
{
// blah blah
});
}
}