SignalR 并发列表最佳实践
SignalR concurrent list best practice
我在 C# MVC Web 应用程序中使用 SignalR。我想要一个当前表单认证用户的对象列表( UserDetail )。此对象应包含 UserId(来自 table 的 Pk)和 ConnectionId(SignalR)。
今天我在中心使用静态列表 class :
public static List<UserDetail> ConcurrentUsers = new List<UserDetail>();
并在每个 Hub 事件中处理从列表中删除和插入到列表中的操作
public override Task OnDisconnected(bool stopCalled)
{
if (ConcurrentUsers != null && ConcurrentUsers.Any(x => x.ConnectionId.Equals(Context.ConnectionId)))
ConcurrentUsers.Remove(ConcurrentUsers.Find(x => x.ConnectionId.Equals(Context.ConnectionId)));
return base.OnDisconnected(stopCalled);
}
public override Task OnConnected()
{
IsUserAuthenticated();
return base.OnConnected();
}
public override Task OnReconnected()
{
IsUserAuthenticated();
return base.OnReconnected();
}
private void IsUserAuthenticated()
{
if (HttpContext.Current != null && HttpContext.Current.User != null && HttpContext.Current.User.Identity.IsAuthenticated)
{
var user = DalHelper.GetUserNoTracking(HttpContext.Current.User.Identity.Name);
if (user == null)
{
Clients.Caller.GoToLogin();
return null;
}
if (!ConcurrentUsers.Any(x => x.ConnectionId.Equals(Context.ConnectionId)))
{
UserDetail ud = new UserDetail {
ConnectionId = Context.ConnectionId,
UserId = user.UserId,
};
ConcurrentUsers.Add(ud);
}
}
return null;
}
当我想用他的数据更新用户时,假设他收到一条新消息,然后我可以遍历列表并找到该用户的所有 connectionId,并将消息推送到所有连接的用户客户端。
private static void FlushMessageToClient(Guid UserId, string Message)
{
string[] cids = Hub.ConcurrentUsers.Where(x => x.UserId.Equals(UserId)).Select(x => x.ConnectionId).ToArray();
foreach (string cid in cids)
{
Hub.Clients.Client(cid).messageReceived(Message);
}
}
一切正常,直到网站运行一个小时,用户进进出出,它将开始收到错误:
System.NullReferenceException: Object reference not set to an instance
of an object. at Infra.ChatHub.b__16(UserDetail
x) at System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func
2
predicate) at ChatHub.IsUserAuthenticated()
有没有更好的编程方式?
如果你有一个键索引,我会使用字典:
Dictionary<Guid, UserDetail > dictionary = new Dictionary<Guid, UserDetail >();
你可以这样做:
dictionary.Add(userId, userDetail);
var detail = dictionary[userId];
关于你的错误,请在查询之前检查用户ID是否在列表中。
if (dictionary.ContainsKey(userId)) {}
可以在下面找到最佳选项
将 SignalR 用户映射到连接。
http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections
我在 C# MVC Web 应用程序中使用 SignalR。我想要一个当前表单认证用户的对象列表( UserDetail )。此对象应包含 UserId(来自 table 的 Pk)和 ConnectionId(SignalR)。
今天我在中心使用静态列表 class :
public static List<UserDetail> ConcurrentUsers = new List<UserDetail>();
并在每个 Hub 事件中处理从列表中删除和插入到列表中的操作
public override Task OnDisconnected(bool stopCalled)
{
if (ConcurrentUsers != null && ConcurrentUsers.Any(x => x.ConnectionId.Equals(Context.ConnectionId)))
ConcurrentUsers.Remove(ConcurrentUsers.Find(x => x.ConnectionId.Equals(Context.ConnectionId)));
return base.OnDisconnected(stopCalled);
}
public override Task OnConnected()
{
IsUserAuthenticated();
return base.OnConnected();
}
public override Task OnReconnected()
{
IsUserAuthenticated();
return base.OnReconnected();
}
private void IsUserAuthenticated()
{
if (HttpContext.Current != null && HttpContext.Current.User != null && HttpContext.Current.User.Identity.IsAuthenticated)
{
var user = DalHelper.GetUserNoTracking(HttpContext.Current.User.Identity.Name);
if (user == null)
{
Clients.Caller.GoToLogin();
return null;
}
if (!ConcurrentUsers.Any(x => x.ConnectionId.Equals(Context.ConnectionId)))
{
UserDetail ud = new UserDetail {
ConnectionId = Context.ConnectionId,
UserId = user.UserId,
};
ConcurrentUsers.Add(ud);
}
}
return null;
}
当我想用他的数据更新用户时,假设他收到一条新消息,然后我可以遍历列表并找到该用户的所有 connectionId,并将消息推送到所有连接的用户客户端。
private static void FlushMessageToClient(Guid UserId, string Message)
{
string[] cids = Hub.ConcurrentUsers.Where(x => x.UserId.Equals(UserId)).Select(x => x.ConnectionId).ToArray();
foreach (string cid in cids)
{
Hub.Clients.Client(cid).messageReceived(Message);
}
}
一切正常,直到网站运行一个小时,用户进进出出,它将开始收到错误:
System.NullReferenceException: Object reference not set to an instance of an object. at Infra.ChatHub.b__16(UserDetail x) at System.Linq.Enumerable.Any[TSource](IEnumerable
1 source, Func
2 predicate) at ChatHub.IsUserAuthenticated()
有没有更好的编程方式?
如果你有一个键索引,我会使用字典:
Dictionary<Guid, UserDetail > dictionary = new Dictionary<Guid, UserDetail >();
你可以这样做:
dictionary.Add(userId, userDetail);
var detail = dictionary[userId];
关于你的错误,请在查询之前检查用户ID是否在列表中。
if (dictionary.ContainsKey(userId)) {}
可以在下面找到最佳选项 将 SignalR 用户映射到连接。
http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections