c# - Blazor 服务器计时器停止页面更新的问题

标签 c# .net-core blazor blazor-server-side

我编写了一个程序,可以使用 Blazor 服务器应用定期更新页面。

执行该程序时,每秒都会获取当前时间和数据库表中的值并显示在页面上,但该计时器中的处理会在一定时间后停止。

虽然测量不准确,但计时器中的处理会在大约 10 到 30 分钟后停止。

但是我看浏览器的debug工具并没有报错,在Visual Studio上也没有报错。

我搜索了这样的示例,但找不到它,也无法确定是什么原因造成的。

我希望此计时器中的处理能够正常工作,直到我明确将其指定为停止为止。

这可能是什么原因造成的? 我遇到麻烦了,因为我不知道原因。 请给我一些建议。 谢谢。

    private string Time { get; set; }

    protected override void OnInitialized()
    {
        var timer = new System.Threading.Timer((_) =>
        {
            Time = DateTime.Now.ToString();
            InvokeAsync(() =>
            {   
                // Get the value from the database
                databaseValue = TimerProcessGetValue();                
                StateHasChanged();
            });
        }, null, 0, 1000);
        base.OnInitialized();
    }

定时器中调用的进程

public async Task<int?> TimerProcessGetValue()
{
    int? timerProcessValue;
    using(DbContext = DbFactory.CreateDbContext())
    {
        timerProcessValue = dbContext.TestTable.Single(x => x.Id == 1).TestValue;
    }
    return timerProcessValue;
}

后记

目标组件的全文。

PageRefleshTest.cs

@page "/refresh-ui-manually"
@using System.Threading;
@using TestBlazorServer.Models;
@using Microsoft.EntityFrameworkCore;
@inject IDbContextFactory<SQLbeginnerContext> DbFactory

<h1>@Time</h1> 
<h1>TestValue:@databaseValue</h1>

private string Time { get; set; }
private int? databaseValue { get; set; }

protected override void OnInitialized()
{
    var timer = new System.Threading.Timer((_) =>
    {
        Time = DateTime.Now.ToString();
        InvokeAsync(() =>
        {
            // Get the value from the database
            databaseValue = TimerProcessGetValue().Result;
            StateHasChanged();
        });
    }, null, 0, 1000);
    base.OnInitialized();
}


public async Task<int?> TimerProcessGetValue()
{
    int? timerProcessValue;
    using (var dbContext = DbFactory.CreateDbContext())
    {
        timerProcessValue = dbContext.TestTable.Single(x => x.Id == 1).TestValue;
    }
    return timerProcessValue;
}

使用System.Timers.Timer实现

@page "/Refresh2"
@inject IDbContextFactory<SQLbeginnerContext> DbFactory
@using TestBlazorServer.Models
@using Microsoft.EntityFrameworkCore
@using System.Timers
@implements IDisposable

<h3>PageRefresh2</h3>
<h1>@Time</h1>
<h1>@currentCount</h1>
<h1>TestValue:@databaseValue/h1>

@code {

private int currentCount = 0;
private Timer timer2 = new(1000);
private string Time { get; set; }
private int? databaseValue { get; set; }

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();


public async Task<int?> TimerProcessGetValue()
{
    int? timerProcessValue;
    await using (var dbContext = DbFactory.CreateDbContext())
    {
        timerProcessValue = dbContext.TestTable.Single(x => x.Id == 1).TestValue;
    }
    return timerProcessValue;
}

}

enter image description here

最佳答案

这当然可能是由于 SignalR 连接失败造成的。不过,这应该会重新连接。

这里的主要嫌疑人是.Result。您应该始终避免异步代码中的 .Wait().Result。 Aslo,您的数据库方法并不是真正的异步,这也可能发挥作用。

但是“半小时”也可能来自垃圾收集器抛出的 Timer 对象。参见this page上的紫色注释。当您有一个计时器时,您应该将其锚定在一个字段中并对其进行 Dispose()。

下一步是使事件处理程序成为async void,这是极少数需要的情况之一。然后只调用 StatehasChanged 调用。

@implements IDisposable

...
@code {
  System.Threading.Timer _timer;  // use a private field
  protected override void OnInitialized()
  {
    _timer = new System.Threading.Timer(async (_) =>
    {
        Time = DateTime.Now.ToString();
        databaseValue = await TimerProcessGetValue();
        await InvokeAsync(StateHasChanged);    
    }, null, 0, 1000);
    // base.OnInitialized();  -- not needed
  }

  public void Dispose() 
  {
    _timer?.Dispose();
  }
}

并使 Db 操作实际上异步:

public async Task<int?> TimerProcessGetValue()
{
    int? timerProcessValue;
    using (var dbContext = DbFactory.CreateDbContext())
    {
        timerProcessValue = 
          (await dbContext.TestTable.SingleAsync(x => x.Id == 1))
          .TestValue;
    }
    return timerProcessValue;
}

只有您可以现场测试此功能,请告诉我们它是否有效。

关于c# - Blazor 服务器计时器停止页面更新的问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/69418287/

相关文章:

C# Blazor 和数据绑定(bind)到 DateTime?

.net-core - 自定义 `AuthenticationStateProvider` 认证失败

asp.net-core - 防止 Kestrel 在端口使用时崩溃

c# - 无法将图像添加到 C# WPF .Net Core WPF 应用程序

blazor - 如何在服务器端 Blazor 中自定义 ASP.NET Core 标识?

c# - 将文件添加到 C# Web 应用程序的 bin 目录可能会产生什么后果?

javascript - 在客户端和服务器之间传输日期时间的最佳方式 (c#/javascript)

c# - Microsoft图表控件无法设置背景透明

c# - 从基类继承 href 和 id 的 ASP.NET WebApi 响应模型中的排序属性

bootstrap-4 - 在 blazor 客户端中显示模态对话框