因此,如果我正确理解您的问题,那么您希望能够让同一用户在导航到另一个页面时保持与您的信号器中心的连接。如果是这样,那么我想出了一个对我来说非常有效的解决方案。
这里是hub类中的方法:
private static List<UserViewModel> ConnectedUsers = new List<UserViewModel>();
/*
* The 4 methods below handle user connection/disconnection
*/
public void Connect(string UserId)
{
if(ConnectedUsers.Count(x => x.ConnectionId.Equals(Context.ConnectionId)) == 0)
{
if(ConnectedUsers.Count(x => x.UserId.Equals(UserId)) == 0)
{
var ConnectionId = Context.ConnectionId;
ConnectedUsers.Add(new UserViewModel(UserId, ConnectionId));
}
else
{
SetConnection(UserId);
}
}
}
//This will be called when a user disconnects
public void Disconnect()
{
var item = ConnectedUsers.FirstOrDefault(x => x.ConnectionId.Equals(Context.ConnectionId));
if(item != null)
{
ConnectedUsers.Remove(item);
//Update
}
}
//This method will handle when a user is assigned another ConnectionId
public void SetConnection(string UserId)
{
if (ConnectedUsers.Count(x => x.UserId.Equals(UserId)) != 0)
{
UserViewModel Existing = ConnectedUsers.FirstOrDefault(x => x.UserId.Equals(UserId));
ConnectedUsers.Remove(Existing);
Existing.ConnectionId = Context.ConnectionId;
ConnectedUsers.Add(Existing);
}
else
{
Connect(UserId);
}
}
/*
This gets called every time a user navigates to another page,
but if they stay on the site then their connection will be reset
and if they go off site then their connection will be terminated and reset
the next time the log back into the site
*/
public override Task OnDisconnected(bool stopCalled)
{
Disconnect();
return base.OnDisconnected(stopCalled);
}
在您的应用程序中,您需要为您的用户分配一个可以与 ViewModel 配对的唯一且静态的 ID 或 GUID:
public class UserViewModel
{
public string UserId { get; set; }
public string ConnectionId { get; set; }
public UserViewModel() { }
public UserViewModel(string UserId, string ConnectionId)
{
this.UserId = UserId;
this.ConnectionId = ConnectionId;
}
}
连接方法将在用户连接时处理。首先,它将查看已连接用户是否已包含在 ConnectedUsers 列表中,并通过其 ConnectionId 和 UserId 查找用户来执行此操作。如果它们不存在,则将它们添加到列表中,但如果它们确实存在,则转到 SetConnection 方法。
在 SetConnection 方法中,我们得到一个 UserId 并查找以确保列表中存在现有用户。如果我们找到一个现有用户,那么我们将他们从列表中删除,更新他们的连接 ID,然后将他们放回列表中。这有助于确保 Count 拥有正确数量的用户。你会注意到在 SetConnection 方法中有一个对 Connect 的调用,我稍后会解释。
接下来是断开连接方法。它将通过 UserId 在 ConnectedUser 列表中查找用户(这很重要,因为它始终保持不变)并将其删除。当用户注销时或通过我们的 OnDisconnect 方法调用此方法。
OnDisconnect 方法是您可以看到我已覆盖的方法。每当用户导航到您网站上的另一个页面或完全离开该网站时,都会调用此方法(稍后会详细介绍)。
现在我们可以检查我们的 JS:
$(document).ready(function () {
var shub = $.connection.securityHub;
$.connection.hub.start().done(function () {
Connect(shub);
});
//Client calls here
});
//Server calls here
function Connect(shub) {
var id = $('#unique-user-id').attr('data-uid');
if (Cookies.get('Connected') == null) {
Cookies.set('Connected', 'UserConnected', { path: '' });
shub.server.connect(id);;
}
else {
shub.server.setConnection(id);
}
}
function Disconnect() {
Cookies.remove('Connected', { path: '' });
$.connection.securityHub.server.disconnect();
}
确保将脚本标记添加到您的母版页或 _Layout.cshtml 并对此进行引用,因为这需要在您网站的每个页面上运行。在$(document).ready 部分,它将在每次加载页面时启动集线器,这也将在$(document).ready 之外调用我们的连接方法。
Connect 方法将集线器作为参数,它会找到我在页面上设置的唯一 ID($('#unique-user-id') 是我的 _Layout 中的一个元素,因此每个页面都会有它,但您可以将 UserId 传递给任何你想要的方式)。然后它会寻找我称之为 Connected 的 cookie。此 cookie 在会话过期时过期,因此如果他们要关闭选项卡或浏览器,则 Connected cookie 将消失。如果 cookie 为空,那么我设置 cookie 并运行 Hub.cs 中的 Connect 方法。如果 cookie 已经存在,那么我们将运行 SetConnection 方法,以便现有用户可以获取新的 ConnectionId。
我包含了一个 Disconnect 方法,可以随时触发。单击注销按钮后,您可以运行 disconnect 方法,它将删除 cookie 并断开您与服务器的连接。
现在我想谈谈 Connect 和 OnDisconnect 方法。这是一种每次用户导航到您网站上的另一个页面、关闭浏览器或退出您网站的选项卡时都会运行的方法。这意味着每次用户首先导航到您网站上的另一个页面时,他们都会断开连接(但他们的 Connected cookie 将保持不变,因为如果他们转到另一个页面,我们的任何 JS 方法都没有被调用,所以我们没有删除他们的曲奇饼)。页面加载后,我们的 JS 开始发挥作用,$(document).ready 运行然后运行。它将启动集线器,然后运行 JS Connect 方法,该方法将看到 Connect cookie 存在并在服务器上运行 SetConnection 方法,然后找到当前连接的用户并在我们的列表中为他们提供一个新的 ConnectionId。
其中唯一有趣的部分是,如果您显示已连接用户的列表,当他们转到其他页面时,它们会闪烁。我的意思是说您只是在 div 或其他内容中显示用户数 (ConnectedUsers.Count)。假设您有 5 个连接的用户,其中一个用户导航到另一个页面,计数将短暂变为 4(因为 OnDisconnect 方法将运行),然后又回到 5(因为 JS 调用 Connect 方法)。
您可以做不同的事情,而不是像我一样通过 JS 传递 UserId,在 Hub.cs 类中,您可以像这样获取 UserId:Context.User.Identity.GetUserId() 这可能比将其作为页面上的数据属性。如果您想这样做,请确保使用:using Microsoft.AspNet.Identity 包含此内容。这正是我决定这样做的方式。