dotnet / aspire

An opinionated, cloud ready stack for building observable, production ready, distributed applications in .NET
https://learn.microsoft.com/dotnet/aspire
MIT License
3.72k stars 430 forks source link

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

Open kjartanium opened 3 months ago

kjartanium commented 3 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 3 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 3 weeks 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)