【问题标题】:Mapping signalr users to connections using SQL Server使用 SQL Server 将信号器用户映射到连接
【发布时间】:2015-09-28 15:53:17
【问题描述】:

我读到了这个topic,关于将 SignalR 用户映射到连接。简而言之,该主题解释了四种映射方法,我想使用第四种方法(永久,外部存储)。该方法使用 SQL Server 数据库存储ConnectionId,当客户端连接时(当OnConnected 方法被触发时)和当客户端关闭浏览器时(当OnDisconnected 方法被触发时)它只是使ConnectionId无效。

这是数据库的代码:

public class UserContext : DbContext
{
    public DbSet<User> Users { get; set; }
    public DbSet<Connection> Connections { get; set; }
}

public class User
{
    [Key]
    public string UserName { get; set; }
    public ICollection<Connection> Connections { get; set; }
}

public class Connection
{
    public string ConnectionID { get; set; }
    public string UserAgent { get; set; }
    public bool Connected { get; set; }
}

这里是 hub 类中的代码:

[Authorize]
public class ChatHub : Hub
{
    public void SendChatMessage(string who, string message)
    {
        var name = Context.User.Identity.Name;
        using (var db = new UserContext())
        {
            var user = db.Users.Find(who);
            if (user == null)
            {
                Clients.Caller.showErrorMessage("Could not find that user.");
            }
            else
            {
                db.Entry(user)
                    .Collection(u => u.Connections)
                    .Query()
                    .Where(c => c.Connected == true)
                    .Load();

                if (user.Connections == null)
                {
                    Clients.Caller.showErrorMessage("The user is no longer connected.");
                }
                else
                {
                    foreach (var connection in user.Connections)
                    {
                        Clients.Client(connection.ConnectionID)
                            .addChatMessage(name + ": " + message);
                    }
                }
            }
        }
    }

    public override Task OnConnected()
    {
        var name = Context.User.Identity.Name;
        using (var db = new UserContext())
        {
            var user = db.Users
                .Include(u => u.Connections)
                .SingleOrDefault(u => u.UserName == name);

            if (user == null)
            {
                user = new User
                {
                    UserName = name,
                    Connections = new List<Connection>()
                };
                db.Users.Add(user);
            }

            user.Connections.Add(new Connection
            {
                ConnectionID = Context.ConnectionId,
                UserAgent = Context.Request.Headers["User-Agent"],
                Connected = true
            });
            db.SaveChanges();
        }
        return base.OnConnected();
    }

    public override Task OnDisconnected(bool stopCalled)
    {
        using (var db = new UserContext())
        {
            var connection = db.Connections.Find(Context.ConnectionId);
            connection.Connected = false;
            db.SaveChanges();
        }
        return base.OnDisconnected(stopCalled);
    }
}

我想增强这种方法,因为使用这种方法会创建许多根本不需要的 ConnectionId。此外,使用这种方法,连接表将随着时间的推移而扩大,而没有任何用处。

【问题讨论】:

  • 是否需要维护多个连接ID?您是否需要支持a single user connected on two or more machines simoulathenously
  • 在我的应用程序中,用户可能使用多个浏览器或多个设备,所以我有一个要求,同时支持两台或多台机器。

标签: asp.net sql-server asp.net-mvc signalr signalr-hub


【解决方案1】:

如何清理 connectionIds 而不是将 Connected 属性设置为 false?您的 OnDisconnnected() 方法可能看起来像这样:

public override Task OnDisconnected(bool stopCalled)
{
    using (var db = new UserContext())
    {
        var connection = db.Connections.Find(Context.ConnectionId);
        db.Connections.Remove(connection);
        db.SaveChanges();
    }
    return base.OnDisconnected(stopCalled);
}

这将阻止 Connections 表无限增长。然后,您可以完全删除 Connected 属性,并让 Connection 行的存在表明连接处于活动状态。

更新

另一种方法,如果您不想删除记录但重新使用数据库中已有的记录,可能如下所示:

public override Task OnConnected()
{
    var name = Context.User.Identity.Name;
    using (var db = new UserContext())
    {
        var user = db.Users
            .Include(u => u.Connections)
            .SingleOrDefault(u => u.UserName == name);

        if (user == null)
        {
            user = new User
            {
                UserName = name,
                Connections = new List<Connection>()
            };
            db.Users.Add(user);
        }

        var connection = user.Connections.Where(c => c.Connected == false && UserAgent == Context.Request.Headers["User-Agent"]).FirstOrDefault();
        if (connection == null) 
        {
             connection = new Connection();
             connection.UserAgent = Context.Request.Headers["User-Agent"];
             user.Connections.Add(connection);
        }

        connection.ConnectionID = Context.ConnectionId;
        connection.Connected = true;
        db.SaveChanges();
    }
    return base.OnConnected();
}

这是否符合您的想法?

【讨论】:

  • 感谢您的回答,我给您+1。但是为什么要删除记录,如果 onConnect 触发时存在记录,更新记录呢?
  • 谢谢艾哈迈德。我建议删除记录以避免表无限增长,因为我认为这是您的要求。如果记录刚刚更新,表将继续增长,因为每次浏览器通过 signalR 连接时都会生成一个新的唯一 connectionId。 OnConnected() 只为每个连接触发一次,因此无需在我看到的情况下更新任何内容 - 只需添加有关新连接的信息。
  • 我的意思是我们在 OnConnected 事件中有两个案例。第一种情况是当数据库中的用户没有连接时,在这种情况下,我们将在数据库中创建新记录。第二种情况是数据库中有用户的连接,但 Connected 的值为 false,所以在这种情况下,我们将更新 ConnectionID 的值和 Connected 的值。我希望这能解释我在上一条评论中所说的话。
  • 我想我明白你在追求什么。我不确定重复使用现有记录而不是删除和创建新记录的收益有多大,但如果你有一个高流量的场景,那么也许是这样。我已经更新了我的答案,重新使用了 Connected false 的连接实现。你怎么看?
猜你喜欢
  • 1970-01-01
  • 2013-08-30
  • 1970-01-01
  • 2016-11-28
  • 1970-01-01
  • 2020-05-12
  • 1970-01-01
  • 2012-03-27
  • 2012-12-04
相关资源
最近更新 更多