gpt4 book ai didi

asp.net-core - ASP.NET Core HostedService 在创建数据库之前尝试访问它

转载 作者:行者123 更新时间:2023-12-04 12:21:47 25 4
gpt4 key购买 nike

我正在使用 .NET 5.0 开发一个 asp.net 项目;在这个项目中,我使用 Entity Framework ,代码优先,以及一个 sql lite 文件数据库。
startup.cs文件我使用以下代码以编程方式创建和更新数据库架构:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
{
// migrate any database changes on startup (includes initial db creation)
dataContext.Database.Migrate();

...
}
然后我有一个服务,我用它来清理一些数据库表中的旧数据。它会在启动时定期清理它们:
public class TimedDbCleanerService : IHostedService, IDisposable
{

...
}
使用这些功能:
public Task StartAsync(CancellationToken stoppingToken)
{
_timer = new Timer(DoWork, null, TimeSpan.Zero,
TimeSpan.FromHours(_dbCleanerSettings.Hours));

return Task.CompletedTask;
}

private void DoWork(object state)
{
// create scoped dbcontext
using (var scope = _scopeFactory.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<DataContext>();

// check and remove stuff

dbContext.SaveChanges();
}
}
startup.cs用这行代码注册:
services.AddHostedService<TimedDbCleanerService>();
这里的问题是,如果在执行项目之前数据库文件不存在,则清洁服务会尝试访问尚不存在的数据库表。
使用调试器我可以看到 Database.Migrate()在服务访问数据库之前调用,但迁移似乎是一个需要时间才能完成的异步任务。
在创建和启动更清洁的服务之前,有没有一种方法可以等待迁移以完全执行其工作?

最佳答案

创建一个类来协调迁移。这将执行迁移并帮助其他人使用 SemaphoreSlim 等待完成。 .
在这个类中,我们注入(inject)了 DbContext并运行迁移,然后让等待迁移的线程继续执行。

public class DatabaseMigrator
{
private readonly AppDbContext _dbContext;

private static readonly SemaphoreSlim _migrationEvent = new SemaphoreSlim(0);

public DatabaseMigrator(AppDbContext dbContext)
{
_dbContext = dbContext;
}

public void Migrate()
{
_dbContext.Database.Migrate();
_migrationEvent.Release(1);
}

public Task WaitForMigrationAsync(CancellationToken cancellationToken = default)
{
return _migrationEvent.WaitAsync(cancellationToken);
}
}
将这个类注册到 DI:
services.AddSingleton<DatabaseMigrator>();
在您的 Main函数或 Startup.Configure ,注入(inject)此函数并运行迁移:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DatabaseMigrator databaseMigrator)
{
databaseMigrator.Migrate();
// ...
}
在后台服务中,我们不注入(inject)迁移器,因为它依赖于像 DbContext 这样的作用域服务。 .相反,我们从我们创建的范围中解析一个,等待迁移完成。
class CleanerService : BackgroundService
{
private IServiceScopeFactory _serviceScopeFactory;

public CleanerService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}

protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var migrator = scope.ServiceProvider.GetRequiredService<DatabaseMigrator>();
await migrator.WaitForMigrationAsync(stoppingToken);
}

await PerformCleanup();
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
await PerformCleanup();
}
}

private async Task PerformCleanup()
{
using var scope = _serviceScopeFactory.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// ... clean things up
}
}

一个小点:而不是使用 Timer在强制您使用 void 的异步上下文中,异步工作带来一大堆问题,可以拨打 Task.Delay在一个循环中,它将线程释放到线程池中,让它在等待时执行其他任务:
await PerformCleanup();
while (!stoppingToken.IsCancellationRequested)
{
await Task.Delay(TimeSpan.FromHours(1), stoppingToken);
await PerformCleanup();
}

关于asp.net-core - ASP.NET Core HostedService 在创建数据库之前尝试访问它,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/68590750/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com