ASP.NET Core SignalR Clients объект избавился от исключения при вызове из обратного вызова - PullRequest
0 голосов
/ 05 июня 2018

У меня есть следующая реализация концентратора в новом и чистом веб-приложении ASP.NET Core 2.1.Я только что обновил до последней версии ASP.NET Core 2.1 и до последней версии Visual Studio 2017.

Класс работал и при запуске из отладчика подключается один клиент.Я могу видеть это с помощью отладчика, и я могу видеть это в клиенте, потому что я регистрирую «userobject», который я посылаю после того, как он подключился.Клиент остается на связи.

На втором шаге я внедрил IPbxConnection в концентратор, который также работает (я вижу действительный объект с помощью отладчика).Реализация IPbxConnection вызовет обработчик OnUserUpdated через 5 секунд (я просто делаю это с обратным вызовом по таймеру, который теперь находится в реализации IPbxConnection для тестирования).Это всегда приводит к исключению удаления объекта, выданному объекту Clients.Как я могу отправить уведомление всем клиентам в этом обратном вызове?Кажется, объект Clients не сохраняет свое состояние и действителен только во время обработки сообщений ... однако я хочу постоянно передавать информацию клиенту.

public class PresenceHub : Hub
{
    //Members
    private IPbxConnection _connection; 

    /// <summary>
    /// Constructor, pbx connection must be provided by dependency injection
    /// </summary>        
    public PresenceHub(IPbxConnection connection)
    {
        _connection = connection;
        _connection.OnUserUpdated((e) =>
        {                
            Clients.All.SendAsync("UpdateUser", "updateuserobject");
        });
        _connection.Connect();
    }

    /// <summary>
    /// Called whenever a user is connected to the hub. We will send him all the user information
    /// </summary>
    public override async Task OnConnectedAsync()
    {            
        await base.OnConnectedAsync();
        await Clients.Caller.SendAsync("AddUser", "userobject");
    }
}

Ответы [ 4 ]

0 голосов
/ 28 октября 2018

Это простое решение работает для меня.Я не добавил ничего лишнего к классу запуска.

РЕДАКТИРОВАТЬ После некоторого размышления я решил, что хотя этот код работает, это не очень хорошая модель, не в последнюю очередь потому, что статический код заканчиваетсяпытаясь использовать поля в расположенном объекте.Концентратор - это легкий, недолговечный контейнер, который следует использовать как таковой.Поэтому я перемещаю свой длительный процесс из статических элементов концентратора в шаблон IHostedService

Мой концентратор содержит длительный асинхронный процесс, определенный в статическом члене.Поскольку концентраторы являются временными, в некоторых случаях концентратор удаляется, когда асинхронный процесс пытается отправить сообщения.Я добавил контекстный концентратор для инъекции в конструктор концентратора

public class IisLogFileHub : Hub
{
    IHubContext<IisLogFileHub> _hubContext = null;

    public IisLogFileHub(IHubContext<IisLogFileHub> hubContext)
    {
        _hubContext = hubContext;
    }
}

В любой момент длительного процесса сообщения можно отправлять следующим образом:

await _hubContext.Clients.All.SendAsync("ReceiveMessage", msg);
0 голосов
/ 07 июня 2018

Как описано в этом выпуске на Github https://github.com/aspnet/SignalR/issues/2424 Концентраторы недолговечны по конструкции и располагаются после каждого вызова.

Единственный способ, которым я нашел доступ к вашим Клиентам за пределами текущего запроса Область действияна вашем концентраторе путем внедрения HubContext в отдельный класс.

В вашем случае широковещательная рассылка всем клиентам будет выглядеть примерно так:

public class HubEventEmitter
{
    private IHubContext<PresenceHub> _hubContext;

    public HubEventEmitter(IPbxConnection connection, IHubContext<PresenceHub> hubContext)
    {
        _hubContext = hubContext;
        _connection.OnUserUpdated((e) =>
        {
            _hubContext.Clients.All.SendAsync("UpdateUser", "updateuserobject");
        });
    }
}

, если вы хотите уведомить только определенных клиентов, которых выПридется собрать идентификатор соединения из контекста и использовать его следующим образом

_hubContext.Clients.Client(connectionId).SendAsync("UpdateUser", "updateuserobject");
0 голосов
/ 25 августа 2018

Я столкнулся с той же проблемой некоторое время назад.Хотя я тоже думал, что хаб был местом, куда нужно звонить, когда вам нужно отправить сообщение в c #, но на самом деле это не так.Концентратор больше похож на точку крепления.Ваш бэкэнд c # должен иметь своего собственного клиента, который также подключается к концентратору.Таким образом, ваш концентратор выглядит следующим образом:

public class PresenceHub : Hub
{
    public async Task Send(Userobject userobject)
    {
        await Clients.Others.SendAsync("AddUser", userobject);
    }


    public override async Task OnConnectedAsync()
    {            
        await base.OnConnectedAsync();
        await Clients.Caller.SendAsync("AddUser", "userobject");
    }
}

, а затем пользовательский класс / фрагмент кода, который вызывает концентратор следующим образом:

public class MyCustomCode{
    public PresenceHub(IPbxConnection connection)
    {
        hubConnection = new HubConnectionBuilder().WithUrl("localhost\mysignalrhub").Build();
        hubConnection.StartAsync().Wait();

        _connection = connection;
        _connection.OnUserUpdated((e) =>
        {                
            hubConnection.InvokeAsync("Send", e).Wait();
        });
        _connection.Connect();
    }

}

Некоторая документация: https://docs.microsoft.com/en-us/aspnet/core/signalr/dotnet-client?view=aspnetcore-2.1

0 голосов
/ 07 июня 2018

Эта проблема косвенно дается этим вопросом на github https://github.com/aspnet/Docs/issues/6888. Это была «проблема» в документации, поэтому то, что я пытался сделать, наверняка невозможно (здесь не упоминается, как это сделать, однако).

Кажется, что экземпляр класса-концентратора в целом живет только тогда, когда сделан вызов, и поэтому вы не можете использовать обратные вызовы.

...