Если я правильно понял, вы хотите отложить отправку сообщений SignalR, используя что-то вроде синхронизированного вызова в некотором IHostedService. Вот чего мне удалось достичь.
- Ваш подход, который заключается в использовании ConcurrentQueue, который содержит вызываемые делегаты Action для обработки одновременных вызовов концентратора, является правильным. Как вы упоминаете, он должен вводиться как синглтон.
Итак, класс Queues
:
public class Queues {
public ConcurrentQueue<Action<IHubContext<MyHub, IMyEvents>>> MessagesQueue { get; set; }
}
- Теперь нам нужно захватить
ConnectionId
звонящего, чтобы звонок мог получить ответ позже. SendMessage
поставить в очередь необходимый делегат действия для выполнения вызова экземпляра концентратора в качестве параметра.
В качестве примера SendMessage
вызовет ответ обратно вызывающей стороне, а BroadcastMessage
отправит сообщение всем клиентам.
Использование вместо этого захваченного экземпляра концентратора приведет к исключению, поскольку концентратор будет быстро удален. Вот почему это будет введено позже в другом классе. Посмотрите на SendMessage_BAD
Вот класс MyHub
и соответствующий интерфейс IMyEvents
:
public interface IMyEvents {
void ReceiveMessage(string myMessage);
}
public class MyHub : Hub<IMyEvents> {
Queues queues;
public MyHub(Queues queues) {
this.queues = queues;
}
public void SendMessage(string message) {
var callerId = Context.ConnectionId;
queues.MessagesQueue.Enqueue(hub => hub.Clients.Client(callerId).ReceiveMessage(message));
}
// This will crash
public void SendMessage_BAD(string message) {
this.callerId = Context.ConnectionId;
queues.MessagesQueue.Enqueue(_ => this.Clients.Client(callerId).ReceiveMessage(message));
}
public void BroadcastMessage(string message) {
queues.MessagesQueue.Enqueue(hub => hub.Clients.All.ReceiveMessage(message));
}
}
- Теперь, используя наивный подход, этот код вызовет отправку сообщения отложенным способом. (На работе таймер обеспечивает регулярную каденцию, а класс
IHostedService
, но его здесь нет). Этот класс должен вводиться как синглтон.
Здесь класс DeferredMessageSender
:
public class DeferredMessageSender {
Queues queues;
IHubContext<MyHub, IMyEvents> hub;
public DeferredMessageSender(Queues queues, IHubContext<MyHub, IMyEvents> hub) {
this.queues = queues;
this.hub = hub;
}
public void GlobalSend() {
while(queues.MessagesQueue.TryDequeue(out var evt)) {
evt.Invoke(hub);
}
}
}
Надеюсь, это поможет.