c# - 更新页面时出现 Blazor Server System.ObjectDisposedException 问题
问题描述
添加了在 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
}
解决方案
https://docs.microsoft.com/en-us/dotnet/api/system.threading.timer?view=net-5.0
当不再需要计时器时,使用 Dispose 方法释放计时器持有的资源。请注意,调用 Dispose() 方法重载后可能会发生回调,因为计时器将回调排队以供线程池线程执行。您可以使用 Dispose(WaitHandle) 方法重载来等待所有回调完成。
这就是为什么await InvokeAsync(StateHasChanged);
在组件被释放后被调用的原因。
推荐阅读
- java - 如何使用 ExecutorService 重新启动之前在 Swing 应用程序中终止的线程
- java - 使用@Async注解限制线程数并等待最大线程数
- c# - nHibernate 事务日志记录
- gulp - Gulp 3 => Gulp 4 问题
- javascript - 如何自动播放 w3school javascript 报价幻灯片?
- ios - 保存用户默认值时更改应用程序图标不起作用
- c# - 为每个浏览器选项卡添加单例
- android - 无法在 Android 上完成 Scorm
- python - Python:float() 参数必须是字符串或数字,而不是 'pandas._libs.interval.Interval'
- c# - 在 TreeView 和 DataGridView 中查看多个文本文件