Blazor wasm 中的定期后台任务
Periodic background task in Blazor wasm
在 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 视为 'normal pages'。
当您有内容要在屏幕上显示时使用 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
}
}
}
如果您使用的是 ASP.NET 核心托管风格的 Blazor WebAssembly,则可以使用 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();
}
如果作业已本地化为单个组件,请使用此选项。为了可重用性,更喜欢 .
在 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 视为 'normal pages'。
当您有内容要在屏幕上显示时使用 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
}
}
}
如果您使用的是 ASP.NET 核心托管风格的 Blazor WebAssembly,则可以使用 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();
}
如果作业已本地化为单个组件,请使用此选项。为了可重用性,更喜欢