Blazor Server应用中Scoped服务更新共享变量后Razor页面未同步显示的问题排查
嘿,我来帮你搞定这个问题!你遇到的是Blazor Server中非常典型的「状态更新后组件未同步」的问题,主要有几个核心的错误需要修正,咱们一步步来:
1. TagService缺少属性变更通知机制
Blazor组件不会自动监听普通POCO类的属性变化——只有当组件自身状态变更,或者收到明确的属性变更通知时,才会重新渲染页面。你的TagService只是简单的自动属性,修改值后组件根本不知道要刷新。
修正方案:让TagService实现INotifyPropertyChanged接口
这个接口是.NET中标准的属性变更通知机制,当属性值变化时触发事件,组件订阅后就能自动刷新。
修改后的TagService代码:
using System.ComponentModel; using System.Runtime.CompilerServices; namespace WebApplication7.Services { public class TagService : INotifyPropertyChanged { private int _tag1 = 0; private int _tag2 = 0; private int _tag3 = 0; public int Tag1 { get => _tag1; set { if (_tag1 != value) { _tag1 = value; OnPropertyChanged(); } } } public int Tag2 { get => _tag2; set { if (_tag2 != value) { _tag2 = value; OnPropertyChanged(); } } } public int Tag3 { get => _tag3; set { if (_tag3 != value) { _tag3 = value; OnPropertyChanged(); } } } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } } }
另外注意:你原来的代码里重复定义了Tag2属性(应该是Tag3),这个笔误会导致逻辑错误,已经在上面修正了。
2. SetTags服务的注入方式错误
[Inject]属性只在Blazor组件(Razor页面/组件)中生效,服务类之间的依赖注入必须用构造函数注入——你原来的SetTags里的TagService其实是null,调用Set_Values()的时候根本没修改到真实的TagService实例!
修正方案:用构造函数注入TagService
修改后的SetTags代码:
namespace WebApplication7.Services { public class SetTags { private readonly TagService _tagService; // 通过构造函数注入TagService,这是服务间依赖注入的正确方式 public SetTags(TagService tagService) { _tagService = tagService; } public void Set_Values() { _tagService.Tag1 = 32; _tagService.Tag2 = 24; _tagService.Tag3 = 20; // 修正原来的重复Tag2问题 // 其他标签的设置逻辑 } } }
3. Razor页面需要订阅属性变更事件
现在TagService能发出变更通知了,还需要让Razor页面订阅这个事件,这样属性变化时组件才会重新渲染。
修改后的Razor页面代码:
@page "/razor_page1" @using WebApplication7.Services @inject TagService TagService @inject SetTags SetTags @implements IDisposable <!-- 实现IDisposable用于取消订阅事件,避免内存泄漏 --> <td> <button class="button_1" @onclick="func_button">更新标签</button> </td> <div> <p>Tag1: @TagService.Tag1</p> <p>Tag2: @TagService.Tag2</p> <p>Tag3: @TagService.Tag3</p> </div> @code { protected override void OnInitialized() { // 订阅TagService的属性变更事件,触发组件重新渲染 TagService.PropertyChanged += (sender, e) => StateHasChanged(); } public void func_button() { SetTags.Set_Values(); // 如果没有订阅事件,这里可以手动调用StateHasChanged(),但推荐用事件订阅的方式 // StateHasChanged(); } public void Dispose() { // 页面销毁时取消订阅,避免内存泄漏 TagService.PropertyChanged -= (sender, e) => StateHasChanged(); } }
最后验证你的Startup配置
你的Startup.cs里的服务注册是正确的:
public void ConfigureServices(IServiceCollection services) { services.AddScoped<ConnectService>(); services.AddScoped<TagService>(); services.AddScoped<SetTags>(); }
Scoped服务在Blazor Server中是每个用户会话一个实例,所以多个页面会共享同一个TagService实例,修改后所有页面都会同步显示最新值。
备注:内容来源于stack exchange,提问作者Mdarende

