我有登录到 asp.net 网站的组织,当每个成员登录时,我将其 ConnectionId 和 OrganizationId 添加到 SignalR Hub OnConnected 覆盖中名为 OrganizationMembers 的静态 ConcurrentDictionary。我这样做是为了将数据发送到页面回调函数,该函数包含仅与该组织(SignalR 组)相关的数据。OrganizationId 将代表我向其发送数据的 SignalR 组。
同样在 OnConnnected 方法中,我将 OrganizationId 和 OrganizationId.ToString() 添加到另一个表示唯一组织 Id 的 ConcurrentDictionary。稍后我将解释为什么要存储 OrganizationId.ToString()。我存储唯一的组织 ID,以便在调用方法并反复休眠的后台任务中,我可以枚举唯一的组织 ID,并且只发送与其相关的每个组织(SignalR 组)数据。
在 OnDisconnected Hub 覆盖中,删除连接后,我想检查 OrganizationMembers ConcurrentDictionary 中的 OrganizationId 值,以查看该成员是否是最后一个具有该 OrganizationId 的要断开连接的成员,如果是,则将其从 UniqueOrganizations 字典中删除。我知道字典 Values.Contains() 是 O(n) 所以我真的很想避免这种情况。
因此,当任务枚举 UniqueOrganizations 时,不会有我试图不必要地向其发送数据的组织(SignalR 组),例如,来自同一组织的 5 个成员登录但后来都关闭了他们的浏览器,我不会尝试通过 SignalR 回调发送该组数据。
诚然,我不知道 SignalR 集线器的内部工作原理,因此如果我尝试向所有已断开连接的组织成员(SignalR 组)不必要地发送数据,这可能并不重要。
这是对 SignalR Hub 的过度思考吗?我是否应该担心确定最后一个组织(SignalR 组)成员是否已从集线器断开连接并且不从 UniqueOrganizations 中删除 OrganizationId?
如果可以这样做,我该如何避免字典 Values.Contains(),因为它是 O(n)?
// the key is the ConnectionId of an Organization Member
// the value is the OrganizationId of the Member
protected static ConcurrentDictionary<string, int> _organizationMembers;
public static ConcurrentDictionary<string, int> OrganizationMembers {
get {
if(_organizationMembers == null) {
_organizationMembers = new ConcurrentDictionary<string, int>();
}
return _organizationMembers;
}
}
// the key is the OrganizationId to send the specific data to
// the value is the OrganizationId converted to string
protected static ConcurrentDictionary<int, string> _uniqueOrganizations;
public static ConcurrentDictionary<int, string> UniqueOrganizations {
get {
if(_uniqueOrganizations == null) {
_uniqueOrganizations = new ConcurrentDictionary<int, string>();
}
return _uniqueOrganizations;
}
}
// Hub Code
public override Task OnConnected() {
string connectionId = Context.ConnectionId;
string organizationId = Context.Request.QueryString["organizationId"];
int organizationIdValue = int.Parse(organizationId);
OrganizationMembers.TryAdd(connectionId, organizationIdValue);
UniqueOrganizations.TryAdd(organizationIdValue, organizationId);
// the organizationId represents the SignalR group
Groups.Add(connectionId, organizationId);
return base.OnConnected();
}
public override Task OnDisconnected() {
string organizationId = string.Empty;
int organizationIdValue;
string connectionId = Context.ConnectionId;
OrganizationMembers.TryRemove(connectionId, out organizationIdValue);
// if that happens to be the last Member connection to be removed
// then remove the OrganizationId from the unique OrganizationIds
// so it won't be a wasted enumeration and useless callback
// I want to avoid this O(n) Contains()
if(!OrganizationMembers.Values.Contains(organizationIdValue)) {
UniqueOrganizations.TryRemove(organizationIdValue, out organizationId);
}
Groups.Remove(connectionId, organizationId);
return base.OnDisconnected();
}
// Task code
foreach(int organizationIdValue in DataCache.UniqueOrganizations.Keys) {
// this is why I also stored the OrganizationId as a string so I wouldn't have to
// convert it to a string each time the dictionary is enumerated.
// I can just have the organizationId string ready to use as the SignalR Group.
string organizationId = UniqueOrganizations[organizationIdValue];
try {
string organizationData = GetOrganizationData(organizationIdValue);
_clients.Group(organizationId).sendToOrganizationData(organizationData);
}
catch(Exception ex) {
_clients.Group(organizationId).sendToOrganizationError();
}
}