Blazor Server中DotNetObjectReference状态异常致IFrame加载问题求助
问题现象
- 数据库存储多个Blob文件,点击多个Razor组件的文件按钮后,通过弹窗内的IFrame展示文件
- 首次打开弹窗的组件内操作正常,切换组件再返回时,IFrameSrc始终为null或空值;切换到其他PDF组件也会出现该问题
- 重载页面或在新标签页打开可临时解决问题
- 若在
HandleWantedFile方法末尾处置DotNetObjectReference,后续打开PDF会抛出异常,且始终显示该组件首次打开的文件,怀疑与未撤销URL有关,但无法理解为何返回旧的IFrameSrc
异常信息
blazor.server.js:1 Uncaught (in promise) Error:
System.ArgumentException: There is no tracked object with id '5'.
Perhaps the DotNetObjectReference instance was already disposed.
(Parameter 'dotNetObjectId') at Microsoft.JSInterop.…64
dotNetObjectId) at
Microsoft.JSInterop.Infrastructure.DotNetDispatcher.BeginInvokeDotNet(JSRuntime
jsRuntime,…fo, String argsJson)
at y.endInvokeDotNetFromJS (blazor.server.js:1:3502)
at Xt._invokeClientMethod (blazor.server.js:1:61001)
at Xt._processIncomingData (blazor.server.js:1:58476)
at Xt.connection.onreceive (blazor.server.js:1:52117)
at s.onmessage (blazor.server.js:1:80262)
相关代码
PdfPopUp.razor
@* PdfPopUp.razor *@ @namespace Project.Pages <MudDialog Style="width: 100%; height: 100%;"> <DialogContent> @if (!string.IsNullOrEmpty(IframeSrc)) { <iframe id="pdfIframe" src="@IframeSrc" style="width: 100%; height: 100vh; border: none;" frameborder="0"></iframe> } else { <p>Error: IFrameSrc is not set.</p> } </DialogContent> </MudDialog> @code { [Parameter] public string IframeSrc { get; set; } = string.Empty; }
FirstComponent.razor(调用弹窗的组件示例)
@* FirstComponent.razor *@ @page "/FirstComponent" @attribute [Authorize(Roles = "user")] @namespace Project.Pages @inject IDialogService dialogService @inject IJSRuntime javaScript <button class="link-button" @onclick="@(x => HandleWantedFile())">@FileName@FileType</button> @code { [Parameter] public string IFrameSrc { get; set; } = string.Empty; private async Task HandleWantedFile() { byte[] file; file = await whaterver();//db call var dotNetReference = DotNetObjectReference.Create(this); await javaScript.InvokeVoidAsync("initializeBlobUrlCreation", dotNetReference); await javaScript.InvokeVoidAsync("createAndSetBlobUrl", "pdfIframe", file, "application/pdf", dotNetReference); var options = new DialogOptions { FullWidth = true, MaxWidth = MaxWidth.Large, CloseButton = true}; dialogService.Show<PdfPopUp>(currentBlob.FileName, new DialogParameters { ["IFrameSrc"] = this.IFrameSrc }, options); } [JSInvokable] public async Task SetIframeSource(string url) { IFrameSrc = url; await InvokeAsync(StateHasChanged); } }
wwwroot/JS/site.js
//wwwroot/JS/site.js function initializeBlobUrlCreation(dotNetRef) { window.createAndSetBlobUrl = function(iframeId, byteArray, mimeType) { var blob = new Blob([new Uint8Array(byteArray)], { type: mimeType }); var url = URL.createObjectURL(blob); dotNetRef.invokeMethodAsync('SetIframeSource', url); var iframeElement = document.getElementById(iframeId); if (iframeElement) { iframeElement.src = url; } }; } function createAndSetBlobUrl(iframeId, byteArray, mimeType) { var blob = new Blob([new Uint8Array(byteArray)], { type: mimeType }); var url = URL.createObjectURL(blob); DotNet.invokeMethodAsync('Project', 'SetIframeSource', dotNetReference, url); var iframeElement = document.getElementById(iframeId); if (iframeElement) { iframeElement.src = url; scalePDF(iframeId); } } function scalePDF(iframeId) { var iframe = document.getElementById(iframeId); if (iframe) { iframe.onload = function () { var pdfContent = iframe.contentWindow.document.getElementsByTagName('body')[0]; pdfContent.style.zoom = "100%"; }; } }
解决方法与排查方向
核心问题分析
- DotNetObjectReference生命周期混乱:每次调用方法都新建引用,未正确释放或复用,导致JS调用Blazor方法时找不到对象,或组件销毁后引用残留
- IFrame ID全局冲突:所有弹窗的IFrame共用同一个
pdfIframeID,切换组件后DOM中可能存在多个同ID元素,JS获取元素时取到旧实例 - Blob URL未清理:未调用
URL.revokeObjectURL()释放资源,旧URL被缓存或占用,导致新URL无法正确绑定 - 状态同步延迟:通过JS回调设置组件
IFrameSrc后,弹窗可能已打开,参数未同步最新值
具体修复步骤
1. 规范DotNetObjectReference生命周期
在组件中声明全局引用,在销毁时释放:
private DotNetObjectReference<FirstComponent>? _dotNetRef; protected override void OnInitialized() { _dotNetRef = DotNetObjectReference.Create(this); } public void Dispose() { _dotNetRef?.Dispose(); }
调用JS时使用该全局引用,避免每次创建新实例。
2. 避免IFrame ID冲突
修改PdfPopUp.razor生成唯一ID:
@code { [Parameter] public string IframeSrc { get; set; } = string.Empty; private readonly string _iframeId = $"pdfIframe_{Guid.NewGuid():N}"; }
弹窗中使用该唯一ID:
<iframe id="@_iframeId" src="@IframeSrc" style="width: 100%; height: 100vh; border: none;" frameborder="0"></iframe>
调用弹窗时传递ID参数(需在PdfPopUp中新增对应Parameter)。
3. 直接生成Blob URL并传递给弹窗
跳过JS回调,直接在C#中生成URL并传递:
private async Task HandleWantedFile() { byte[] file = await whaterver(); // db call // 调用JS生成Blob URL var blobUrl = await javaScript.InvokeAsync<string>("createBlobUrl", file, "application/pdf"); // 打开弹窗并传递URL var dialogRef = dialogService.Show<PdfPopUp>(currentBlob.FileName, new DialogParameters { ["IFrameSrc"] = blobUrl }, new DialogOptions { FullWidth = true, MaxWidth = MaxWidth.Large, CloseButton = true}); // 弹窗关闭时清理URL dialogRef.Closed += async (args) => { await javaScript.InvokeVoidAsync("revokeBlobUrl", blobUrl); }; }
简化JS代码:
function createBlobUrl(byteArray, mimeType) { var blob = new Blob([new Uint8Array(byteArray)], { type: mimeType }); return URL.createObjectURL(blob); } function revokeBlobUrl(url) { URL.revokeObjectURL(url); }
4. 清理冗余JS代码
删除原initializeBlobUrlCreation和冗余的createAndSetBlobUrl函数,避免逻辑冲突。
额外排查点
- 检查组件是否被路由缓存,若缓存导致状态未重置,需在
OnParametersSet中重置IFrameSrc - 用浏览器开发者工具查看DOM中的IFrame元素,确认ID唯一且src属性正确
- 验证Blob URL在弹窗关闭后是否被正确清理,避免内存泄漏
内容的提问来源于stack exchange,提问作者DaGa

