为啥我的 Blazor 服务器应用程序的计时器没有刷新其组件
Posted
技术标签:
【中文标题】为啥我的 Blazor 服务器应用程序的计时器没有刷新其组件【英文标题】:Why Is My Blazor Server App's Timer Is Not Refreshing Its Components为什么我的 Blazor 服务器应用程序的计时器没有刷新其组件 【发布时间】:2021-12-29 23:35:52 【问题描述】:我完全重写了这个问题,因为对任何人都没有任何意义 - 我为这些问题道歉。
首先,我有一个名为 LocalSystemService 的 Singleton 服务,它处理 Blazor 服务器应用程序与在单独服务器上运行的单独 Web API 系统之间的 RESTful 通信。我已使用以下调用将该服务添加到我的 Blazor 应用程序中:
services.AddScoped<ILocalSystemService, LocalSystemService>();
我现在已经从一个简单的计时器转移到一个单独的服务,以响应我读过的其他文章。此服务称为 CheckLDC,并使用以下内容进行注册:
services.AddSingleton<CheckLDC>();
该服务的构造如下:
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Forms;
using Microsoft.Extensions.Caching.Memory;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Timers;
using System.Web.UI;
using System.Web.UI.htmlControls;
using Telerik.Blazor;
using Telerik.Blazor.Components;
using Frontend.Data;
using Frontend.Services;
namespace Frontend.Services
public class LDCExecutedEventArgs : EventArgs
public class CheckLDC : IDisposable
public event EventHandler<LDCExecutedEventArgs> JobExecuted;
void OnJobExecuted()
JobExecuted?.Invoke(this, new LDCExecutedEventArgs());
#region Globals
static ReaderWriterLock locker = new ReaderWriterLock();
private System.Timers.Timer checkRemoteData;
private bool _Running;
[Inject]
public ILocalSystemService LocalSystemService get; set;
[Inject]
public ILogger<CheckLDC> _logger get; set;
[Inject]
public IMemoryCache _cache get; set;
private const string LocationCacheName = "LocalSystem";
#endregion
public void StartExecuting()
if (!_Running)
// Initiate a Timer
checkRemoteData = new System.Timers.Timer(1000);
checkRemoteData.Elapsed += HandleTimer;
checkRemoteData.AutoReset = true;
checkRemoteData.Enabled = true;
_Running = true;
private async void HandleTimer(object source, ElapsedEventArgs e)
**This call results in a NULL for the LocalSystemService!!!**
**if (LocalSystemService.IsThereAnUpdate().Result)**
await Task.Run(() =>
try
locker.AcquireWriterLock(int.MaxValue);
if (!_cache.TryGetValue(LocationCacheName, out transferSystem))
//We need to grab everything:
JsonSerializer serializer = new JsonSerializer();
#region Location
try
_cache.Set(LocationCacheName, LocalSystemService.GetLocalSystem().Result);
catch (Exception locWriteX)
_logger.LogError("Failed to restore location locally with the error: " + locWriteX.ToString());
#endregion
finally
locker.ReleaseWriterLock();
);
// Notify any subscribers to the event
OnJobExecuted();
public void Dispose()
if (_Running)
checkRemoteData?.Dispose();
然后我把这段代码放在我的主要组件后面
[Inject]
public ILocalSystemService LocalSystemService get; set;
[Inject]
public CheckLDC CheckTheBackend get; set;
请注意,我使用内存缓存来跨请求存储数据。注入到位后,我的 OnInit 方法如下所示:
protected override async Task OnInitializedAsync()
await Task.Run(() =>
//This call uses the LocalSystemService to grab and store the main data class into the cache
CurrentSystem = CreateCaches().Result;
);
//These are my event subscriptions
CheckTheBackend.JobExecuted += HandleJobExecuted;
CheckTheBackend.StartExecuting();
最后,在 JobExecute 上被调用的方法是:
public async void HandleJobExecuted(object sender, LDCExecutedEventArgs e)
await InvokeAsync(StateHasChanged);
现在我在尝试从 CheckLDC 的 HandleTimer 事件调用中调用 LocalSystemService 时遇到 NULL 异常 - 我粗体键入了继续失败的调用。我已经为 LocalSystemService 尝试了 AddTransient 和 AddScope,但没有任何效果。在 Blazor 应用程序中,我可以毫无问题地调用 LocalSystemService - 但在 CheckLDC 单例中总是失败。
有什么想法吗?
【问题讨论】:
您必须提供一些代码。使用 Timer 和 StateHasChanged 制作模型。用 Task.Delay 或其他东西替换 API。见minimal reproducible example 没有任何代码可以使用,请参阅我最近在刷新 FetchData 中的 WeatherForecast 列表时回答的这个问题 - ***.com/questions/69967013/… 抱歉没有包含代码!我已经逐步完成并观察了每个 StateHasChanged 方法在我希望它们被调用的时候被准确地调用。无论我等待多久,它都不会改变 UI。 我为 CheckLDC 服务添加了一个接口,将两个服务的服务调用更改为 'services.AddScoped您不能在此处使用属性注入 - 仅适用于组件。
CheckLDC 需要使用构造函数注入
private readonly ILocalSystemService LocalSystemService;
private readonly ILogger<CheckLDC> _logger;
private readonly IMemoryCache _cache;
public CheckLDC(ILocalSystemService localSystemService,
ILogger<CheckLDC> logger,
IMemoryCache cache)
LocalSystemService = localSystemService;
_logger = logger;
_cache = cache;
【讨论】:
【参考方案2】:抱歉,如果没有完整的可重现代码示例,就很难遵循执行流程。但是,基于 FetchData 页面的以下代码 sn-p 和您的代码(我试图对您的代码尽可能真实)有效。注释代码是多余的。复制并测试...然后尝试将其应用到您的程序中,并报告问题。
@page "/fetchdata"
@using WebApplication1.Data
@using System.Timers;
@inject WeatherForecastService ForecastService
@implements IDisposable
<h1>Weather forecast</h1>
<p>This component demonstrates fetching data from a service.</p>
@if (forecasts == null)
<p><em>Loading...</em></p>
else
<table class="table">
<thead>
<tr>
<th>Date</th>
<th>Temp. (C)</th>
<th>Temp. (F)</th>
<th>Summary</th>
</tr>
</thead>
<tbody>
@foreach (var forecast in forecasts)
<tr>
<td>@forecast.Date.ToShortDateString()</td>
<td>@forecast.TemperatureC</td>
<td>@forecast.TemperatureF</td>
<td>@forecast.Summary</td>
</tr>
</tbody>
</table>
@code
private WeatherForecast[] forecasts;
private static System.Timers.Timer checkRemoteData;
protected override async Task OnInitializedAsync()
// forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
//await Task.Run(() =>
//
forecasts = await ForecastService.GetForecastAsync(DateTime.Now); //.Result;
// What is ShowModal? Are you setting it to true somewhere ?
// Perhaps this is why you do not see the changes, if you design
// to show the changes in a window modal
// ShowModal = false;
//);
//This works and calls the main method every second
checkRemoteData = new System.Timers.Timer(4000);
checkRemoteData.Elapsed += OnTimedEvent;
checkRemoteData.AutoReset = true;
checkRemoteData.Enabled = true;
private async void OnTimedEvent(Object source, ElapsedEventArgs e)
//if (LocalSystemService.IsThereAnUpdate().Result)
//
// await InvokeAsync(StateHasChanged);
// I have used the same call as in the OnInit - niether one works!
forecasts = await ForecastService.GetForecastAsync(DateTime.Now);
await InvokeAsync(() => StateHasChanged()); //.ConfigureAwait(false);
// What is this for ? Are you trying to emulate
// delay? Just increase the interval (to 4000...)
// await Task.Delay(500);
// Not needed
// await InvokeAsync(StateHasChanged);
//
public void Dispose()
checkRemoteData.Elapsed -= OnTimedEvent;
更新:
我已将以下方法添加到 WeatherForecastService 以模拟 LocalSystemService.IsThereAnUpdate().Result
public Task<bool> IsThereAnUpdate()
return Task.FromResult( true);
还添加了这个:
if (ForecastService.IsThereAnUpdate().Result)
现在当 IsThereAnUpdate 返回 true 时,UI 会更新,而当它返回 false 时则不会。
【讨论】:
对不起,多余的代码 - ShowModal 处理模式窗口,可以忽略。在您的情况下,ForecastServer 类似于我的 LocalSystemService,您在 OnTimedEvent 中看到的其余代码只是我添加越来越多的尝试来刷新 UI。我将编辑问题以澄清问题,因为您的解决方案应该有效,但它没有。 @Ken,我的代码示例确实有效。这是肯定的。请参阅我的答案中的更新部分。以上是关于为啥我的 Blazor 服务器应用程序的计时器没有刷新其组件的主要内容,如果未能解决你的问题,请参考以下文章
Blazor DI 在 .razor 和商务类中的工作方式不同。为啥?
在android中,为啥我的服务在按下电源按钮后没有进入睡眠状态