关闭浏览器时OnDisconnectedAsync未触发,行锁无法释放求助
解决SignalR行锁定在浏览器关闭/刷新时无法自动释放的问题
当前实现的核心问题在于:OnDisconnectedAsync方法仅尝试移除连接建立时URL参数中指定的row-id对应的锁,但实际场景中用户可能在单个会话内锁定多行,且连接建立时不一定传递该参数,导致浏览器关闭或刷新时,所有该用户锁定的行无法被自动释放。
解决方案
通过维护连接ID到锁定行ID的反向映射,在连接断开时遍历并释放该连接所有锁定的行,修改后的ItemAccessHub代码如下:
public class ItemAccessHub : Hub { private static readonly ConcurrentDictionary<string, (string ConnectionId, string Module, string Username)> RowLocks = new(); // 新增反向映射:记录每个连接锁定的所有行ID private static readonly ConcurrentDictionary<string, HashSet<string>> ConnectionToRowLocks = new(); public override Task OnConnectedAsync() { return base.OnConnectedAsync(); } public override Task OnDisconnectedAsync(Exception exception) { // 获取当前连接所有锁定的行,并移除反向映射条目 if (ConnectionToRowLocks.TryRemove(Context.ConnectionId, out var userLockedRows)) { foreach (var rowId in userLockedRows) { // 仅当当前连接持有该行锁时,才移除并通知其他客户端 if (RowLocks.TryRemove(rowId, out var lockInfo) && lockInfo.ConnectionId == Context.ConnectionId) { Clients.Others.SendAsync("RowUnlocked", rowId); } } } return base.OnDisconnectedAsync(exception); } public Task LockRow(string rowId, string module, string username) { // 获取当前连接的锁定行集合,不存在则创建新集合 var userRows = ConnectionToRowLocks.GetOrAdd(Context.ConnectionId, _ => new HashSet<string>()); // 锁定HashSet操作(HashSet非线程安全) lock (userRows) { userRows.Add(rowId); } // 更新行锁映射 RowLocks[rowId] = (Context.ConnectionId, module, username); return Clients.Others.SendAsync("RowLocked", rowId, username); } public Task UnlockRow(string rowId) { if (RowLocks.TryRemove(rowId, out var lockInfo) && lockInfo.ConnectionId == Context.ConnectionId) { // 从反向映射中移除该行ID if (ConnectionToRowLocks.TryGetValue(Context.ConnectionId, out var userRows)) { lock (userRows) { userRows.Remove(rowId); // 若集合为空,移除连接的反向映射条目以节省内存 if (userRows.Count == 0) { ConnectionToRowLocks.TryRemove(Context.ConnectionId, out _); } } } return Clients.Others.SendAsync("RowUnlocked", rowId); } return Task.CompletedTask; } }
关键修改说明
- 新增反向映射:
ConnectionToRowLocks记录每个连接ID对应的所有锁定行ID,确保断开连接时能找到所有需要释放的行。 - LockRow方法:锁定行时,同时将行ID添加到当前连接的锁定集合中,同步维护双向映射关系。
- UnlockRow方法:解锁行时,同步从反向映射中移除对应的行ID,避免无效条目残留。
- OnDisconnectedAsync方法:断开连接时,通过反向映射获取该连接所有锁定的行,逐个校验并释放锁,同时通知其他客户端行已解锁。
页面端代码无需修改,保持原有的SignalR连接和事件监听逻辑即可。
内容的提问来源于stack exchange,提问作者BlazorStudio
相关产品推荐
相关产品推荐

