使用SignalR hub向特定连接的用户广播消息

时间:2014-06-10 08:07:54

标签: c# javascript asp.net-mvc signalr

我的目标是在数据库中插入/更新以立即通知连接的用户。

我有Hub

[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);
    }
}

正如您所看到的,我从已连接用户的手动映射中删除了OnDisconnected,以使用户名和ConnectionId稍后用于我的Entity Framework MemberShip用户广播消息,因为每次客户端离开时都会调用OnDisconnected集线器,而不是实际断开连接,例如关闭浏览器。

SignalR的初始化工作非常完美,我测试了它,GetAllPendingNotifications在连接启动时被调用,我调用了SignalR Javascript代码的功能,参见下面的代码。

// A simple templating method for replacing placeholders enclosed in curly braces.
if (!String.prototype.supplant) {
    String.prototype.supplant = function (o) {
        return this.replace(/{([^{}]*)}/g,
            function (a, b) {
                var r = o[b];
                return typeof r === 'string' || typeof r === 'number' ? r : a;
            }
        );
    };
}

if (typeof String.prototype.endsWith !== 'function') {
    String.prototype.endsWith = function(suffix) {
        return this.indexOf(suffix, this.length - suffix.length) !== -1;
    };
}

$(function () {

    // Reference the auto-generated proxy for the hub.
    var notificationsHub = $.connection.notificationsHub;
    var userName, userId;

    // Create a function that the hub can call back to display messages.
    notificationsHub.client.updateNotificationMessages = updateNotificationsToPage;

    function updateNotificationsToPage(notificationMessages) {
        alert('Im in updating notifications');
        if (notificationMessages.count > 0) {
            $.each(function(index) {
                alert($(this).Message);
            });
        }
    };

    function init() {
        notificationsHub.server.getAllPendingNotifications(userId).done(updateNotificationsToPage);
    }

    function userDetailsRetrieved(data) {
        userName = data.UserName;
        userId = data.Id;
        // Start the connection.
        $.connection.hub.start().done(init);
    }

    function getUserDetails() {
        var baseURL = document.baseURI,
            url;
        if (!baseURL.endsWith('Home/GetUserDetails')) {
            if (baseURL.endsWith('/'))
                url = baseURL + 'Home/GetUserDetails';
            else if (baseURL.endsWith('/Home') ||
                baseURL.endsWith('/Home/'))
                url = baseURL + '/GetUserDetails';
            else {
                url = baseURL + '/Home/GetUserDetails';
            }
        } else
            url = baseURL;

        $.ajax({
            url: url, success: userDetailsRetrieved, type: 'POST', error: function () {
            console.log(arguments);
        }, dataType: 'json' });
    }

    getUserDetails();
});

我知道映射可能不是必需的,我可以覆盖实现以将其映射到我的实际ApplicationUser GUID ID以避免它,但是现在它似乎服务于目的,可能会改变它,如果我首先让它发挥作用。

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

private IHubConnectionContext _clients;
private IHubConnectionContext Clients
{
    get {
        return _clients ?? (_clients = GlobalHost.ConnectionManager.GetHubContext<NotificationsHub>().Clients);
    }
}

哪个填充正确。 在应用程序的某些方面,IWorker正在调用Clients属性。

foreach (HubUserHandler hubUserHandler in HubUsersManager.ConnectedUsers)
        {
            ApplicationUser user = await GetApplicationUserWithName(hubUserHandler.Name);
            List<string> userRoles = await GetUserRoles(user);
            bool isInRole = userRoles.Any(p => p.Equals("ARole"));

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

            if (isInRole)
                Clients.Client(hubUserHandler.ConnectionId).updateNotificationMessages(testList);
        }

我的浏览器没有任何内容,应该弹出两个警报,一个说Im in updating notifications,一个TEST MESSAGE,就像您在上面的Javascript代码中看到的那样。

[编辑]

更改了代码以将邮件发送到特定Group,但没有任何内容再次出现在我的客户端浏览器中。

        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);

我想我错过了什么!!!

1 个答案:

答案 0 :(得分:2)

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