从 "other place in code" 中获取 SignalR Hub 而不会丢失其客户端
Obtaining SignalR Hub without losing its clients from "other place in code"
我有一个应用程序,该应用程序必须通过 USB 端口连接的外部设备在网页上显示数据
之前,我给了用户按钮 "Start listening",它向后端发送 http 请求,后端开始监听端口(并阻止了应用程序,但没问题,因为它应该同时由 1 个人使用)直到收到无错误响应 (SerialDataReceivedEvent
/SerialErrorReceivedEvent
)
SerialData
从该请求返回并显示在页面
上
我必须使用 SignalR 重写它,所以我很快就想出了这样一个天真的解决方案:
public class DeviceReaderHub : Hub
{
private readonly IConfiguration _config;
// this is static because listening that port (SerialPort) has to stay open
private static DeviceReader_Helper _service;
public DeviceReaderHub(IConfiguration config)
{
_config = config;
if (_service == null)
{
_service = new DeviceReader_Helper();
_service.Open(_config["DeviceInfo:Port"]);
}
_service.DataReceived_Delegate = SendMessage;
_service.ErrorReceived_Delegate = SendErrorMessage;
}
public async Task SendMessage(string message)
{
await Clients.All.SendAsync("onRead", message);
}
public async Task SendErrorMessage(string message)
{
await Clients.All.SendAsync("onRead", $"error = {message}");
}
public async override Task OnConnectedAsync()
{
await Clients.All.SendAsync("onConnected");
await base.OnConnectedAsync();
}
}
但我很快就收到了现实检查 - 我不能那样做,因为 DeviceReaderHub
被处理了。
Exception thrown: 'System.ObjectDisposedException' in Microsoft.AspNetCore.SignalR.Core.dll
我考虑在 SerialDataReceivedEvent
或 SerialErrorReceivedEvent
被调用时获取新的 DeviceReaderHub
中心
但我不知道如何在不丢失连接的客户端的情况下获取集线器实例
SignalR 集线器是瞬态对象,这意味着新的集线器实例用于来自客户端的集线器上的每个方法调用。根据this Microsoft Docs:
Don't store state in a property on the hub class. Every hub method call is executed on a new hub instance.
因此,除了接收和处理来自客户端的请求外,您不应使用集线器执行任何其他操作。为了将消息发送到集线器外部的客户端,SignalR 提供了 HubContext<T>
class,可从依赖注入中获得。例如:
public class DeviceReader_Helper {
private readonly IHubContext<DeviceReaderHub> _hubContext;
// you can obtain hubContext either from constructor DI, or service locator pattern with an IServiceProvider
public DeviceReader_Helper(IHubContext<DeviceReaderHub> hubContext) {
_hubContext = hubContext;
}
public async Task SendMessage(string message) {
await _hubContext.Clients.All.SendAsync("onRead", message);
}
}
有关详细信息,请参阅 this。至于你担心的
but I have not idea how can I get instance of the hub without losing connected clients
无需集线器实例即可连接客户端。集线器仅用于接收来自客户端的消息,并不是保持客户端与服务器连接所必需的。
我有一个应用程序,该应用程序必须通过 USB 端口连接的外部设备在网页上显示数据
之前,我给了用户按钮 "Start listening",它向后端发送 http 请求,后端开始监听端口(并阻止了应用程序,但没问题,因为它应该同时由 1 个人使用)直到收到无错误响应 (SerialDataReceivedEvent
/SerialErrorReceivedEvent
)
SerialData
从该请求返回并显示在页面
我必须使用 SignalR 重写它,所以我很快就想出了这样一个天真的解决方案:
public class DeviceReaderHub : Hub
{
private readonly IConfiguration _config;
// this is static because listening that port (SerialPort) has to stay open
private static DeviceReader_Helper _service;
public DeviceReaderHub(IConfiguration config)
{
_config = config;
if (_service == null)
{
_service = new DeviceReader_Helper();
_service.Open(_config["DeviceInfo:Port"]);
}
_service.DataReceived_Delegate = SendMessage;
_service.ErrorReceived_Delegate = SendErrorMessage;
}
public async Task SendMessage(string message)
{
await Clients.All.SendAsync("onRead", message);
}
public async Task SendErrorMessage(string message)
{
await Clients.All.SendAsync("onRead", $"error = {message}");
}
public async override Task OnConnectedAsync()
{
await Clients.All.SendAsync("onConnected");
await base.OnConnectedAsync();
}
}
但我很快就收到了现实检查 - 我不能那样做,因为 DeviceReaderHub
被处理了。
Exception thrown: 'System.ObjectDisposedException' in Microsoft.AspNetCore.SignalR.Core.dll
我考虑在 SerialDataReceivedEvent
或 SerialErrorReceivedEvent
被调用时获取新的 DeviceReaderHub
中心
但我不知道如何在不丢失连接的客户端的情况下获取集线器实例
SignalR 集线器是瞬态对象,这意味着新的集线器实例用于来自客户端的集线器上的每个方法调用。根据this Microsoft Docs:
Don't store state in a property on the hub class. Every hub method call is executed on a new hub instance.
因此,除了接收和处理来自客户端的请求外,您不应使用集线器执行任何其他操作。为了将消息发送到集线器外部的客户端,SignalR 提供了 HubContext<T>
class,可从依赖注入中获得。例如:
public class DeviceReader_Helper {
private readonly IHubContext<DeviceReaderHub> _hubContext;
// you can obtain hubContext either from constructor DI, or service locator pattern with an IServiceProvider
public DeviceReader_Helper(IHubContext<DeviceReaderHub> hubContext) {
_hubContext = hubContext;
}
public async Task SendMessage(string message) {
await _hubContext.Clients.All.SendAsync("onRead", message);
}
}
有关详细信息,请参阅 this。至于你担心的
but I have not idea how can I get instance of the hub without losing connected clients
无需集线器实例即可连接客户端。集线器仅用于接收来自客户端的消息,并不是保持客户端与服务器连接所必需的。