【问题标题】:SignalR concurrent list best practiceSignalR 并发列表最佳实践
【发布时间】:2015-03-14 17:41:25
【问题描述】:

我在 c# MVC Web 应用程序中使用 SignalR。我想要一个当前表单认证用户的对象列表( UserDetail )。此对象应包含 UserId(表中的 Pk)和 ConnectionId(SignalR)。

今天我在中心类中使用静态列表:

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:对象引用未设置为实例 的一个对象。在 Infra.ChatHub.b__16(UserDetail x) 在 System.Linq.Enumerable.Any[TSource](IEnumerable1 source, Func2 谓词)在 ChatHub.IsUserAuthenticated()

有没有更好的编程方法?

【问题讨论】:

  • 问题是您在迭代集合(.Where)时正在更新(.Add)。您要么需要使用锁,要么更改为线程安全集合

标签: c# signalr


【解决方案1】:

如果你有键索引,我会使用字典:

Dictionary<Guid, UserDetail > dictionary = new Dictionary<Guid, UserDetail >();

你可以这样做:

dictionary.Add(userId, userDetail);   
var detail = dictionary[userId];

关于您的错误,请在查询前检查 userId 是否在列表中。

if (dictionary.ContainsKey(userId)) {}

【讨论】:

  • 请记住,每个 UserId 都有很多 ConnectionId ,用户可以从不同的选项卡/浏览器登录。所以我需要快速可靠地访问所有给定 UserId 的 connectionId,反之亦然。
  • 好的(我认为 userId 是一个键,如问题所述)。然后,您可以使用 struct (userId, connectionID) 作为键。见stackoverflow.com/questions/689940/…
  • UserId 是关键,但可以有超过 1 个连接 ID。
【解决方案2】:

可以在下面找到最佳选项 将 SignalR 用户映射到连接。

http://www.asp.net/signalr/overview/guide-to-the-api/mapping-users-to-connections

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-19
    • 2019-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多