Как получить уведомление, когда пользователь закрывает браузер в SignalR Blazor?

#c# #signalr #blazor #blazor-server-side #.net-5

#c# #сигналр #blazor #blazor-на стороне сервера #.net-5

Вопрос:

Я хочу отправить уведомление всем пользователям, подключенным в группе, когда пользователь уходит. Как я могу это сделать?

Как вы можете видеть в моем коде, я переопределил этот OnDisconnectedAsync метод, но он работает только при моем вызове HubConnection.DisposeAsync(); . Но если я закрою браузер или вкладку, он ничего не сделает.

 public override Task OnConnectedAsync()
{
    Console.WriteLine("User Joined.");
    return base.OnConnectedAsync();
}

public override Task OnDisconnectedAsync(Exception exception)
{
    Console.WriteLine("User Left.");
    return base.OnDisconnectedAsync(exception);
}
 

Есть ли какой-нибудь способ получать уведомления при закрытии браузера?

Ответ №1:

Вы можете создать класс обслуживания, который является производным от CircuitHandler:

CircuitHandlerService.cs

 using Microsoft.AspNetCore.Components.Server.Circuits;
using System;
using System.Collections.Concurrent;
using System.Threading;
using System.Threading.Tasks;

public class CircuitHandlerService : CircuitHandler 
    {
        public ConcurrentDictionary<string, Circuit> Circuits { get; set; }
        public event EventHandler CircuitsChanged;

        protected virtual void OnCircuitsChanged()
        => CircuitsChanged?.Invoke(this, EventArgs.Empty);

        public CircuitHandlerService()
        {
            Circuits = new ConcurrentDictionary<string, Circuit>();
        }

        public override Task OnCircuitOpenedAsync(Circuit circuit, CancellationToken cancellationToken)
        {
            Circuits[circuit.Id] = circuit;
            OnCircuitsChanged();
            return base.OnCircuitOpenedAsync(circuit, cancellationToken);
        }

        public override Task OnCircuitClosedAsync(Circuit circuit, CancellationToken cancellationToken)
        {
            Circuit circuitRemoved;
            Circuits.TryRemove(circuit.Id, out circuitRemoved);
            OnCircuitsChanged();
            return base.OnCircuitClosedAsync(circuit, cancellationToken);
        }

        public override Task OnConnectionDownAsync(Circuit circuit, CancellationToken cancellationToken)
        {
            return base.OnConnectionDownAsync(circuit, cancellationToken);
        }

        public override Task OnConnectionUpAsync(Circuit circuit, CancellationToken cancellationToken)
        {
            return base.OnConnectionUpAsync(circuit, cancellationToken);
        }
               
    }
 

Добавьте службу в контейнер DI:

 services.AddSingleton<CircuitHandler>(new CircuitHandlerService());
 

Протестируйте сервис:

 @page "/"

@using Microsoft.AspNetCore.Components.Server.Circuits

@inject CircuitHandler circuitHandler
@implements IDisposable


<h1>Hello, world!</h1>

Welcome to your new app.

<p>
    Number of Circuits: @((circuitHandler as BlazorCircuitHandler.Services.CircuitHandlerService).Circuits.Count)
    <ul>
        @foreach (var circuit in (circuitHandler as BlazorCircuitHandler.Services.CircuitHandlerService).Circuits)
        {
            <li>@circuit.Key</li>
        }
    </ul>
</p>

@code {

    protected override void OnInitialized()
    {
        // register event handler
        (circuitHandler as CircuitHandlerService).CircuitsChanged  = HandleCircuitsChanged;
        
        
    }

    public void Dispose()
    {
        // unregister the event handler when the component is destroyed
        (circuitHandler as CircuitHandlerService).CircuitsChanged -= HandleCircuitsChanged;
        
       
    }

    public void HandleCircuitsChanged(object sender, EventArgs args)
    {
        // notify the UI that the state has changed
       InvokeAsync(() => StateHasChanged());
    }
}
 

Ответ №2:

Используйте CircuitHandler

Сервер Blazor позволяет коду определять обработчик схемы, который позволяет запускать код при изменениях состояния схемы пользователя. Обработчик схемы реализуется путем получения из CircuitHandler и регистрации класса в сервисном контейнере приложения.

 public class TrackingCircuitHandler : CircuitHandler 
{
    private HashSet<Circuit> circuits = new HashSet<Circuit>();
    public override Task OnConnectionUpAsync(Circuit circuit, CancellationToken cancellationToken)
    {
        circuits.Add(circuit);
        return Task.CompletedTask;
    }

    public override Task OnConnectionDownAsync(Circuit circuit, CancellationToken cancellationToken)
    {
        circuits.Remove(circuit);
        return Task.CompletedTask;
    }

    public int ConnectedCircuits => circuits.Count;
}
 

Обработчики каналов регистрируются с помощью DI

 services.AddSingleton<CircuitHandler, TrackingCircuitHandler>();