0

Im using EF Core in a .NET 9.0 Web API project, and I was trying to implement automatic migrations on API startup. The current project makes use of Docker containers (Configured using a Docker compose file, along with other services), without success. The DB is SQL Server and it already has migrations applied (just two, applied on the first app startup), so I expected this method to bring just an empty list (in both: project and DB, has the same migrations count, the same migration names, etc) because there are no pending migrations, they're already synchronized.

The problem: It always brings migrations brings the same list of migrations as if they had never been applied.

Nothing worked for me. Here is the current implementation:

DatabaseExtension.cs:

    public static IServiceCollection AddApplicationDbContexts(this IServiceCollection services, IConfiguration configuration)
    {
        var masterConnString = configuration["MASTERDB_CS"]
            ?? (configuration.GetConnectionString("MasterDb")
            ?? throw new InvalidOperationException("Connection string 'MasterDb' not found."));

        services.AddDbContext<MasterDbContext>(options =>
            options.UseSqlServer(masterConnString, x => {
                x.MigrationsAssembly(typeof(MasterDbContext).Assembly.GetName().Name);
            })
        );

        return services;
    }

    public static IApplicationBuilder RunDbMigrations(this IApplicationBuilder app)
    {
        using (var scope = app.ApplicationServices.CreateScope())
        {
            var dbMaster = scope.ServiceProvider.GetRequiredService<MasterDbContext>();
            var pendingMigrations = dbMaster.Database.GetPendingMigrations();  // IT DOESN'T WORK

            // The connection strings were checked here:
            string cs = dbMaster.Database.GetConnectionString();
            Console.WriteLine(!string.IsNullOrWhiteSpace(cs) ? $"Usando DB: {cs}" : "SIN CONEXION");

            // An empty list is expected here (DB, Project are synchronized)
            // no need to migrate anything
            if (!pendingMigrations.Any())
            {
                Console.WriteLine("No hay migraciones pendientes...");
                return app;
            }

            // Instead, I always receive the two migrations (an initial an another complementary one)
            // so is always trying to create the DB from scratch, and it fails because it already exists
            try
            {
                Console.WriteLine($"Aplicando migrations...");
                dbMaster.Database.Migrate();
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Error al aplicar migraciones!: {ex.Message}");
                throw;
            }

            return app;
        }
    }

Theere are two projects in my .NET solution: The API and a class library that stores the EF Entities, migrations and contexts

In Program.cs:

// APPLICATION SETUP
builder.Services
    .AddApplicationCors()                                   // CORS
    .AddApplicationDbContexts(builder.Configuration)        // Databases
    // ...
    // ...


var app = builder.Build();

// --------------------------------- MIDDLEWARES ---------------------------------

// DB Migrations:
app.RunDbMigrations();

app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();

What I'm missing??

I tried everything:

  • My local environment pointing to a remote DB (it works there), it gets an empty list as expected (DB, and project are synchronized)
  • I reviewed connection strings (I ensured the connection strings to be exactly the same)
  • I also included the migrations assembly in the .UseSqlServer() configuration method for my DbContext
  • Even I tested the API with Scalar (A modern Swagger) using my endpoints, creating and uptating entities, all is working normal (It means that the Backend and the DB are communicating successfully in the same docker network)
4
  • All that method does is read from the __EFMigrationsHistory (assuming you haven't changed it) migrations table in the database and compare with the migrations available in code. If it is bringing back all migrations, are you 100% sure it is reading from the same database that you think it is? And you haven't done anything like changing the context name or something that would de-sync the table from the code? Commented Jun 23 at 14:47
  • Thanks for the answer @DavidG and yes, that's right, same connection string (from an environment variable), same that backend takes to create/edit/update entities, also the context is the right one, I have two contexts in the class library project, but as you can see, I'm just using one in the services.AddDbContext<MasterDbContext>(options => ) ... line, there are no renames of contexts or migrations as well (the project development just started) Commented Jun 23 at 18:12
  • I'm curious what happens if you kill the database and apply the migrations from scratch again. Does it still complain if you run it all again? Commented Jun 24 at 9:14
  • Run this: public sealed class MigrationHistoryRow { public string MigrationId { get; set; } = default!; public string ProductVersion { get; set; } = default!; } var migrations = context.Database.SqlQueryRaw<MigrationHistoryRow>("SELECT * FROM [__EFMigrationsHistory]").ToList(); Somewhere in RunDbMigrations method just to be sure it return those two migrations. Commented Aug 12 at 2:50

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.