使用 Signalr 更新列表

Updating a list with Signalr

我正在编写信号器客户端-服务器应用程序。

服务器维护一个项目列表。

客户端显示列表并让用户向列表中添加项目。

假设服务器有一个 GetList 方法,returns 整个列表,客户端有一个 NewItem 方法,每当客户端向列表添加项目时,服务器都会调用该方法。

这是客户端代码:

public void Init()
{
    hubConnection = new HubConnection(url);
    proxy = hubConnection.CreateHubProxy(hubName);
    proxy.On<String>("NewItem", OnNewItem);
    hubConnection.Start();
    list = proxy.Invoke("GetList");
}

private void OnNewItem(string item)
{
   list.Add(item);
}

我担心以下情况:

  1. 客户端 A 已连接
  2. 客户端 B 连接到服务器
  3. 客户端A添加一个项目到列表中,同时客户端B发送GetList消息获取列表

此时服务器中有2个线程。线程 A 正在添加一个项目,然后将事件与新项目一起发送到客户端。线程 B 正在加载列表并将其发送回客户端 B。

假设线程 B 在线程 A 添加新项目之前从数据库加载了列表。然后有一个上下文切换,线程 A 添加了项目并向所有客户端发送了一条包含新项目的消息。

现在线程 B 恢复并将列表(没有新项目)发送给客户端 B。

从客户端 B 的角度来看,它收到了一个事件,其中包含他不知道如何处理的新项目,然后它获得了列表但没有新项目。

客户端 B 可以忽略新项目消息(因为它是在列表初始化之前出现的)但是他将始终缺少 1 个项目,因为服务器永远不会为该项目发送另一个事件。

B也可以将消息添加到空列表中,然后添加它得到的列表。但是,如果客户端 A 没有添加项目,而是删除了项目怎么办? B在列表初始化之前如何处理删除项事件?

您尝试做的事情听起来可能有些困难,尤其是当您允许多个客户端同时任意编辑列表时。如果是这种情况,您将考虑实施 operational transformation algorithm。虽然这已经得到了相当广泛的研究,但它远非微不足道。

如果您只是想订阅单一来源所做的编辑,这会容易得多。您可以利用以下事实:您将订阅 await hubConnection.Start(); 完成后发送的任何更新。

这意味着在客户端调用 "GetList" 期间或之后对 "NewItem" 进行的任何服务器端调用都将被客户端 OnNewItem 回调接收只要您在尝试调用 "GetList".

之前正确 await 调用 hubConnection.Start()

您在 OnNewItem 中收到的项目仍然可以重复对 "GetList" 的回复。要解决此问题,您为添加到列表中的每个项目提供一个单调递增的 ID,您将其与项目本身一起传递给 OnNewItem 回调。 "GetList" 响应还应包含其最近添加的项目的 ID。

在收到对 "GetList" 的回复之前,您将 OnNewItem 中收到的所有项目保存在一个临时列表中。收到对 "GetList" 的响应后,您可以在临时列表中添加 ID 大于添加到 "GetList" 响应的最新项目 ID 的任何项目。您忽略 ID 小于或等于 "GetList" 响应中返回的 ID 的任何项目。