从 ABP 中的 BackgroundWorker 调用 SignalR 服务

Calling SignalR service from a BackgroundWorker in ABP

我有一个 BackgroundWorker,它应该以 5 秒的间隔向所有在线客户端广播一些内容:

DeactivationBackgroundWorker:

public class DeactivationBackgroundWorker : PeriodicBackgroundWorkerBase, ISingletonDependency
{
    private readonly IRepository<HitchRequest, long> _hitchRequestRepository;
    private readonly IHitchHub _hitchHub;

    public DeactivationBackgroundWorker(AbpTimer timer,
                                        IRepository<HitchRequest, long> hitchRequestRepository,
                                        IHitchHub hitchHub) : base(timer)
    {
        _hitchRequestRepository = hitchRequestRepository;
        Timer.Period = 5000;
        _hitchHub = hitchHub;
    }

    protected override async void DoWork()
    {
        await broadcastHitchRequestsAsync();
    }

    [UnitOfWork]
    private async Task broadcastHitchRequestsAsync() {
        var activeHitchRequests = _hitchRequestRepository.GetAllList(p => p.IsActive);

        foreach (var hitchRequest in activeHitchRequests)
        {
            await _hitchHub.RequestHitch(hitchRequest.Id);
        }

    }
}

IHitchHub:

public interface IHitchHub: ITransientDependency
{
    Task RequestHitch(long hitchId);
}

HitchHub:

public class HitchHub : AbpCommonHub, IHitchHub
{
    private readonly IOnlineClientManager _onlineClientManager;

    public HitchHub(IOnlineClientManager onlineClientManager, IClientInfoProvider clientInfoProvider): base(onlineClientManager, clientInfoProvider)
    {
        _onlineClientManager = onlineClientManager;
    }

    public async Task RequestHitch(long hitchId)
    {
        var onlineClients = _onlineClientManager.GetAllClients();
        foreach (var onlineClient in onlineClients) {

            var signalRClient = Clients.Client(onlineClient.ConnectionId);

            await signalRClient.SendAsync("receiveHitch", hitchId);
        }
    }
}

不知道为什么HitchHubclass里面的Clients总是空的!我应该在哪里初始化它?

注入 IHubContext<HitchHub> 而不是 IHitchHub

例如,参见ABP的SignalRRealTimeNotifier

相关问题:https://github.com/aspnet/SignalR/issues/182

如果您使用 Asp.net 核心,请参阅:

否则:

Hub 不应由您自己或任何注入框架实例化

您必须使用连接管理器。对于您的代码,您应该使用:

GlobalHost.ConnectionManager.GetHubContext<HitchHub>().RequestHitch(hitchRequest.Id);

**关于 Hub 对象生命周期的背景信息:**

https://docs.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-server:

You don't instantiate the Hub class or call its methods from your own code on the server; all that is done for you by the SignalR Hubs pipeline. SignalR creates a new instance of your Hub class each time it needs to handle a Hub operation such as when a client connects, disconnects, or makes a method call to the server.

Because instances of the Hub class are transient, you can't use them to maintain state from one method call to the next. Each time the server receives a method call from a client, a new instance of your Hub class processes the message. To maintain state through multiple connections and method calls, use some other method such as a database, or a static variable on the Hub class, or a different class that does not derive from Hub. If you persist data in memory, using a method such as a static variable on the Hub class, the data will be lost when the app domain recycles.

If you want to send messages to clients from your own code that runs outside the Hub class, you can't do it by instantiating a Hub class instance, but you can do it by getting a reference to the SignalR context object for your Hub class. For more information, see How to call client methods and manage groups from outside the Hub class later in this topic.

另请参阅: https://docs.microsoft.com/en-us/aspnet/signalr/overview/guide-to-the-api/hubs-api-guide-server#callfromoutsidehub