dotnet / aspire

Tools, templates, and packages to accelerate building observable, production-ready apps
https://learn.microsoft.com/dotnet/aspire
MIT License
3.91k stars 476 forks source link

Error updating database using dotnet ef database update when using postgresql #4497

Open kjartanium opened 5 months ago

kjartanium commented 5 months ago

Using GA version.

  1. Create a .NET Aspire Starter project in VS

  2. Add packages in Api service project:

    • Aspire.Npgsql.EntityFrameworkCore.PostgreSQL
    • Microsoft.EntityFrameworkCore.Design
    • Microsoft.EntityFrameworkCore.Tools
  3. Create a DbContext in the api service

public class ApplicationDbContext(DbContextOptions options) : DbContext(options)
{
    public DbSet<User> Users { get; set; }
}

public class User
{
    public int UserId { get; set; }
    public required string Name { get; set; }
}
  1. Create a postgres container and database in the apphost (Add Aspire.Hosting.PostgreSQL package)
var builder = DistributedApplication.CreateBuilder(args);

var postgresdb = builder.AddPostgres("postgres").AddDatabase("postgresdb");

var apiService = builder.AddProject<Projects.startertest_ApiService>("apiservice").WithReference(postgresdb);

builder.AddProject<Projects.startertest_Web>("webfrontend")
    .WithExternalHttpEndpoints()
    .WithReference(apiService);

builder.Build().Run();
  1. Add a the following in the api service program.cs:
builder.AddNpgsqlDbContext<ApplicationDbContext>("postgresdb");
  1. Create a migration in the Api service using "dotnet ef migrations add initial"

  2. Update the migration in the database fails with the following:

    Build started... Build succeeded. System.ArgumentException: Host can't be null at Npgsql.NpgsqlConnectionStringBuilder.PostProcessAndValidate() at Npgsql.NpgsqlConnection.SetupDataSource() at Npgsql.NpgsqlConnection.set_ConnectionString(String value) at Npgsql.NpgsqlConnection..ctor(String connectionString) at Npgsql.NpgsqlConnection.CloneWith(String connectionString) at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlRelationalConnection.CloneWith(String connectionString) at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlDatabaseCreator.Exists(Boolean async, CancellationToken cancellationToken) at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlDatabaseCreator.Exists() at Microsoft.EntityFrameworkCore.Migrations.HistoryRepository.Exists() at Microsoft.EntityFrameworkCore.Migrations.HistoryRepository.GetAppliedMigrations() at Npgsql.EntityFrameworkCore.PostgreSQL.Migrations.Internal.NpgsqlMigrator.Migrate(String targetMigration) at Microsoft.EntityFrameworkCore.Design.Internal.MigrationsOperations.UpdateDatabase(String targetMigration, String connectionString, String contextType) at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabaseImpl(String targetMigration, String connectionString, String contextType) at Microsoft.EntityFrameworkCore.Design.OperationExecutor.UpdateDatabase.<>c__DisplayClass0_0.<.ctor>b__0() at Microsoft.EntityFrameworkCore.Design.OperationExecutor.OperationBase.Execute(Action action) Host can't be null

I've tried with all possible locations of the migration assembly and so forth, same error.

kjartanium commented 5 months ago

Hmm, I'm assuming this happens because the connection string isn't really created at that point since the services isn't fired up. I guess i need to do it runtime, or are there any other workarounds?

davidfowl commented 2 months ago

This is because the connection string isn't available when you run from the command line. It's possible to run some dotnet ef commands without a valid connection string (a dummy one is still required), but dotnet ef database update isn't possible to run without the database running and the connection string being available in the application.

Possible workarounds;

Run the apphost, get the connection string from the dashboard (look at the environment variables of the application), and use it to run dotnet ef database update (passing in the connection string)

KurtP20 commented 1 month ago

Nice workaround, thanks for the tip! For me it worked only after adding --no-build option, because running the app locks some files that need to be updated during build.

KurtP20 commented 1 month ago

In my understanding, adding --startup-project <AppHost> should spin up the AppHost so that the container is running and the connection string is available, but there is an error as I have described on SO. Could you please comment on the SO question?