Blazor Server调用Action委托抛出NullReferenceException如何解决?
根因定位
抛出NullReferenceException的核心原因是*DownloadFileNameAction*为静态Action变量,调用Invoke时该变量尚未完成赋值,或已被其他用户请求/组件生命周期操作重置为null:
- 静态变量在Blazor Server应用中是全局共享的,所有用户的组件实例共用同一个静态变量值,多用户同时访问页面时后初始化的组件会覆盖之前的赋值,极端情况下会被重置为null
- 若JS端调用的时机早于组件
OnInitializedAsync方法执行完成,变量还没被赋值就被调用 - 组件销毁后未清理静态变量,重新加载组件前收到JS调用也会触发空引用
解决方案
1. 临时修复:先加空值判断避免直接抛异常
修改TransferFileName方法,调用前先做非空校验:
[JSInvokable] public static void TransferFileName(string fileName) { DownloadFileNameAction?.Invoke(fileName); }
这一步只能避免异常抛出,无法解决多用户回调冲突、回调触发时机不对的核心问题。
2. 永久修复:改用实例方法绑定的JS互操作方案
Blazor官方推荐使用实例绑定的方式处理JS调用C#的场景,避免静态变量的全局共享问题,修改代码如下:
C#侧修改
// 移除静态修饰符,改为实例变量 public Action<string> DownloadFileNameAction; // 移除静态修饰符,改为实例方法 [JSInvokable] public void TransferFileName(string fileName) { DownloadFileNameAction?.Invoke(fileName); } protected override async Task OnInitializedAsync() { DownloadFileNameAction = TransferFileToUserForDownload; // 创建当前组件的实例引用,传递给JS端绑定 var dotNetRef = DotNetObjectReference.Create(this); await JSRuntime.InvokeVoidAsync("registerComponentRef", dotNetRef); } // 实现IDisposable接口,组件销毁时清理引用避免内存泄漏 public void Dispose() { DownloadFileNameAction = null; } // 把async void改为async Task,避免方法内异常无法被捕获 public async Task TransferFileToUserForDownload(string fileName) { var getFileName = fileName; // 此处省略其他代码 }
JS侧修改
// 存储C#组件实例引用 let componentRef = null; // 供C#侧调用的注册方法 function registerComponentRef(ref) { componentRef = ref; } // 原来调用C#静态方法的位置,替换为调用实例方法 function triggerFileTransfer(fileName) { if(componentRef) { componentRef.invokeMethodAsync('TransferFileName', fileName); } }
修改后每个用户的组件实例独立绑定回调,不会出现多用户冲突,也不会因为初始化时机问题抛出空引用异常。
内容的提问来源于stack exchange,提问作者sudip chand
相关产品推荐
相关产品推荐

