如何在用户关闭 SignalR Blazor 中的浏览器时收到通知?

How to get notify when a user closes the browser in SignalR Blazor?

我想在用户离开时向组中所有连接的用户发送通知。我该怎么做?

正如您在我的代码中看到的那样,我已经覆盖了 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);
}

有没有办法在浏览器关闭时收到通知?

使用CircuitHandler

Blazor Server allows code to define a circuit handler , which allows running code on changes to the state of a user's circuit. A circuit handler is implemented by deriving from CircuitHandler and registering the class in the app's service container.

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>();

您可以创建一个派生自 CircuitHandler 的服务class:

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());
    }
}