0

I have an MVC application.

I have implemented singalR to receive live notifications but how to get only user specific notifications.

NotificationSend.cs

public class NotificationSend : Hub
{
    private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext<NotificationSend>();
    public static ConcurrentDictionary<string, MyUserType> MyUsers = new ConcurrentDictionary<string, MyUserType>();

    public override Task OnConnected()
    {
        MyUsers.TryAdd(Context.ConnectionId, new MyUserType() { ConnectionId = Context.ConnectionId });
        return base.OnConnected();
    }

    public override Task OnDisconnected(bool stopCalled)
    {
        MyUserType garbage;

        MyUsers.TryRemove(Context.ConnectionId, out garbage);

        return base.OnDisconnected(stopCalled);
    }

    public static void SendToUser(string messageText)
    {
        hubContext.Clients.Client(MyUsers.Keys.ToList().FirstOrDefault()).Notification(messageText);
    }

    public static void StopLoader(string messageText)
    {
        hubContext.Clients.Client(MyUsers.Keys.ToList().FirstOrDefault()).Stoploader(messageText);
    }
}
public class MyUserType
{
    public string ConnectionId { get; set; }
}

HomeController.cs

public class HomeController : Controller
    {

    public async Task<ActionResult> SaveData()
        {
         foreach (var mydata in DataList)
                {
                   // save data code and show below message on UI
                   NotificationSend.SendToUser(mydata.Name + ": Data saved");

I'm able to get notification on UI perfectly fine but problem is

If user A using his own machine and his login he should get only his notification , I know webapp url is same.

for that i make below change but not any notification is visible after this change.

string UserID = User.Identity.Name;
hubContext.Clients.User(UserID).Notification(mydata.Name + ": Data saved");

Layout.js

$(function () {
            var notification = $.connection.notificationSend;
            console.log(notification);
            notification.client.Notification = function (Count) {
                $('#liveupdate').empty();
                $('#liveupdate').show();
                $('#liveupdate').append(Count);
            };
            $.connection.hub.start().done(function () {
                var connectionId = $.connection.hub.id;
                console.log("Connected Successfully");
            }).fail(function (response) {
                console.log("not connected" + response);
            });
        });
21
  • 1
    for a message to be sent back to the client the Server needs to know socket connection information and by using same browser instance the resultant address is same i think, that's why a message is pushed to all tabs, SignalR is working fine Commented Mar 22, 2019 at 8:46
  • 1
    where are you registring individual users in SignalR ? and what does this line do hubContext.Clients.All.Notification(mydata.Name + ": Data saved"); Commented Mar 22, 2019 at 11:43
  • 1
    have you implemented OnConnected() and OnDisconnected() in your Hub? Commented Mar 22, 2019 at 11:44
  • 1
    for starters make a static class to persisit HubContext instance in memory and add a group to hub based on user's id or email that could identify him/her uniquely then use the context to push the notification to that specific group that would contain only that user Commented Mar 22, 2019 at 11:48
  • 1
    I've added my answer , take a look if this make sense Commented Mar 22, 2019 at 11:58

2 Answers 2

2

Add a static class its instance will be created once and will persist information in memory like the instance of context

public static class NotificationsResourceHandler
{
    private static readonly IHubContext myContext;       
    public static Dictionary<string, string> Groups;



    static NotificationsResourceHandler()
    {
        myContext = GlobalHost.ConnectionManager.GetHubContext<MyHub>();   
        Groups = new Dictionary<string, string>();
    }

    public static void BroadcastNotification(dynamic model, NotificationType notificationType, string userName)
    {
        myContext.Clients.Group(userName).PushNotification(new { Data = model, Type = notificationType.ToString() });
    }
}

and in your Hub

[HubName("yourHub")]
public class MyHub : Hub
{
    public override Task OnConnected()
    {
        var userEmail = Context.QueryString["useremail"]?.ToLower();
        if (userEmail == null) throw new Exception("Unable to Connect to Signalr hub");

        if (NotificationsResourceHandler.Groups.All(x => x.Value != userEmail))
        {
            NotificationsResourceHandler.Groups.Add(Context.ConnectionId, userEmail);
            Groups.Add(Context.ConnectionId, userEmail);
        }
        return base.OnConnected();
    }

    public override Task OnDisconnected(bool stopCalled)
    {
        NotificationsResourceHandler.Groups.Remove(Context.ConnectionId);
        Clients.All.removeConnection(Context.ConnectionId);

        return base.OnDisconnected(stopCalled);
    }
}

Notification will be pushed to individual groups, for your problem you should create a separate group for each user as provided in code.

Sign up to request clarification or add additional context in comments.

2 Comments

thanks for your effort but I can change the complete implementation right now any chance we can made change in my current code ?
well I can try, I had this solution working for me that's why I posted it, I can try to get your code working, in the meantime, you can set up this solution and keep the previous one if this works then transform yours with this.
1

Here is my example code in VB.Net (you may convert it to C#):

Public Class SignalRHub
    Inherits Hub

    Private Shared hubContext As IHubContext = GlobalHost.ConnectionManager.GetHubContext(Of SignalRHub)()

    Public Sub SendToAll(ByVal msg As String)
        hubContext.Clients.All.addNewMessageToPage(msg)
    End Sub

    Public Shared Sub SendToUser(ByVal user As String, ByVal msg As String)
        hubContext.Clients.Group(user).addNewMessageToPage(msg)
    End Sub

    Public Overrides Function OnConnected() As Task
        Dim name As String = Context.User.Identity.Name
        Groups.Add(Context.ConnectionId, name)
        Return MyBase.OnConnected()
    End Function

End Class

You have to use Group. Basically what I do is 1 group is for 1 user. Define by the username.

Then just call the function:

Dim user As User = idb.Users.Where(Function(a) a.id = userid).FirstOrDefault
Dim msg as string = "Any notification message"
SignalRHub.SendToUser(user.UserName, msg)

Lastly, javascript code to trigger that:

var notification = $.connection.signalRHub;
notification.client.addNewMessageToPage = function (msg) {
    $("#notification").prepend(msg);
}

ID notification where you want to put the notification message.

1 Comment

I tried but no notification i'm getting :( can you help me with c# code which will work for specific user login, other user should not want to see others notification.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.