Клиенты SignalR 2.2 не получают сообщений

У меня есть самодостаточное приложение SignalR, работающее в контексте консольного приложения. Я подключаюсь к концентраторам внутри него, используя класс-оболочку, чтобы я не мог ссылаться на сборки SignalR.Core из моего проекта ASP.NET. Таким образом, это еще один класс С# из моего приложения, который отвечает за передачу сообщений связанным клиентам.

Я могу вызвать методы PlanHub (Register и Unregister) непосредственно из Javascript, эти точки останова попадают. Однако, когда я вызываю клиентские методы из класса вне концентратора (даже если я использую Clients.All для устранения проблем с регистрацией группы), клиенты никогда не получат сообщение. Что я делаю неправильно?

При запуске этого кода я могу проверить, что код Clients.All.updateStatus(planId, message); успешно удаляется, но ничего не записывается в консоль на клиенте.

Вот соответствующий код:

PlanHub.cs

public class PlanHub : Hub
{
    private const string GroupPrefix = "PlanGroup_";

    public void Register(int companyId)
    {
        Groups.Add(Context.ConnectionId, $"{GroupPrefix}{companyId}");
    }

    public void Unregister(int companyId)
    {
        Groups.Remove(Context.ConnectionId, $"{GroupPrefix}{companyId}");
    }
}

PlanPublisher.cs

public class PlanPublisher
{
    private readonly static Lazy<PlanPublisher> _instance = new Lazy<PlanPublisher>(() => 
        new PlanPublisher(GlobalHost.ConnectionManager.GetHubContext<PlanHub>().Clients));
    private IHubConnectionContext<dynamic> Clients { get; set; }

    private PlanPublisher(IHubConnectionContext<dynamic> clients)
    {
        Clients = clients;
    }

    public static PlanPublisher Instance => _instance.Value;

    public void UpdateStatus(int planId, string message)
    {
        //This code gets hit and no exceptions are thrown, but the clients
        //never receive the message. Using Clients.All instead of my 
        //Groups for testing -- still doesn't work
        Clients.All.updateStatus(planId, message);
    }
}

Код вызова (из другого класса С#)

PlanPublisher.Instance.UpdateStatus(plan.Id, $"Publishing started for {plan.Name}...");

Javascript

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script> 
<script src="~/scripts/jquery.signalR-2.2.0.min.js"></script>
<script src="http://localhost:8080/signalr/hubs"></script>
<script>
    $(document).ready(function() {
        $.connection.hub.url = "http://localhost:8080/signalr";

        var planMessagePublisher = $.connection.planHub;

        planMessagePublisher.client.updateStatus = function (planId, message) {
            console.log('Status: ' + planId + ' - ' + message);
        };

        $.connection.hub.start().done(function() {
            //This works! My PlanHub.Register method gets called successfully
            planMessagePublisher.server.register(@Model.CompanyId);

            // New connection ID is successfully sent to console
            console.log('Now connected, connection ID=' + $.connection.hub.id);
        });
    });
</script>

Выход отладчика JS Console

jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Auto detected cross domain url.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Client subscribed to hub 'planhub'.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Negotiating with 'http://localhost:8080/signalr/negotiate?clientProtocol=1.5&connectionData=%5B%7B%22name%22%3A%22planhub%22%7D%5D'.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: webSockets transport starting.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Connecting to websocket endpoint 'ws://localhost:8080/signalr/connect?transport=webSockets&clientProtocol=1.5&connectionToken=AQAAANCMnd8BFdERjHoAwE%2FCl%2BsBAAAA5D8YUVyzBEG4tTlTaGn0MgAAAAACAAAAAAADZgAAwAAAABAAAABDjF1MaCTzZI0XTM8gC29xAAAAAASAAACgAAAAEAAAAKrk0jv%2FKF4YFzDvNwmSR8IoAAAAacm1d1r7dJpjOtVtCFIFRpugkubyZm1e5Z8OtOFtnhZyEBO1SO4lqhQAAABiG7hBydTiypPh8k2ZYz20ropNxw%3D%3D&connectionData=%5B%7B%22name%22%3A%22planhub%22%7D%5D&tid=6'.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Websocket opened.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: webSockets transport connected. Initiating start request.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: The start request succeeded. Transitioning to the connected state.
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Now monitoring keep alive with a warning timeout of 13333.333333333332, keep alive timeout of 20000 and disconnecting timeout of 30000
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Invoking planhub.Register
jquery.signalR-2.2.0.min.js?v=8588299:8 [06:23:19 GMT-0700 (Pacific Daylight Time)] SignalR: Invoked planhub.Register

Он регистрируется успешно, но никогда не получает никаких данных. Точка останова Clients.All.updateStatus(planId, message); была удалена несколько раз, но консоль никогда не получала никаких дополнительных данных ведения журнала.

РЕДАКТИРОВАТЬ: Было высказано предположение, что я смотрю, работает ли пользовательский зависимый преобразователь. Код, размещенный здесь, живет в своем собственном проекте с очень небольшим количеством, кроме того, что вы видите здесь. Код, который вызывает PlanPublisher.UpdateStatus(), имеет настраиваемый зависимый преобразователь, но это не имеет значения, поскольку он изолирован в своей собственной сборке. PlanPublisher.cs и PlanHub.cs содержатся в очень простом проекте, который имеет только ссылки на SignalR.Core и SignalR.SelfHost.

Ответ 1

Попробуйте следующее:

public void UpdateStatus(int planId, string message)
{
    var clients = GlobalHost.ConnectionManager.GetHubContext<PlanHub>().Clients;
    clients.All.updateStatus(planId, message);
}

Я думаю, что проблема в том, что PlanPublisher инициализируется до того, как будет создан контекст концентратора (и Клиенты).

Ответ 2

Я попытался упростить ваш пример (используя SignalR 2.2.0)

Класс концентратора:

public class PlanHub : Hub
{
    private const string GroupPrefix = "PlanGroup_";

    // hubcontext
    private static IHubContext hubContext = GlobalHost.ConnectionManager.GetHubContext <PlanHub> ();

    public void Register(int companyId)
    {
        Groups.Add(Context.ConnectionId, $"{GroupPrefix}{companyId}");
    }

    public void Unregister(int companyId)
    {
        Groups.Remove(Context.ConnectionId, $"{GroupPrefix}{companyId}");
    }

    // static method using hub context
    public static void Static_UpdateStatus(int planId, string message)
    {
        hubContext.Clients.All.updateStatus(planId, message);
    }
}

Так как у меня на самом деле нет бэкэнд, и я не знаю, как вы собираетесь это делать, я просто настроил таймер, чтобы вызвать метод из С# каждые 10 секунд.

Таймер вызова и фиктивный:

    public void startTimer()
    {
        timer = new System.Timers.Timer();
        timer.Elapsed += new System.Timers.ElapsedEventHandler(ExecuteTimerJob);
        timer.Interval = 10000; // 10 sec
        timer.Start();
    }

    public static void ExecuteTimerJob(object source, System.Timers.ElapsedEventArgs e)
    {
        // this is the call from C#
        PlanHub.Static_UpdateStatus(123, "some message");
    }

JS:

$(function () {
    var planHub = $.connection.planHub;
    $.connection.hub.start().done(function () {
         console.debug('Connected to PlanHub...');
    })
    planHub.client.updateStatus = function (planId, message) {
        console.log("planId: " + planId);
        console.log("message: " + message);
    } 
});

Результат в консоли браузера:

введите описание изображения здесь