为什么我的 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;
}