C# 使用信号集线器向特定连接用户广播消息

C# 使用信号集线器向特定连接用户广播消息,c#,javascript,asp.net-mvc,signalr,C#,Javascript,Asp.net Mvc,Signalr,我的目标是在数据库发生插入/更新时立即通知连接的用户 我有一个集线器 [Authorize] public class NotificationsHub : Hub { private readonly IWorker _worker; public NotificationsHub() : this(new WorkerFacade(new ExtremeDataRepository())) { } public NotificationsHub(IWorker w

我的目标是在数据库发生插入/更新时立即通知连接的用户

我有一个
集线器

[Authorize]
public class NotificationsHub : Hub
{
    private readonly IWorker _worker;

    public NotificationsHub() : this(new WorkerFacade(new ExtremeDataRepository())) { }

    public NotificationsHub(IWorker worker)
    {
        _worker = worker;
    }

    public override Task OnConnected()
    {
        if (!HubUsersManager.ConnectedUsers.Any(p => p.Name.Equals(Context.User.Identity.Name)))
        {
            HubUsersManager.ConnectedUsers.Add(new HubUserHandler
            {
                Name = Context.User.Identity.Name,
                ConnectionId = Context.ConnectionId
            });
        }
        return base.OnConnected();
    }

    //public override Task OnDisconnected()
    //{
    //    HubUserHandler hubUserHandler =
    //        HubUsersManager.ConnectedUsers.FirstOrDefault(p => p.Name == Context.User.Identity.Name);

    //    if (hubUserHandler != null)
    //        HubUsersManager.ConnectedUsers.Remove(hubUserHandler);

    //    return base.OnDisconnected();
    //}

    public async Task<ICollection<NotificationMessage>> GetAllPendingNotifications(string userId)
    {
        return await _worker.GetAllPendingNotifications(userId);
    }

    public void UpdateNotificationMessagesToAllClients(ICollection<NotificationMessage> notificationMessages)
    {
        Clients.All.updateNotificationMessages(notificationMessages);
    }
}
我知道映射可能不是必需的,我可以重写实现,将其映射到我的实际
ApplicationUser
GUID Id以避免它,但现在它似乎可以达到这个目的,如果我首先让它工作,它可能会改变它

您还可以看到
IWorker
引用,如果客户端调用服务器方法,我将使用该引用获取一些数据。 此外,
IWorker
具体实现正在被所有
控制器使用,并且具有属性
客户端
,因此我可以立即将消息广播回连接的客户端

private IHubConnectionContext _clients;
private IHubConnectionContext Clients
{
    get {
        return _clients ?? (_clients = GlobalHost.ConnectionManager.GetHubContext<NotificationsHub>().Clients);
    }
}
我的浏览器中没有任何内容,应该会弹出两个警报,一个在更新通知中说
Im
,另一个是
testmessage
,就像您在上面的
Javascript
代码中看到的那样

[编辑]

更改代码以将消息发送到特定的
,但在我的客户端浏览器中不会再次显示任何内容

        public override Task OnConnected()
        {
            if (_groupManager.Count == 0)
            {
                ICollection<string> rolesCollection = _worker.GetAllRoles();
                foreach (string roleName in rolesCollection)
                {
                    _groupManager.Add(roleName);
                }
            }

            foreach (string groupRole in _groupManager.Groups)
            {
                if (Context.User.IsInRole(groupRole))
                {
                    Groups.Add(Context.ConnectionId, groupRole);
                }
            }

            return base.OnConnected();
        }
public override任务OnConnected()
{
如果(_groupManager.Count==0)
{
ICollection rolesCollection=_worker.GetAllRoles();
foreach(角色集合中的字符串roleName)
{
_groupManager.Add(roleName);
}
}
foreach(字符串groupRole在_groupManager.Groups中)
{
if(Context.User.IsInRole(groupRole))
{
添加(Context.ConnectionId,groupRole);
}
}
返回base.OnConnected();
}
在我工人阶级的某个地方:

    List<NotificationMessage> testList = new List<NotificationMessage>
        {
            new NotificationMessage {Message = "TEST MESSAGE!"}
        };

    Clients.Group("TestGroup").updateNotificationMessages(testList);
List testList=新列表
{
新建通知消息{Message=“TEST Message!”}
};
Clients.Group(“TestGroup”).updateNotificationMessages(testList);

我想我错过了什么

我的2美分:你看过
Clients.User()方法了吗?它已经为您完成了所有的
ConnectionId
映射,并且您正在使用
Context.User
,因此它应该是现成的。如果需要更复杂的东西,可以在
IUserIdProvider
的自定义实现中编写自己的映射逻辑。至少你会消除一个层次的复杂性(但如果不是因为这个问题,你不确定它会解决你的具体问题)。只是一个想法。

是的,我将把逻辑移到IUserIdProvider,因为我使用ASP.NET身份验证,所以它看起来更好。我将尝试Clients.User()empty重载。Clients.User()对我的问题没有意义,因为我可能连接了多个用户,他们需要接收相同的消息。我不认为这与使用ConnectionId的方法有什么不同。与通过“不安全”的连接ID进行循环不同,您将通过用户ID进行循环,这是一个不太需要解决的问题。此外,我建议您看看是否可以找到一种方法,以某种方式对用户进行分组(例如使用角色),并利用SignalR提供的组支持,这样您就可以通过一次呼叫同时访问所有用户。也许你可以在这个方向上回顾你的逻辑。是的,我同意你的评论,我将删除当前的逻辑,并尝试从中心使用Groups和ConnectionId构建新的逻辑。有没有办法让所有当前连接到集线器客户端的ConnectionID?我知道IUserIdProvider将允许我将ConnectionId映射到数据库中的User.Identity id。当然,对于群组,我只需要说发送消息给群组,所有连接到群组的客户端理论上都会收到通知!虽然没有办法获得所有当前的连接ID,但即使这样,也有可能不是推荐的方法。处理ConnectionId越少越好,而且通常可以在不使用它们的情况下重新构造代码来解决问题。团队在这方面真的很棒。
        public override Task OnConnected()
        {
            if (_groupManager.Count == 0)
            {
                ICollection<string> rolesCollection = _worker.GetAllRoles();
                foreach (string roleName in rolesCollection)
                {
                    _groupManager.Add(roleName);
                }
            }

            foreach (string groupRole in _groupManager.Groups)
            {
                if (Context.User.IsInRole(groupRole))
                {
                    Groups.Add(Context.ConnectionId, groupRole);
                }
            }

            return base.OnConnected();
        }
    List<NotificationMessage> testList = new List<NotificationMessage>
        {
            new NotificationMessage {Message = "TEST MESSAGE!"}
        };

    Clients.Group("TestGroup").updateNotificationMessages(testList);