与单例服务Blazor Serverside的两种方式数据绑定

时间:2019-11-12 21:26:02

标签: core blazor-server-side

我一直在使用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可用这一事实,并在服务器上的值更改时获得实时更新。

我确实从另一个选项卡中的单例服务中获得了最新的价值,但是只有在单击那里的按钮之后。

2 个答案:

答案 0 :(得分:2)

See the documentation here

  

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()”,您现在会注意到所有组件都在任何打开的选项卡上立即重绘。希望对您有所帮助。