【发布时间】:2021-08-09 15:07:55
【问题描述】:
在 Blazor wasm 中,我想定期执行作业(代码), 即使用户正在浏览页面(例如每 x 分钟)。
这可能吗?什么是实用的方法?
【问题讨论】:
在 Blazor wasm 中,我想定期执行作业(代码), 即使用户正在浏览页面(例如每 x 分钟)。
这可能吗?什么是实用的方法?
【问题讨论】:
创建一个服务来管理计时器
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}";
}
}
【讨论】:
为此,您可以将 MainLayout.razor 或 App.razor 视为“普通页面”。
当你有东西要在屏幕上显示时使用 MainLayout:
....
<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
}
}
}
【讨论】:
PeriodicTimer 会做得更好:)
如果您使用 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>());
此解决方案的一个好处是,无论用户是否导航到网站上的任何特定页面,服务都会开始运行。或者即使没有用户访问该站点。
【讨论】:
在 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。
【讨论】: