Blazor同步模式下获取会话ID的实现方案求助
技术背景
使用.NET 7.0 + Blazor构建Web应用,数据源连接由IMoxClientConnection实例管理,每个用户需对应独立实例(无需每次打开页面重新登录)。已实现ConnectionHandler(基于MoxSessionId与IMoxClientConnection实例的字典)并注册为Singleton服务,所有业务服务通过其GetClientConnectorForSession(string moxSessionId)方法获取对应连接实例。
核心需求
为每个客户端浏览器标签页生成唯一会话ID,标签页打开使用期间保持持久,暂不考虑浏览器/标签页关闭后的留存。
已尝试方案
最初计划通过Cookie存储自定义会话ID,因Blazor中实现复杂度转而尝试ProtectedSessionStorage:
- 在
Program.cs中注册服务:builder.Services.AddScoped<ProtectedSessionStorage>(); - 在基础服务类中编写同步方法获取/生成会话ID:
private string CreateOrGetSessionId() { var sessionId = _sessionStorage.GetAsync<string>("MoxSessionId").Result.Value; if (string.IsNullOrWhiteSpace(sessionId)) { sessionId = Guid.NewGuid().ToString(); _sessionStorage.SetAsync("MoxSessionId", sessionId); } return sessionId; }
遇到的问题
执行GetAsync后程序无响应,尝试查找同步方法(如GetItem)也无法正常工作。
可行实现方案
问题根源
Blazor中ProtectedSessionStorage的方法均为异步操作,同步方法中使用.Result会阻塞主线程,而JS互操作(SessionStorage依赖JS)需要主线程资源,最终导致死锁。必须采用异步方式处理。
方案1:基于ProtectedSessionStorage的异步会话管理
1. 创建Scoped会话ID服务
public class SessionIdService { private readonly ProtectedSessionStorage _sessionStorage; private string _cachedSessionId; public SessionIdService(ProtectedSessionStorage sessionStorage) { _sessionStorage = sessionStorage; } public async Task<string> GetSessionIdAsync() { // 优先使用缓存的会话ID if (!string.IsNullOrWhiteSpace(_cachedSessionId)) return _cachedSessionId; // 从SessionStorage读取 var storageResult = await _sessionStorage.GetAsync<string>("MoxSessionId"); if (storageResult.Success && !string.IsNullOrWhiteSpace(storageResult.Value)) { _cachedSessionId = storageResult.Value; return _cachedSessionId; } // 生成新会话ID并存储 _cachedSessionId = Guid.NewGuid().ToString(); await _sessionStorage.SetAsync("MoxSessionId", _cachedSessionId); return _cachedSessionId; } }
2. 注册服务
在Program.cs中添加:
builder.Services.AddScoped<SessionIdService>();
3. 在业务服务中使用
public class YourBusinessService { private readonly SessionIdService _sessionIdService; private readonly ConnectionHandler _connectionHandler; public YourBusinessService(SessionIdService sessionIdService, ConnectionHandler connectionHandler) { _sessionIdService = sessionIdService; _connectionHandler = connectionHandler; } public async Task ExecuteDataOperationAsync() { var sessionId = await _sessionIdService.GetSessionIdAsync(); var clientConnection = _connectionHandler.GetClientConnectorForSession(sessionId); // 使用clientConnection执行数据源操作 } }
方案2:无需加密,使用普通SessionStorage
若无需会话ID加密保护,可改用ISessionStorageService(Blazor Server需确保启用JS互操作,Blazor WebAssembly需引入Microsoft.AspNetCore.Components.WebAssembly.Storage包):
1. 注册服务
builder.Services.AddScoped<ISessionStorageService, SessionStorageService>();
2. 修改SessionIdService
public class SessionIdService { private readonly ISessionStorageService _sessionStorage; private string _cachedSessionId; public SessionIdService(ISessionStorageService sessionStorage) { _sessionStorage = sessionStorage; } public async Task<string> GetSessionIdAsync() { if (!string.IsNullOrWhiteSpace(_cachedSessionId)) return _cachedSessionId; _cachedSessionId = await _sessionStorage.GetItemAsync<string>("MoxSessionId"); if (string.IsNullOrWhiteSpace(_cachedSessionId)) { _cachedSessionId = Guid.NewGuid().ToString(); await _sessionStorage.SetItemAsync("MoxSessionId", _cachedSessionId); } return _cachedSessionId; } }
后续使用方式与方案1一致。
内容的提问来源于stack exchange,提问作者Markus H.

