为什么我的 Blazor 服务器应用程序的计时器不刷新其组件
Why Is My Blazor Server App's Timer Is Not Refreshing Its Components
我完全重写了这个问题,因为对任何人来说都没有任何意义 - 对于这些问题我深表歉意。
首先,我有一个名为 LocalSystemService 的单例服务,它处理 RESTful Blazor 服务器应用程序与单独服务器上的单独 Web API 系统 运行 之间的通信。我已使用以下调用将该服务添加到我的 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 单例中总是失败。
有什么想法吗?
抱歉,如果没有完整的可重现代码示例,很难遵循执行流程。但是,以下基于 FetchData 页面的代码片段和您的代码(我尝试尽可能符合您的代码)有效。注释的代码是多余的。复制并测试...然后尝试将其应用到您的程序中,并报告问题。
@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 returns 为真时,UI 会更新,而当它 returns 为假时,则不会。
您不能在此处使用 属性 注入 - 这仅适用于组件。
您将需要为 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;
}
我完全重写了这个问题,因为对任何人来说都没有任何意义 - 对于这些问题我深表歉意。
首先,我有一个名为 LocalSystemService 的单例服务,它处理 RESTful Blazor 服务器应用程序与单独服务器上的单独 Web API 系统 运行 之间的通信。我已使用以下调用将该服务添加到我的 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 单例中总是失败。
有什么想法吗?
抱歉,如果没有完整的可重现代码示例,很难遵循执行流程。但是,以下基于 FetchData 页面的代码片段和您的代码(我尝试尽可能符合您的代码)有效。注释的代码是多余的。复制并测试...然后尝试将其应用到您的程序中,并报告问题。
@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 returns 为真时,UI 会更新,而当它 returns 为假时,则不会。
您不能在此处使用 属性 注入 - 这仅适用于组件。
您将需要为 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;
}