在使用 Entity Framework 进行数据库迁移期间,如何避免 .NET 5 WebApp 中出现 Windows 服务错误 1053?

Posted

技术标签:

【中文标题】在使用 Entity Framework 进行数据库迁移期间,如何避免 .NET 5 WebApp 中出现 Windows 服务错误 1053?【英文标题】:How can I avoid Windows Service error 1053 in my .NET 5 WebApp during database migration with Entity Framework? 【发布时间】:2022-01-15 04:18:26 【问题描述】:

我的 .NET 5 WebApp 作为 Windows 服务运行,并在启动期间使用 Entity Framework 播种和更新(迁移)其数据库。在致电Host.Run() 之前,我确保我的数据库已更新。在过去的一年里,这一切都很好。现在,我有一个需要几分钟才能完成的大型数据库更新。在此数据库更新期间,Windows 服务将关闭并出现错误 1053,表示超时。我怀疑这是由于运行时在给定的默认超时期限(看起来大约 30 秒)内没有达到对 Host.Run() 的调用。问题是我必须在调用Host.Run() 之前执行这些数据库更新,因为数据库应该在对其进行任何访问之前正确更新。

解决这个问题最简单的方法是什么?我可以尝试编写自定义服务生命周期来增加超时。我可以移动要在Host.Run() 之后执行的数据库更新,并在执行时限制访问的额外开销。我不喜欢这些解决方案中的任何一个,并寻求更好的选择。也许我的假设也完全错误。下面提供了我的代码。

public class Program

    public static async Task Main(string[] args)
    
        IHost host = CreateHostBuilder(args).Build();
       
        using (IServiceScope scope = host.Services.CreateScope())
        
            IServiceProvider services = scope.ServiceProvider;
            SeedAndUpdateDb seed = services.GetRequiredService<SeedAndUpdateDb>();
            await seed.InitializeAsync(); //<- This call takes a few minutes to complete
        

        await host.RunAsync();
    
    
    public static IHostBuilder CreateHostBuilder(string[] args)
    
        var hostingConfig = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .Build();
            
        return Host.CreateDefaultBuilder(args)
            .UseWindowsService()
            .ConfigureWebHostDefaults(webBuilder =>
            
                webBuilder.UseStartup<Startup>();
                webBuilder.ConfigureKestrel(serverOptions =>
                
                    serverOptions.Configure(hostingConfig.GetSection("Kestrel"));
                );
                webBuilder.UseKestrel(options =>
                 );
            );
    

【问题讨论】:

【参考方案1】:

底线是这样的:如果您正在运行的可执行文件要被识别为已启动的 Windows 服务,它必须(在作为服务安装并以此方式启动之后)在服务启动时告诉 Windows Service Control Manager 它实际上是一个服务并且它已经成功启动。

如果它没有及时这样做,您会收到可怕的错误消息,即服务没有及时启动。

您对.UseWindowsService() 的调用设置了到SCM 的所有管道,但对SCM 的调用仅在await host.RunAsync() 中的.NET Core 主机启动代码中进行。

因此,如果在您的可执行文件启动(由于您启动服务)和您的代码调用 await host.RunAsync() 之间的时间过长,您的可执行文件将会运行并且您的 托管服务 最终会运行,但是就 Windows 而言,您的 Windows 服务 没有(及时)启动。

解决方案是使用命令行标志调用迁移,而不是在每次启动时调用。或者在您的托管服务中调用一次迁移,以便在 Windows 服务已经运行时。

对于命令行方法:因此在部署之后,您可以在启动 Windows 服务之前运行Your-Service.exe --migrate

类似这样的:

public static async Task Main(string[] args)

    IHost host = CreateHostBuilder(args).Build();
   
    if (args.Any(a => a == "--migrate"))
    
        using (IServiceScope scope = host.Services.CreateScope())
        
            IServiceProvider services = scope.ServiceProvider;
            SeedAndUpdateDb seed = services.GetRequiredService<SeedAndUpdateDb>();
            await seed.InitializeAsync(); //<- This call takes a few minutes to complete
        

        Console.WriteLine("Database migrated, you can now start the service");
    
    else
    
        await host.RunAsync();
    

【讨论】:

好提示。不知道这是可能的!但是,也许我的问题不完整?我不仅使用该方法执行迁移(通常不会花费很长时间),而且还对表中的数据执行更新(以使用适当的数据填充我添加的新字段)。 查看编辑,因此要么迁移和播种,要么启动服务,而不是两者兼而有之。 有趣,我会试试这个,一旦我有足够的结果回来报告! 我终于测试过了。提供命令行参数按预期工作。使用参数作为服务启动程序仍然不起作用,因为服务会报告通常的超时错误。但是,在命令行上启动它并等待它终止就可以了。我会说这更像是一种解决方法,而不是直接解决我的问题。如果我将它作为迁移服务启动,我仍然对如何避免超时感到困惑,但由于这对我有用,我现在将不理会这个主题并完全像这样实现它!谢谢! @Alpha 我已经用更多解释更新了我的答案,希望对您有所帮助。

以上是关于在使用 Entity Framework 进行数据库迁移期间,如何避免 .NET 5 WebApp 中出现 Windows 服务错误 1053?的主要内容,如果未能解决你的问题,请参考以下文章

使用Entity Framework Core管理数据库架构进行数据迁移

使用Entity Framework Core管理数据库架构进行数据迁移

使用Entity Framework Core管理数据库架构进行数据迁移

如何在 Entity Framework 中强制沿导航关系进行完整加载?

Entity Framework实体框架使用TrackerEnabledDbContext进行操作日志跟踪

如何使用 Entity Framework Code First V6.1.2 进行集成测试