更新页面时出现 Blazor Server System.ObjectDisposedException 问题
Posted
技术标签:
【中文标题】更新页面时出现 Blazor Server System.ObjectDisposedException 问题【英文标题】:Blazor Server System.ObjectDisposedException issues when updating pages 【发布时间】:2021-11-26 05:31:00 【问题描述】:在 Blazor Server 中添加了每秒更新数据的过程。
这工作正常,但是当我在浏览器中按刷新页面 (F5) 时,我收到以下错误:
System.ObjectDisposedException: 'Cannot process pending renders after the renderer has been disposed.
ObjectDisposed_ObjectName_Name'
目标代码在这里
@code
private List<Models.Recipe> recipes get; set;
private List<Models.NowOrder> nowOrders get; set;
private List<Models.PlanOrder> planOrders get; set;
System.Threading.Timer _timer;
protected override void OnInitialized()
using (var dbContext = DbFactory.CreateDbContext())
this.recipes = dbContext.Recipes.ToList();
this.planOrders = dbContext.PlanOrders.ToList();
this.nowOrders = dbContext.NowOrders.ToList();
_timer = new System.Threading.Timer(async (_) =>
Time = DateTime.Now.ToString();
databaseValue = await TimerProcessGetValue();
await InvokeAsync(StateHasChanged);
, null, 0, 1000);
public void Dispose()
_timer?.Dispose();
public async Task<int?> TimerProcessGetValue()
int? timerProcessValue;
using (var dbContext = DbFactory.CreateDbContext())
timerProcessValue = (await dbContext.TestTable.SingleAsync(x => x.Id == 1)).TestValue;
return timerProcessValue;
刷新页面时“await InvokeAsync(StateHasChanged);”
如果注释掉以下部分,即使按F5键也不会报错,所以我认为我对异步处理的处理是错误的,但是我很困扰,因为我什至找不到相同的情况如果我搜索我的错误。
您认为这是什么原因? 感谢您的合作。
_timer = new System.Threading.Timer(async (_) =>
Time = DateTime.Now.ToString();
databaseValue = await TimerProcessGetValue();
await InvokeAsync(StateHasChanged);
, null, 0, 1000);
更改为验证码
_timer = new System.Threading.Timer(async (_) =>
Time = DateTime.Now.ToString();
//databaseValue = await TimerProcessGetValue();
await Task.Delay(500); //Added verification code.
await InvokeAsync(StateHasChanged);
, null, 0, 1000);
追加。(2021 年 10 月 10 日)
使用的版本:net core 5.0.7。
使用的浏览器:Edge。
我没有使用任何其他浏览器。
我的环境有限,只能用Edge……
以下是我们已验证的三个代码。
①这是一个使用Dispose(WaitHandle)的例子。
这并没有改善错误。
public void Dispose()
using(System.Threading.WaitHandle waitHandle = new System.Threading.ManualResetEvent(false))
if (_timer.Dispose(waitHandle))
const int millisecondsTimeout = 500;
if (!waitHandle.WaitOne(millisecondsTimeout))
System.Diagnostics.Debug.WriteLine("Dispose Test");
②这是一个使用 System.Timers.Timer 的例子。
使用 F5 键重新加载成功。
private int currentCount = 0;
private Timer timer2 = new(1000);
protected override void OnInitialized()
timer2.Elapsed += (sender, eventArgs) => OnTimerCallback();
timer2.Start();
private void OnTimerCallback()
_ = InvokeAsync(() =>
currentCount++;
Time = DateTime.Now.ToString();
databaseValue = TimerProcessGetValue().Result;
StateHasChanged();
);
public void Dispose() => timer2.Dispose();
③增加错误处理。
即使使用这种方法,我也成功地使用 F5 键重新加载,没有任何错误。
try
_timer = new System.Threading.Timer(async (_) =>
Time = DateTime.Now.ToString();
databaseValue = await TimerProcessGetValue();
await InvokeAsync(StateHasChanged);
, null, 0, 1000);
catch
// empty on purpose
【问题讨论】:
你在标记部分使用@implements IDisposable
吗?
你能通过将databaseValue = await TimerProcessGetValue();
替换为Task.Delay(500);
来创建minimal reproducible example 吗?因为我无法重现此错误。用一个新的普通项目做到这一点。
抱歉信息不足。是的,我正在使用@implements IDisposable。
非常感谢。添加了验证码。如果我没有达到我想做的事,我很抱歉。但是,通过用这个 await Task.Delay (500) ; 替换它,可以正常执行页面重新加载。这是为什么...
这意味着它是由 TimerProcessGetValue() 以某种方式引起的。
【参考方案1】:
https://docs.microsoft.com/en-us/dotnet/api/system.threading.timer?view=net-5.0
当不再需要计时器时,使用 Dispose 方法释放计时器持有的资源。请注意,调用 Dispose() 方法重载后可能会发生回调,因为计时器将回调排队以供线程池线程执行。您可以使用 Dispose(WaitHandle) 方法重载来等待所有回调完成。
这就是为什么在释放组件后调用await InvokeAsync(StateHasChanged);
。
【讨论】:
这看起来像是解决方案,但我认为 DisposeAsync() 更适合这里。 抱歉迟到了。谢谢你的精彩回答。我没有经验,也不是完全可以理解,但是意识到调用Dispose时,它调用了StateHasChanged,它是在组件被销毁之前使用的,这可以吗?当然,我每次重新加载页面时都没有收到错误消息。 (虽然这几乎是一个错误......)【参考方案2】:扩展@Brianparker 的回答:
将@implements IDisposable
替换为@implements IAsyncDisposable
然后将 Dispose() 方法替换为
public ValueTask DisposeAsync()
return _timer?.DisposeAsync() ?? ValueTask.CompletedTask;
更新
但我得到同样的错误
我无法重现您的错误,但我认为 F5 相当残酷。
您可以尝试在 StateHasChanged 调用周围放置一个 try/catch:
try
await InvokeAsync(StateHasChanged);
catch
// empty on purpose
【讨论】:
抱歉迟到了。非常感谢您向我展示更正示例。这是一种我不熟悉的写作方式,所以我会尝试找出它的含义。我会尽快用您收到的内容进行更正。 我尝试了教我的代码,但我得到了同样的错误并且情况没有改善......我会再检查一下...... 嗯,我无法重现这个。您确定它会在禁用 TimerProcessGetValue 行的情况下发生吗?我添加了一个可能的补丁,试试看。 我永远感谢您的帮助。由于情况,我现在没有环境可以尝试,所以明天我会尝试。正如其他人所回答的那样,我今天尝试了 Dispose (WaitHandle)。我指的是这个链接。 (很抱歉不是英文的)C # programming commentary。但总而言之,这也没有改善错误。这只是个人猜测,但我认为它也受到system.threading.timer的影响,所以我明天用System.Timers.Timer试试。我马上告诉你结果。 抱歉这么晚了。我在问题中添加了我尝试过的代码。首先,Dispose (WaitHandle) 没有解决错误。正如我在调试时跟踪代码时所指出的那样,似乎在调用 Dispose 之后调用了 StateHasChanged。在 System.Timers.Timer 示例中,重新加载成功。正如我在最后告诉你的那样,我正在添加错误处理。 (如果描述方法有误,我很抱歉) 再次,用 F5 键重新加载和常规处理工作正常。有几个我试过了,但我不知道是什么原因造成的......以上是关于更新页面时出现 Blazor Server System.ObjectDisposedException 问题的主要内容,如果未能解决你的问题,请参考以下文章
如何更改刷新 Blazor PWA 应用程序时出现的“正在授权...”消息?
尝试获取资源时出现 Blazor WASM NetworkError。 “CORS 缺少允许来源”
当用户在 Blazor Webassembly 身份验证和授权中具有多个角色时出现问题?
Cors Policy 问题 Blazor WASM、Web API 和 Identity Server 4 和 IIS