使用 Scoped IHostedService 访问 DBContext 时出现问题



我正在尝试创建一个执行 Sql 命令的计划任务。实际的计划进程工作正常,但是我的数据库上下文存在一个问题,即它实际上无法访问数据库。

它将获得一个上下文,其中包含所有适当的表,但对表的任何操作都会引发InvalidOperationException。当尝试在调试模式下检查任何表元素时,它们也都以红色X作为图标表示InvalidOperationException

我也尝试通过MediatR命令和处理程序发送命令,但出现相同的错误。每次更改代码的尝试都会导致相同的错误,没有偏差。

这是我第一次尝试创建后台服务,作为参考,我正在学习来自 https://thinkrethink.net/2018/02/21/asp-net-core-background-processing/

启动.cs

// truncated
services.AddDbContext<MyDbContext>();
services.AddSingleton<IHostedService, Daily_Task>();

后台服务

namespace MyApi.ScheduledTasks
{
public abstract class BackgroundService : IHostedService
{
private Task _executingTask;
private readonly CancellationTokenSource tokenSource = new CancellationTokenSource();
public virtual Task StartAsync(CancellationToken token)
{
_executingTask = ExecuteAsync(tokenSource.Token);
return _executingTask.IsCompleted ? _executingTask : Task.CompletedTask;
}
public virtual async Task StopAsync(CancellationToken token)
{
if (_executingTask == null)
return;
try
{
tokenSource.Cancel();
}
finally
{
await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, token));
}
}
protected virtual async Task ExecuteAsync(CancellationToken token)
{
do
await Process();
while (!token.IsCancellationRequested);
}
protected abstract Task Process();
}
}

作用域处理器

namespace MyApi.ScheduledTasks
{
public abstract class ScopedProcessor: BackgroundService
{
private readonly IServiceScopeFactory _scopeFactory;
public ScopedProcessor(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
protected override async Task Process()
{
using (var scope = _scopeFactory.CreateScope())
{
await ProcessInScope(scope.ServiceProvider);
}
}
public abstract Task ProcessInScope(IServiceProvider provider);
}
}

要计划的任务

namespace MyApi.ScheduledTasks
{
public class Daily_Task : ScopedProcessor
{
private DateTime Start => DateTime.Now;
private DateTime NextTask { get; private set; }
public Daily_Task(IServiceScopeFactory scopeFactory) : base(scopeFactory) 
{
NextTask = SetNextRun(Start);
}
// currently doing minutes for testing
private DateTime SetNextRun(DateTime date) => date.AddMinutes(1);
public override async Task ProcessInScope(IServiceProvider provider)
{
try
{
using (var context = provider.GetRequiredService<MyDbContext>())
{
// even this causes an error
var data = context.MyTable.Any();
}
}
catch (Exception e)
{
Log.Error(e);
}
await Task.CompletedTask;
}
protected override async Task ExecuteAsync(CancellationToken token)
{
do
{
var now = DateTime.Now;
if (now > NextTask)
{
await Process();
NextTask = SetNextRun(now);
}
}
while (!token.IsCancellationRequested);
}
}
}

学习如何做后台服务很容易感到困惑,因为有关于如何做的教程,然后 .NET 在开发 dotnetcore 期间添加并更改了使教程无效的最佳做法。现在有一个可以实现的 BackgroundService 基类,它应该使用 AddHostedService 添加到 DI 容器中。它在Microsoft.Extensions.DependencyInjection中已经有一段时间了。如果您改用它,它将大大简化添加后台服务所必须执行的操作。

有关实现后台服务的快速入门:

仅覆盖执行异步。主机将等待它返回,因此,如果您有一个长时间运行的后台任务(或永久后台任务(,那么您希望返回一个任务,而不是等待它从 ExecuteAsync 完成(例如:使用 Task.Run(。当触发应用关闭时,传递给 ExecuteAsync 的取消令牌将被取消,在进程退出之前,你有几秒钟的时间正常关闭。:P默认情况下,不会为托管服务创建任何范围,因此您需要注入您已经知道如何操作的 IServiceProvider。

参考:https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio

最新更新