Blazor Server中更新ApplicationState的ErrorMessage时出现重复通知问题
看起来你遇到了设置ErrorMessage时重复触发错误通知的问题,我来帮你分析一下可能的原因和对应的解决办法:
首先排查最可能的两个方向:事件重复订阅、ErrorMessage被重复设置
1. 事件被多次订阅(最常见原因)
你的MainLayout在firstRender时订阅AppState.OnNotification事件,Dispose时取消订阅,但如果MainLayout的实例被创建了多个(比如组件重复渲染、布局被嵌套使用),或者Dispose方法没有被正确调用,就会导致多个实例同时订阅事件,每次触发都会执行多次ShowNotification。
验证方式:
在MainLayout的关键方法里加日志输出,看看订阅和取消订阅的执行情况:
protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender && AppState is not null) { AppState.OnNotification += OnNotificationHandler; Console.WriteLine("MainLayout: 已订阅OnNotification事件"); } await base.OnAfterRenderAsync(firstRender); } public void Dispose() { if (AppState is not null) { AppState.OnNotification -= OnNotificationHandler; Console.WriteLine("MainLayout: 已取消订阅OnNotification事件"); } }
如果控制台输出多次“已订阅”但没有对应的“已取消订阅”,说明有MainLayout实例没有被销毁,导致重复订阅。
解决办法:
- 检查应用的布局结构,确保
MainLayout只作为全局布局仅使用一次,没有在多个页面/组件中重复引用或嵌套。 - 改用
WeakEventManager来管理事件,避免订阅者未正确取消订阅导致的内存泄漏和重复触发。修改ApplicationState的事件实现:
using Microsoft.Extensions.DependencyInjection; using System.ComponentModel; public class ApplicationState { private readonly WeakEventManager _notificationEventManager = new WeakEventManager(); private string _errorMessage = string.Empty; public string ErrorMessage { get => _errorMessage; set { if (!string.Equals(_errorMessage, value) && !string.IsNullOrWhiteSpace(value)) { _errorMessage = value; ShowError(); } else { _errorMessage = value; } } } public event Action? OnNotification { add => _notificationEventManager.AddEventHandler(value); remove => _notificationEventManager.RemoveEventHandler(value); } public void ShowError() { _notificationEventManager.HandleEvent(this, EventArgs.Empty, nameof(OnNotification)); _errorMessage = string.Empty; } }
WeakEventManager会自动处理订阅者被销毁后的事件清理,避免无效订阅导致的重复触发。
2. ErrorMessage被重复设置
如果ErrorMessage的setter被调用了两次,自然会触发两次事件。你可以在setter里加日志验证:
public string ErrorMessage { get => _errorMessage; set { Console.WriteLine($"ErrorMessage设置: 当前值={_errorMessage}, 新值={value}"); if (!string.Equals(_errorMessage, value) && !string.IsNullOrWhiteSpace(value)) { _errorMessage = value; ShowError(); } else { _errorMessage = value; } } }
如果控制台输出两次相同的“新值=Some error”,那你需要检查点击按钮的代码逻辑,看看是不是不小心执行了两次赋值操作,或者有没有Blazor绑定/状态变化导致的重复赋值。
关于渲染模式的疑问
你提到了渲染模式的问题,目前你的Routes和AntContainer都使用了InteractiveServerRenderMode(prerender: false),只要你的App.razor中MainLayout是作为全局布局仅使用一次,这个设置本身不会直接导致重复订阅。但如果AntContainer组件内部也引用了MainLayout,就会导致多个布局实例,这时候就需要调整组件结构,避免重复使用布局。
额外的优化建议
在ShowNotification方法中,你可以先捕获当前的ErrorMessage值,避免因为ShowError()里清空_errorMessage导致后续拿到空值(不过你这个场景里事件触发时ErrorMessage还没被清空,所以影响不大,但养成这个习惯更稳妥):
private async Task ShowNotification() { if (AppState is not null) { var errorMsg = AppState.ErrorMessage; if (!string.IsNullOrWhiteSpace(errorMsg)) { await Notice.Error(new NotificationConfig() { Message = errorMsg }); } } }
你可以先按上面的步骤排查,看看是事件重复订阅还是重复赋值导致的问题,应该就能解决两次通知的情况了。
备注:内容来源于stack exchange,提问作者Victor

