【发布时间】:2020-05-13 20:33:33
【问题描述】:
我正在使用 Blazor 构建一个 Web 应用程序,通过 SignalR 同步团队(我的域模型)。 (Blazor RC1 WebApp,core31 WebApi)
我的客户有一个调用,触发服务器将当前团队列表发送给客户。这个方法只有两行:
var currentTeams = await teamService.GetAllTeams().ConfigureAwait(false);
await SendTeamsToClient(currentTeams).ConfigureAwait(false);
这些工作 - SendTeamsToClient 获取列表并在客户端上调用另一个 SignalR 方法 - 成功。对象被发送到客户端并在那里正确处理。
唯一的问题是:客户端调用永远不会返回。客户端方法只有一行:
await connection.InvokeAsync(nameof(ServerHub.SubscribeToAndReceiveTeamsAsync));
而那个人永远不会回来。知道为什么吗?
我有一个解决方法,我将该方法称为“即发即弃”,但这确实不是一个长期可行的解决方案。
我还尝试让服务器将SendTeamsToClient 称为即发即弃,以防万一发生干扰。尽管如此,团队还是会被发送,但最初调用的 hub 方法会在服务器上返回,但不会在客户端上返回。
更新(MCVE 有效,我的项目无效):
所以我试图重现这个问题,但我失败了。此 MCVE 完美运行:
MCVE 前端,数字表示成功进入控制台:
public async Task Ping()
{
Console.WriteLine("Sending ping..."); // 1
await c.InvokeAsync("Ping");
Console.WriteLine("ping returned."); // 4
}
public Task Pong(string p)
{
Console.WriteLine("Received pong: "+p); // 2+3
Pongs.Add(p);
StateHasChanged();
return Task.CompletedTask;
}
MCVE 后端:
public async Task Ping()
{
var pongs = new string[]
{
"Pong1",
"Pong2"
};
await SendPongs(pongs).ConfigureAwait(false);
}
public async Task SendPongs(IEnumerable<string> pongs)
{
foreach (var pong in pongs)
{
await Clients.Caller.Pong(pong).ConfigureAwait(false);
}
}
实际项目前端:
public async Task SubscribeToAndReceiveTeamsAsync()
{
Console.WriteLine("Frontend Hub triggering teams"); // This arrives
await connection.InvokeAsync(nameof(ServerHub.SubscribeToAndReceiveTeamsAsync));
Console.WriteLine("Frontend hub done triggering teams."); // This doesn't
}
public Task ReceiveTeam(Team team) // Works completely (teams arrive in view)
{
Console.WriteLine("Frontend hub receiving team "+team.Name);
Console.WriteLine($"Invoking event with {OnNewTeam?.GetInvocationList().Length} listener(s).");
OnNewTeam?.Invoke(team);
return Task.CompletedTask;
}
实际项目后端:
public async Task SubscribeToAndReceiveTeamsAsync()
{
var currentTeams = await teamService.GetAllTeams().ConfigureAwait(false);
Log(currentTeams.Count()); // This arrives with "2"
await SendTeamsToClient(currentTeams).ConfigureAwait(false);
Log("Server done"); // This arrives, therefore this method returns?
}
private async Task SendTeamsToClient(IEnumerable<Team> teams)
{
foreach (var team in teams)
{
await Clients.Caller.ReceiveTeam(team).ConfigureAwait(false);
}
}
不知道为什么一个可以工作而另一个不能。
快速更新:不管我是使用 IIS 还是自托管。都不行。
更新 2
MCVE 和实际项目都使用 DI:
前端:
builder.Services.AddSingleton<HubConnection>(sp => new HubConnectionBuilder().WithUrl($"{builder.HostEnvironment.BaseAddress}signalr").WithAutomaticReconnect().Build());
后端:
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages(); // <- Not in actual project
endpoints.MapControllers();
endpoints.MapFallbackToFile("index.html"); // <- Not in actual project
endpoints.MapHub<MyHub>("/signalr");
});
我确实添加了一个新方法,但它也有同样的问题:
前端事件调用await connection.InvokeAsync(nameof(ServerHub.SendChatMessage), message);
后端将其发送给其他客户端:
public Task SendChatMessage(string message)
{
Clients.Others.ReceiveMessage(message).ConfigureAwait(false);
return Task.CompletedTask;
} // Debugger says this returns successfully
其他人成功收到消息。
调用者前端调用永远不会返回。
【问题讨论】:
-
您是否在客户端进一步阻塞调用堆栈?
-
没有任何阻塞,除了这个调用。在这个调用之前和之后有一个
Console.WriteLine,当我awaitInvokeAsync时没有调用之后的那个。再往上基本就是调用这一个和多个Console.WriteLines,从OnInitializedAsync开始。 -
我的意思是像
.Wait()或.Resultanywhere 那样阻塞调用堆栈。 -
啊不,我在任何地方都没有阻塞呼叫。
await从上到下。 -
你能发布一个最小的复制吗?