【发布时间】:2018-11-02 10:06:42
【问题描述】:
如何在后台的计时器上写入数据库。例如,检查邮件并将新信件添加到数据库中。在示例中,我在写入数据库之前简化了代码。
Microsoft 示例中的类名。 录音类本身:
namespace EmailNews.Services
{
internal interface IScopedProcessingService
{
void DoWork();
}
internal class ScopedProcessingService : IScopedProcessingService
{
private readonly ApplicationDbContext _context;
public ScopedProcessingService(ApplicationDbContext context)
{
_context = context;
}
public void DoWork()
{
Mail mail = new Mail();
mail.Date = DateTime.Now;
mail.Note = "lala";
mail.Tema = "lala";
mail.Email = "lala";
_context.Add(mail);
_context.SaveChangesAsync();
}
}
}
定时器类:
namespace EmailNews.Services
{
#region snippet1
internal class TimedHostedService : IHostedService, IDisposable
{
private readonly ILogger _logger;
private Timer _timer;
public TimedHostedService(IServiceProvider services, ILogger<TimedHostedService> logger)
{
Services = services;
_logger = logger;
}
public IServiceProvider Services { get; }
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is starting.");
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromMinutes(1));
return Task.CompletedTask;
}
private void DoWork(object state)
{
using (var scope = Services.CreateScope())
{
var scopedProcessingService =
scope.ServiceProvider
.GetRequiredService<IScopedProcessingService>();
scopedProcessingService.DoWork();
}
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Timed Background Service is stopping.");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
#endregion
}
启动:
services.AddHostedService<TimedHostedService>();
services.AddScoped<IScopedProcessingService, ScopedProcessingService>();
似乎一切都像示例中那样完成,但没有向数据库添加任何内容,不是这样吗?
【问题讨论】:
-
您是否尝试过调试?定时器会触发吗?是否调用了服务的
DoWork?它完成还是抛出?除了调试之外,您至少应该在代码中添加日志记录以记录异常。 -
这可能是因为您没有
await您的SaveChangesAsync调用,尤其是在该调用完成之前,生成您的IScopedProcessingService和相应的ApplicationDbContext实例的范围正在处理。 -
Timer类无法处理异步回调。DoWork必须 为async Task以允许SaveChangesAsync在不阻塞的情况下完成。这意味着您不能从计时器的回调中调用它。您可以将计时器替换为包含Task.Delay()的循环。 -
查看 Maarten Balliauw 的文章 Building a scheduled task in ASP.NET Core/Standard 2.0。
ExecuteAsync包含一个循环,该循环调用计时器内的任务,然后调用await Task.Delay(...);。这篇文章走得更远,允许定义自定义任务、解析cron调度字符串、一次执行多个任务等 -
David Fowler 创建了一个带有异步陷阱和警告的 Github 存储库。查看Timer Callbacks 部分,了解调用异步回调的正确方法
标签: c# asp.net-core async-await asp.net-core-2.1 asp.net-core-hosted-services