docker 容器重新启动时 Database.Migrate 失败

Rez*_*oei 5 c# docker entity-framework-core asp.net-core-webapi entity-framework-migrations

我已在 Docker 上部署了 ASP.NET Core Web API。我的应用程序还与 Docker 容器中的 SQL Server 交互。

这是我的docker-compose.yml文件:

 version: '3'
 services:
     db:
         image: mcr.microsoft.com/mssql/server
         container_name: db
         ports:
             - 1533:1433
         environment:
             - TZ=Asia/Tehran
             - ACCEPT_EULA=Y
             - MSSQL_SA_PASSWORD=******
         user: root
         volumes:
             - ./mssql:/var/opt/mssql/data
    kava-service:
        image: service:1.0.0
        container_name: service
        expose:
            - 9001
        ports:
            - 9001:9001
        depends_on:
            - db
        environment:
            - ASPNETCORE__ENVIRONMENT=Production
            - TZ=Asia/Tehran
            - AUTO__MIGRATE=true
            - ConnectionStrings__BusinessDomainConnectionString=Server=db;Initial Catalog=Kava.BusinessDomain;User ID=sa;pwd=****;MultipleActiveResultSets=true;
            - ConnectionStrings__RecordsConnectionString=Server=db;Initial Catalog=Kava.Records;User Id=sa;pwd=****;MultipleActiveResultSets=true;
Run Code Online (Sandbox Code Playgroud)

当我运行 docker 容器(使用docker-compose)时,它工作正常。但是,如果我重新启动此容器,它就不起作用,并且应用程序将退出并显示以下消息:

用户“sa”登录失败。原因:无法打开显式指定的数据库“Kava.BusinessDomain”[客户端:172.18.0.4] kava-service
未处理的异常。Microsoft.Data.SqlClient.SqlException(0x80131904):数据库“Kava.BusinessDomain”已存在。选择不同的数据库名称。

当我删除这个数据库时,一切正常,但我不想删除它。

在开发中(我的意思是 Visual Studio)一切都很好,但在生产中(Docker),迁移无法按预期进行。

这是我的迁移命令:

 BusinessDomainContext businessDomainContext = serviceScope.ServiceProvider.GetRequiredService<IBusinessDomainDatabaseFactory>().Get() as BusinessDomainContext;
 await businessDomainContext.Database.MigrateAsync();
Run Code Online (Sandbox Code Playgroud)

Von*_*onC 0

您的迁移过程可能会尝试创建一个已经存在的数据库。您的ASP.NET Core应用程序应检查数据库是否存在,并仅应用必要的迁移,而不是每次都尝试创建新数据库(使用“在应用程序启动时解析服务”)。

using (var scope = app.ApplicationServices.CreateScope())
{
    var services = scope.ServiceProvider;
    var context = services.GetRequiredService<BusinessDomainContext>();

    // Check if the database exists and apply migrations if necessary
    if (context.Database.CanConnect())
    {
        // context.Database.GetPendingMigrations().Any() is another option.
        if (!context.Database.GetAppliedMigrations().SequenceEqual(context.Database.GetMigrations()))
        {
            await context.Database.MigrateAsync();
        }
    }
    else
    {
        // Logic to handle the scenario where the database does not exist
        // That might include creating the database, if that is your intention
    }
}
Run Code Online (Sandbox Code Playgroud)

Login failed for user 'sa'错误可能是应用程序尝试连接时 SQL Server 容器未完全准备就绪的症状。您可以使用连接字符串测试数据库连接重试策略的实现,如“实现弹性 Entity Framework Core SQL 连接”中所示。

services.AddDbContext<BusinessDomainContext>(options =>
    options.UseSqlServer(
        Configuration.GetConnectionString("BusinessDomainConnectionString"),
        sqlServerOptionsAction: sqlOptions =>
        {
            sqlOptions.EnableRetryOnFailure(
                maxRetryCount: 5, 
                maxRetryDelay: TimeSpan.FromSeconds(10),
                errorNumbersToAdd: null);
        }));
Run Code Online (Sandbox Code Playgroud)

这将设置一个重试策略,应用程序将尝试重新连接到数据库最多五次,重试之间有 10 秒的延迟。


此外,出于调试目的,请尝试增强这些操作的日志记录。并使用docker-compose up -d; docker-compose logs -fto 在后台运行服务,然后跟踪容器的任何输出。

在应用程序内部,您可以使用日志框架来记录消息。在 ASP.NET Core 应用程序中,您可以使用内置的日志记录框架。您可以在您的Program.csStartup.cs文件中将其配置为将消息记录到控制台,然后 Docker 将获取这些消息。

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        })
        .ConfigureLogging(logging =>
        {
            logging.ClearProviders();
            logging.AddConsole();
        });
Run Code Online (Sandbox Code Playgroud)

您可以将ASPNETCORE_ENVIRONMENT环境变量设置为Development从 ASP.NET Core 应用程序获取更详细的日志。

可以在您的docker-compose.yml文件中使用command服务字段来覆盖 Docker 映像在容器启动时运行的默认命令来完成此操作。
这对于添加标志或参数以启用更详细的日志记录非常有用。

例如,如果您的应用程序接受一个--verbose标志来启用更详细的日志,您可以添加command如下字段:

services:
  service:
    image: service:1.0.0
    command: ["./your-app", "--verbose"]
    # rest of your service configuration
Run Code Online (Sandbox Code Playgroud)

在 ASP.NET Core 应用程序的上下文中,您可以使用环境变量控制日志级别。例如,您可以设置Logging__LogLevel__DefaultDebug获取Information更详细的日志:

services:
  service:
    image: service:1.0.0
    environment:
      - ASPNETCORE__ENVIRONMENT=Development
      - AzureFunctionsJobHost__logging__logLevel__default=Debug
    # rest of your service configuration
Run Code Online (Sandbox Code Playgroud)

Debug这会将应用程序中所有类别的日志的日志级别设置为。您还可以使用环境变量(例如Logging__LogLevel__Microsoft=Information.