我一直在使用Webassembly在客户端上使用Blazor。但是我认为我现在应该尝试服务器端版本,我有一个简单的想法想尝试一下。
所以我的理解是Blazor服务器端使用SignalR来“推送”更改,以便客户端重新呈现其页面的一部分。
我想尝试的是将数据绑定到单例服务上的属性,如下所示:
@page "/counter"
@inject DataService dataService
<h1>Counter</h1>
<p>Current count: @currentCount ok</p>
<p> @dataService.MyProperty </p>
<p>
@dataService.Id
</p>
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
@code {
int currentCount = 0;
void IncrementCount()
{
currentCount++;
dataService.MyProperty += "--o--|";
}
}
Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddSingleton<WeatherForecastService>();
services.AddSingleton<DataService>();
}
服务:
namespace bl1.Services
{
public class DataService
{
public DataService()
{
this.Id = System.Guid.NewGuid().ToString();
}
public string Id {get;set;}
public string MyProperty { get; set; }
}
}
所以我的问题是这个。为什么,如果我在两个选项卡中打开此页面,当我在另一个选项卡中的一个选项卡中更改属性的值时,我没有立即看到使用SignalR更新的属性 MyProperty 的值?是否有不应该使用的原因,或者我只是做错了?
我认为在服务器端使用Blazor的好处是,您可以轻松使用SignalR可用这一事实,并在服务器上的值更改时获得实时更新。
我确实从另一个选项卡中的单例服务中获得了最新的价值,但是只有在单击那里的按钮之后。
答案 0 :(得分:2)
Blazor Server应用程序基于ASP.NET Core SignalR构建。每 客户端通过一个或多个SignalR连接与服务器通信 称为电路。电路是Blazor对SignalR的抽象 可以容忍网络临时中断的连接。当一个 Blazor客户端看到SignalR连接已断开,它 尝试使用新的SignalR连接重新连接到服务器。
与浏览器连接的每个浏览器屏幕(浏览器标签或iframe) Blazor Server应用程序使用SignalR连接。这是另一个 与典型的服务器呈现的应用相比具有重要的区别。在一个 服务器渲染的应用程序,可在多个浏览器屏幕中打开同一应用程序 通常不会转化为对 服务器。在Blazor服务器应用中,每个浏览器屏幕都需要一个 单独的电路和组件状态的单独实例 由服务器管理。
Blazor考虑关闭浏览器选项卡或导航到外部 URL正常终止。如果是正常终止, 电路和相关资源将立即释放。一种 客户端也可能会非正常断开连接,例如由于 网络中断。 Blazor服务器存储断开的电路以用于 可配置的时间间隔,以允许客户端重新连接。欲了解更多 信息,请参阅重新连接到同一服务器部分。
因此,在您的情况下,不会向另一个选项卡中的客户端通知在另一个ConnectionContext中的另一个电路上所做的更改。
在客户端should fix the problem上调用StateHasChanged()
。
对于您描述的问题,您最好使用普通的SignalR,而不是Blazor服务器端。
答案 1 :(得分:0)
对不起,您在Ashkan之前没有得到更好的答案(我现在才读您的问题)。实际上,您尝试的是Blazor 做得很好的,您是正确的,它是解决此类问题的理想架构。像上面建议的答案一样,直接使用SignalR对于Blazor WASM解决方案是正确的,但这并不能解决您的问题,即有关使用服务器端Blazor的问题。我将在下面提供解决方案。
重要的一点是要了解,blazor组件不会对其绑定属性的更改进行“轮询”。相反,如果说在某个组件上单击了某个按钮,则该组件将自动将其自身重新渲染到内部树中。然后将对先前的渲染执行差异,并且服务器端的blazor将仅在发生更改时将更新发送到客户端(浏览器)。
在您的情况下,您有一个为其模型使用注入的单例的组件。然后,您可以在两个选项卡中打开组件,并且可以预期(根据Blazor的体系结构)打开,只有单击了按钮的选项卡中的组件才被重新渲染。这是因为没有任何指令指示该组件的另一个实例重新渲染(并且Blazor并未“轮询”属性值的更改)。
您需要做的是指示组件的所有实例重新渲染。然后通过在每个组件上调用StateHasChanged来完成此操作。
因此解决方案是将OnInitialized()中的每个组件连接到一个事件,您可以在修改属性值后通过调用Refresh()来调用该事件;然后在Dispose()中取消接线。
您需要将其添加到组件顶部以正确清理:
@implements IDisposable
然后添加此代码:
static event Action OnChange;
void Refresh() => InvokeAsync(StateHasChanged);
override protected void OnInitialized() => OnChange += Refresh;
void IDisposable.Dispose() => OnChange -= Refresh;
您可以将我的OnChange事件移到您的单例中,而不是将其作为静态事件。
如果您调用“ Refresh()”,您现在会注意到所有组件都在任何打开的选项卡上立即重绘。希望对您有所帮助。