Popay
Popay

Reputation: 41

.NET Core 6 API throws 404 after EF Core migrations are applied

I have database initializer class that applies all pending migrations on startup. After my app hits MigrateAsync() method on dbContext my api endpoints return 404 (like those endpoints don't exist). But if I run my project again (so when there are no pending migrations ie. app doesn't hit MigrateAsync() method) it all works. Somehow applying migrations on startup with EF Core breaks my API.

My program.cs middleware pipeline:

if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();

    using (var scope = app.Services.CreateScope())
    {
        var databaseInitializer = scope.ServiceProvider.GetRequiredService<DatabaseInitializer>();
        await databaseInitializer.InitializeDatabase();
    }
}
else
{
    app.UseHsts();

    app.Use((context, next) => 
    {
        context.Request.Host = new HostString(app.Configuration["AppDomain"]);
        context.Request.Scheme = "https";
        return next();
    });
}


app.UseSerilogRequestLogging();

app.UseHttpsRedirection();

app.UseExceptionHandling();

app.UseAuthentication();

app.UseAuthorization();

app.MapControllers();

My database initializer method for migrations:

public async Task InitializeDatabase()
{
    try
    {
        if (_dbContext.Database.GetPendingMigrations().Any())
            await _dbContext.Database.MigrateAsync();
    }
    catch (Exception ex)
    {
        _logger.LogError(ex, "Error while initializig database");
        throw;
    }
}

Basically every time it hits await _dbContext.Database.MigrateAsync(); it breaks api.

Upvotes: 0

Views: 466

Answers (1)

Mark Homer
Mark Homer

Reputation: 1035

use a context fixture like this:

namespace DataAccessTests.databasefixtures
{
    public class TestDBFixture
    {
        private const string ConnectionString = "your connection string";

        private static readonly object _lock = new();
        private static bool _databaseInitialized;

        public TestDBFixture()
        {
            lock (_lock)
            {
                if (!_databaseInitialized)
                {
                    using (var context = CreateContext())
                    {
                        context.Database.EnsureDeleted();  // delete current db
                        context.Database.Migrate();        // create db and apply migrations (this way migrations going forwards are also valid)
                    }

                    _databaseInitialized = true;
                }
            }
        }

        public CDRContext CreateContext()
            => new CDRContext(
                new DbContextOptionsBuilder<CDRContext>()
                    .UseSqlServer(ConnectionString)
                    .Options);


    }
}

then just create it:

var Fixture = new TestDBFixture();

or you can implement it like this

public class BloggingControllerTest : IClassFixture<TestDatabaseFixture>
{
    public BloggingControllerTest(TestDatabaseFixture fixture)
        => Fixture = fixture;

    public TestDatabaseFixture Fixture { get; }

Upvotes: 0

Related Questions