【问题标题】:Periodic background task in Blazor wasmBlazor wasm 中的定期后台任务
【发布时间】:2021-08-09 15:07:55
【问题描述】:

在 Blazor wasm 中,我想定期执行作业(代码), 即使用户正在浏览页面(例如每 x 分钟)。

这可能吗?什么是实用的方法?

【问题讨论】:

标签: blazor blazor-webassembly


【解决方案1】:

创建一个服务来管理计时器

public class JobExecutedEventArgs : EventArgs {}


public class PeriodicExecutor : IDisposable
{
    public event EventHandler<JobExecutedEventArgs> JobExecuted;
    void OnJobExecuted()
    {
        JobExecuted?.Invoke(this, new JobExecutedEventArgs());
    }

    Timer _Timer;
    bool _Running;

    public void StartExecuting()
    {
        if (!_Running)
        {
            // Initiate a Timer
            _Timer= new Timer();
            _Timer.Interval = 300000;  // every 5 mins
            _Timer.Elapsed += HandleTimer;
            _Timer.AutoReset = true;
            _Timer.Enabled = true;

            _Running = true;
        }
    }
    void HandleTimer(object source, ElapsedEventArgs e)
    {
        // Execute required job

        // Notify any subscribers to the event
        OnJobExecuted();
    }
    public void Dispose()
    {
        if (_Running)
        {
            // Clear up the timer
        }
    }
}

在 Program.cs 中注册

builder.Services.AddSingleton<PeriodicExecutor>();

请求它并在主页初始化中启动它

@page "/home"
@inject PeriodicExecutor PeriodicExecutor

@code {
    protected override void OnInitialized()
    {
        PeriodicExecutor.StartExecuting();
    }
}

如果你想在作业执行时做某事,在任何组件中

@inject PeriodicExecutor PeriodicExecutor
@implements IDisposable

<label>@JobNotification</label>

@code {

   protected override void OnIntiialized()
   {
       PeriodicExecutor.JobExecuted += HandleJobExecuted;
   }
   public void Dispose()
   {
       PeriodicExecutor.JobExecuted -= HandleJobExecuted;
   }

   string JobNotification;
   void HandleJobExecuted(object sender, JobExecutedEventArgs e)
   {
        JobNotification = $"Job Executed: {DateTime.Now}";
   }
}

【讨论】:

【解决方案2】:

为此,您可以将 MainLayout.razor 或 App.razor 视为“普通页面”。

当你有东西要在屏幕上显示时使用 MainLayout:

MainLayout.razor

.... 
<div>Time=@theTime</div>

@code
{
    protected override void OnInitialized()
    {
        Ticker();
        base.OnInitialized();
    }

    string theTime;

    // use an async-void or a timer. An async-void needs no cleanup. 
    async void Ticker()
    {
        while(true)
        {
            await Task.Delay(2_000);
            theTime = DateTime.Now.ToLongTimeString();

            StateHasChanged();   // refresh everything
        }
    }

}

【讨论】:

  • .net 6 PeriodicTimer 会做得更好:)
【解决方案3】:

如果您使用 Blazor WebAssembly 的 ASP.NET Core 托管风格,则可以使用 BackgroundService。例如:

MyBackgroundService.cs

public class MyBackgroundService : BackgroundService, IDisposable
{
    private readonly ILogger<CollectionService> _logger;
    private readonly IServiceScopeFactory _serviceScopeFactory;

    public MyBackgroundService(ILogger<CollectionService> logger, IServiceScopeFactory serviceScopeFactory)
    {
        _logger = logger;
        _serviceScopeFactory = serviceScopeFactory;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        _logger.LogInformation("My Background Service is starting.");

        //Do your work here...

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<MyBackgroundService>();
    services.AddHostedService(provider => provider.GetService<MyBackgroundService>());

此解决方案的一个好处是,无论用户是否导航到网站上的任何特定页面,服务都会开始运行。或者即使没有用户访问该站点。

【讨论】:

  • 这在 WebAssembly 上不起作用,是吗?
  • 好点!我的应用是一个 api 托管的 wasm 应用(所以我的代码在服务器端运行)。如果用户只实现前端(不是托管的 web api),那么这将不起作用。
【解决方案4】:

在 Blazor 组件中最简单的方法是创建 Timer,订阅 Elapsed,然后调用 InvokeAsync 以更新任何状态:

// In a @code block, or a Component.razor.cs file:
private readonly Timer _jobTimer = new() 
{
    AutoReset = true,
    Enabled = true,
    Interval = 60000, // 1 minute
};

protected override void OnInitialized() {
    _jobTimer.Elapsed += JobMethod; 
    // ^ don't forget to unsubscribe and dispose in IDisposable.Dispose!
}

private async void JobMethod(object sender, ElapsedEventArgs e) {
    // do extraneous stuff here
    await InvokeAsync(() => {
        // update state here
        StateHasChanged();
    }
}

protected virtual void Dispose(bool disposing) {
    // however you implement this, but at the very least least:
    _jobTimer.Elapsed -= JobMethod;
    _jobTimer.Dispose();
}

如果作业本地化为单个组件,请使用此选项。对于可重用性,首选Neil's answer

【讨论】:

  • 如果是本地化的,也要配置定时器。
  • 是的,这就是我的意思,但我想我的评论并不清楚。另外,修复了错误的订阅行。
猜你喜欢
  • 2021-09-25
  • 2022-11-24
  • 1970-01-01
  • 2021-10-29
  • 2021-04-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多