You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Server中DotNetObjectReference状态异常致IFrame加载问题求助

问题:多Blazor组件中PDF弹窗IFrameSrc为空/旧值问题

问题现象

  • 数据库存储多个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%";
        };
    }
}

解决方法与排查方向

核心问题分析

  1. DotNetObjectReference生命周期混乱:每次调用方法都新建引用,未正确释放或复用,导致JS调用Blazor方法时找不到对象,或组件销毁后引用残留
  2. IFrame ID全局冲突:所有弹窗的IFrame共用同一个pdfIframeID,切换组件后DOM中可能存在多个同ID元素,JS获取元素时取到旧实例
  3. Blob URL未清理:未调用URL.revokeObjectURL()释放资源,旧URL被缓存或占用,导致新URL无法正确绑定
  4. 状态同步延迟:通过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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 17:14:56