我有一个页面组件,我在其中添加了 SignalR
HubConnection
:
@page "/test"
@using System.Diagnostics
@using Microsoft.AspNetCore.SignalR.Client
@rendermode InteractiveServer
@implements IAsyncDisposable
<PageTitle>Test</PageTitle>
<button @onclick="SendMessage">Send Message</button>
@foreach(var m in _messages)
{
<p>@m</p>
}
@code {
private readonly List<string> _messages = [];
private HubConnection? _hubConnection;
protected override async Task OnInitializedAsync()
{
_hubConnection = new HubConnectionBuilder()
.WithUrl("http://localhost:5200/message")
.WithAutomaticReconnect()
.Build();
_hubConnection.On<string>("OnMessage", GetMessage);
await _hubConnection.StartAsync();
await base.OnInitializedAsync();
}
private void GetMessage(string message)
{
_messages.Add(message);
Debug.WriteLine(message);
InvokeAsync(StateHasChanged);
}
public async ValueTask DisposeAsync()
{
if (_hubConnection is not null)
{
_hubConnection.Remove("OnMessage");
await _hubConnection.StopAsync();
await _hubConnection.DisposeAsync();
}
}
private async Task SendMessage()
{
await _hubConnection.SendAsync("SendMessage", "Hello");
}
}
使用此代码,您可以通过单击按钮发送消息,每个连接的客户端都会收到此消息。
在服务器上我只是将消息转发给所有客户端
public async Task SendMessage(string message)
{
await Clients.All.SendAsync("OnMessage", message);
}
在我的
GetMessage
函数中,我将消息写入输出窗口 Debug.WriteLine(message);
。
当我打开页面并单击发送按钮时,消息将在我的页面上显示一次,并在输出窗口中显示一次。 但是,如果我刷新页面并再次单击,该消息将在我的页面上显示一次,但在我的输出窗口中显示两次,就好像第一个事件侦听器仍然已注册,尽管它应该已被处理,不是吗?
如果您跟踪当前电路并将其也放入输出中,您可以看到
GetMessage
函数从两个电路(刷新之前的电路和刷新之后的电路)中调用(我没有将其放入代码中)为了简单起见)。
事件处理程序是否没有正确处理?当集线器连接关闭并处置时,它如何工作?我在这里误解了什么?
问题似乎与 Blazor 组件的生命周期和 SignalR 连接未得到正确管理或处置有关。在开始新电路之前,请确保完全处理掉之前的电路。
试试这个代码:
@page "/test"
@using System.Diagnostics
@using Microsoft.AspNetCore.SignalR.Client
@rendermode InteractiveServer
@implements IAsyncDisposable
<PageTitle>Test</PageTitle>
<button @onclick="SendMessage">Send Message</button>
@foreach(var m in _messages)
{
<p>@m</p>
}
@code {
private readonly List<string> _messages = new();
private HubConnection? _hubConnection;
private CancellationTokenSource _cancellationTokenSource = new();
protected override async Task OnInitializedAsync()
{
_hubConnection = new HubConnectionBuilder()
.WithUrl("http://localhost:5200/message")
.WithAutomaticReconnect()
.Build();
_hubConnection.On<string>("OnMessage", GetMessage);
_cancellationTokenSource = new CancellationTokenSource();
await _hubConnection.StartAsync(_cancellationTokenSource.Token);
await base.OnInitializedAsync();
}
private void GetMessage(string message)
{
_messages.Add(message);
Debug.WriteLine($"Circuit ID: {CircuitHandler.Current?.CircuitId}, Message: {message}");
InvokeAsync(StateHasChanged);
}
public async ValueTask DisposeAsync()
{
_cancellationTokenSource.Cancel();
if (_hubConnection is not null)
{
_hubConnection.Remove("OnMessage");
await _hubConnection.StopAsync();
await _hubConnection.DisposeAsync();
}
}
private async Task SendMessage()
{
if (_hubConnection != null)
{
await _hubConnection.SendAsync("SendMessage", "Hello");
}
}
}