如何判断用户何时离开Blazor服务器端的站点
Blazor服务器端是基于Signal构建的,所以我假设它知道用户何时离开网站(关闭连接)。是否有任何引发的事件可用于记录此事件?或者任何其他方式真的 我认为这项服务可以帮助您如何判断用户何时离开Blazor服务器端的站点,blazor,blazor-server-side,Blazor,Blazor Server Side,Blazor服务器端是基于Signal构建的,所以我假设它知道用户何时离开网站(关闭连接)。是否有任何引发的事件可用于记录此事件?或者任何其他方式真的 我认为这项服务可以帮助您 public class CircuitHandlerService : CircuitHandler { public ConcurrentDictionary<string, Circuit> Circuits { get; set; } public event
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)
{
Console.WriteLine("OnCircuitClosedAsync");
Circuit circuitRemoved;
Circuits.TryRemove(circuit.Id, out circuitRemoved);
OnCircuitsChanged();
return base.OnCircuitClosedAsync(circuit, cancellationToken);
}
public override Task OnConnectionDownAsync(Circuit circuit, CancellationToken cancellationToken)
{
Console.WriteLine("OnConnectionDownAsync");
return base.OnConnectionDownAsync(circuit, cancellationToken);
}
public override Task OnConnectionUpAsync(Circuit circuit, CancellationToken cancellationToken)
{
return base.OnConnectionUpAsync(circuit, cancellationToken);
}
}
公共类CircuitHandler服务:CircuitHandler
{
公共ConcurrentDictionary电路{get;set;}
公共事件处理程序发生故障;
受保护的虚拟void OnCircuitsChanged()
=>CircuitsChanged?.Invoke(这个,EventArgs.Empty);
公共电路HandlerService()
{
电路=新的ConcurrentDictionary();
}
公用覆盖任务OnCircuitOpenedAsync(电路,取消令牌取消令牌)
{
电路[circuit.Id]=电路;
OnCircuitsChanged();
返回base.OnCircuitOpenedAsync(电路,取消令牌);
}
CircuitClosedAsync上的公共覆盖任务(电路,取消令牌取消令牌)
{
Console.WriteLine(“OnCircuitClosedAsync”);
拆除电路;
Circuits.TryRemove(circuit.Id,out-circuit已移除);
OnCircuitsChanged();
返回base.OnCircuitClosedAsync(电路,取消令牌);
}
公共覆盖任务OnConnectionDownAsync(电路电路,CancellationToken CancellationToken)
{
WriteLine(“OnConnectionDownAsync”);
返回base.OnConnectionDownAsync(电路,取消令牌);
}
公共覆盖任务OnConnectionUpAsync(电路电路,CancellationToken CancellationToken)
{
返回base.OnConnectionUpAsync(电路,取消令牌);
}
}
测试
剃刀索引
@page/“
@使用Microsoft.AspNetCore.Components.Server.Circuits
@使用BlazorCircuitHandler.Services
@注入电路处理程序电路处理程序
@实现IDisposable
你好,世界!
欢迎使用您的新应用程序。
电路数:@((circuitHandler as.Services.CircuitHandlerService.Circuits.Count)
@foreach(var电路输入(作为BlazorCircuitHandler.Services.CircuitHandlerService.Circuits的电路处理器)
{
- @电路,钥匙
}
@代码{
受保护的覆盖无效OnInitialized()
{
(circuitHandler作为CircuitHandlerService)。CircuitsChanged+=HandleCircuitsChanged;
}
公共空间处置()
{
(circuitHandler作为CircuitHandlerService)。CircuitsChanged-=HandleCircuitsChanged;
}
public void HandleCircuitsChanged(对象发送方、事件args args)
{
//通知UI状态已更改
InvokeAsync(()=>StateHasChanged());
}
}
Startup.cs
public void配置服务(IServiceCollection服务)
{
services.AddRazorPages();
AddServerSideBlazor();
AddSingleton(新的CircuitHandlerService());
}
希望这有帮助…在您的应用程序中,可能有一个类派生自Microsoft.AspNetCore.signal.Hub。如果没有,您可以创建一个。那么在这节课上
public class MyHub : Hub
{
public override async Task OnDisconnectedAsync(Exception exception)
}
当用户断开连接时激发。集线器的Context属性包含有关用户的信息(至少是连接id)。我会做并且已经做了以下操作 使用以下签名创建一个接口和该接口的实现,以适合您的场景。这将是您的中心服务,跟踪您的用户联机状态
public interface IUserOnlineService
{
void Connect(string circuitId, User user);
void DisConnect(string circuitId);
}
在启动时将接口的实现作为单例注入
然后创建一个从CircuitHandler派生的自定义类。它应该使用以前创建的IUserOnlineService的实例
public class CircuitHandlerService : CircuitHandler
{
public string CircuitId { get; set; }
IUserOnlineService useronlineservice;
public CircuitHandlerService(IUserOnlineService useronlineservice)
{
this.useronlineservice = useronlineservice;
}
public override Task OnCircuitOpenedAsync(Circuit circuit, CancellationToken cancellationToken)
{
CircuitId = circuit.Id;
return base.OnCircuitOpenedAsync(circuit, cancellationToken);
}
public override Task OnCircuitClosedAsync(Circuit circuit, CancellationToken cancellationToken)
{
useronlineservice.DisConnect(circuit.Id);
return base.OnCircuitClosedAsync(circuit, cancellationToken);
}
}
在启动时将此服务作为作用域服务添加。这样将有一个实例pr连接
services.AddScoped<CircuitHandler>((sp) => new CircuitHandlerService(sp.GetRequiredService<IUserOnlineService>()));
services.AddScoped((sp)=>newCircuitHandlerService(sp.GetRequiredService());
现在,将您的CircuitHandler和IUserOnLineservice插入到您的登录逻辑所在的位置,然后在用户身份验证后,使用CircuitHandler上的CircuitId属性,该属性将保存当前连接的Id,并在IUserOnLineservice接口上调用Connect()。断开连接是在CircuitHandler中处理的,因为我们这里只需要(并且拥有)电路Id。因为Enet已经评论过,如果您想在中注入AuthenticationStateProvider,TrackingCircuitHandler还需要作为AuthenticationStateProvider确定范围 如果要跟踪应用程序中的用户名,可以将作为单例使用,并将其注入TrackingCircuitHandler 如果您只想在用户离开网站时登录,则可以跳过IUsersStateContainer,只需插入记录器 首先创建内存中的“UsersStateContainer”:
public class UsersStateContainer : IUsersStateContainer
{
public ConcurrentDictionary<string, string> UsersByConnectionId { get; set; } =
new ConcurrentDictionary<string, string>();
public event Action OnChange;
public void Update(string connectionId, string name)
{
UsersByConnectionId.AddOrUpdate(connectionId, name, (key, oldValue) => name );
NotifyStateChanged();
}
public void Remove(string connectionId)
{
UsersByConnectionId.TryRemove(connectionId, out var _);
NotifyStateChanged();
}
private void NotifyStateChanged() => OnChange?.Invoke();
}
将此作为作用域服务添加到startup.cs:
services.AddScoped<CircuitHandler, TrackingCircuitHandler>();
services.addScope();
要在blazor页面上显示活动用户,请执行以下操作:
@page "/UserStatePage"
@inject IUsersStateContainer UsersStateContainer
@implements IDisposable
<h2>Connected users</h2>
<ul>
@foreach (var user in UsersStateContainer.UsersByConnectionId)
{
<li>@user.Key - @user.Value</li>
}
</ul>
@code {
protected override async Task OnInitializedAsync()
{
UsersStateContainer.OnChange += OnMyChangeHandler;
}
public void Dispose()
{
UsersStateContainer.OnChange -= OnMyChangeHandler;
}
private async void OnMyChangeHandler()
{
// invoke on ui thread
await InvokeAsync(StateHasChanged);
}
}
@page”/UserStatePage
@注入IUsersStateContainer UsersStateContainer
@实现IDisposable
连接用户
@foreach(UsersStateContainer.UsersByConnectionId中的var用户)
{
- @user.Key-@user.Value
}
@代码{
受保护的重写异步任务OnInitializedAsync()
{
UsersStateContainer.OnChange+=OnMyChangeHandler;
}
公共空间处置()
{
UsersStateContainer.OnChange-=OnMyChangeHandler;
}
私有异步void OnMyChangeHandler()
{
//在ui线程上调用
等待调用同步(StateHasChanged);
}
}
不幸的是,这无法访问blazor集线器,因此我无法使用它跟踪blazor电路的状态!这对我来说是一个很好的开始。你知道吗
services.AddSingleton<IUsersStateContainer, UsersStateContainer>();
public class TrackingCircuitHandler: CircuitHandler
{
private IUsersStateContainer _usersStateContainer;
private AuthenticationStateProvider _authenticationStateProvider;
public TrackingCircuitHandler(IUsersStateContainer usersStateContainer, AuthenticationStateProvider authenticationStateProvider)
{
_usersStateContainer = usersStateContainer;
_authenticationStateProvider = authenticationStateProvider;
}
public override async Task OnConnectionUpAsync(Circuit circuit,
CancellationToken cancellationToken)
{
var state = await _authenticationStateProvider.GetAuthenticationStateAsync();
_usersStateContainer.Update(circuit.Id, state.User.Identity.Name);
return ;
}
public override Task OnConnectionDownAsync(Circuit circuit,
CancellationToken cancellationToken)
{
_usersStateContainer.Remove(circuit.Id);
return Task.CompletedTask;
}
}
services.AddScoped<CircuitHandler, TrackingCircuitHandler>();
@page "/UserStatePage"
@inject IUsersStateContainer UsersStateContainer
@implements IDisposable
<h2>Connected users</h2>
<ul>
@foreach (var user in UsersStateContainer.UsersByConnectionId)
{
<li>@user.Key - @user.Value</li>
}
</ul>
@code {
protected override async Task OnInitializedAsync()
{
UsersStateContainer.OnChange += OnMyChangeHandler;
}
public void Dispose()
{
UsersStateContainer.OnChange -= OnMyChangeHandler;
}
private async void OnMyChangeHandler()
{
// invoke on ui thread
await InvokeAsync(StateHasChanged);
}
}