Using Dapper to auto-migrate a postgres database and create the database
- code example : HERE
- create an Extension folder with a HostExtensions static class.
- inside it create a static generic method that takes the host (what configures the startup and lifetime of the app) which returns it after it’s done.
- we add a retry cause when running in a docker-compose, the databse being ready isn’t a gurantee even with
depends-onspecified.
1 2 3 4 5 6
public static class HostExtensions { public static IHost MigrateDatabase<TContext>(this IHost host, int? retry = 0) { } }
- we add a retry cause when running in a docker-compose, the databse being ready isn’t a gurantee even with
- create a
scopefromhost.Services.CreateScope()and get the required services1 2 3 4 5 6
using (var scope = host.Services.CreateScope()) { var services = scope.ServiceProvider; var configuration = services.GetRequiredService<IConfiguration>(); var logger = services.GetRequiredService<ILogger<TContext>>(); }
- in there add a
tryandcatchblocks, log depending on your desire and create a connection object using theusingscope so it releases (closes) the connection when it’s done.- the connection string for
postgresneeds to NOT have the database name, it’ll look like this:"DatabaseSettings": { "ConnectionString": "Server=HOST;Port=PORT;User Id=USER;Password=PASS;", "DatabaseName":"db-name" <- needs to be lower case ! }
1 2 3 4 5 6 7 8
logger.LogInformation("Migrating postresql database."); using var connection = new NpgsqlConnection(configuration.GetValue<string>("DatabaseSettings:ConnectionString")); // open the connection connection.Open(); // get the database name from `appsettings.json` or where'ver you have it string dbName = configuration["DatabaseSettings:DatabaseName"];
- the connection string for
check if the database you’re trying to create exists or not, the name of the database has to be lower case to match the one postgres will create.
1 2 3 4 5 6 7 8
var checkDatabaseExistsSql = $"SELECT 1 FROM pg_database WHERE datname = '{dbName}'"; var databaseExists = connection.ExecuteScalar<bool>(checkDatabaseExistsSql); if (!databaseExists) { var createDatabaseSql = $"CREATE DATABASE {dbName};"; connection.Execute(createDatabaseSql); }
- kill the existsing connection and create a new one that’ll have the
databaseyou wanna use in it, open it and create acommandobject so you can do the operations you desire:1 2 3 4 5 6 7 8 9 10 11
connection.Close(); // replace the old connection with a new one with the database using var updatedConnection = new NpgsqlConnection($"{configuration["DatabaseSettings:ConnectionString"]}Database={dbName};"); updatedConnection.Open(); using var command = new NpgsqlCommand { Connection = updatedConnection };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
command.CommandText = "DROP TABLE IF EXISTS TABLE-NAME"; command.ExecuteNonQuery(); command.CommandText = @"CREATE TABLE TABLE(Id SERIAL PRIMARY KEY, Name VARCHAR(24) NOT NULL, Description TEXT, ... )"; command.ExecuteNonQuery(); command.CommandText = "INSERT INTO TABLE_NAME(Name, Description) VALUES('STUFF', 'IN HERE');"; command.ExecuteNonQuery(); . . . logger.LogInformation("Migrated postresql database.");
- in the
catchblockwe except a
NpgsqlExceptionexception and retry1 2 3 4 5 6 7 8 9 10 11
catch (NpgsqlException ex) { logger.LogError(ex, "An error occurred while migrating the postresql database"); if (retryForAvailability < 50) { retryForAvailability++; System.Threading.Thread.Sleep(2000); MigrateDatabase<TContext>(host, retryForAvailability); } }
- finally we return the
Host1
return host;
- in
Programfile, we call it providing theProgram classas the Type1
app.MigrateDatabase<Program>();
test it
spin up a postgres container
1
docker container run -p 5432:5432 -e POSTGRES_USER=admin -e POSTGRES_PASSWORD=admin1234 --name testgres postgres
create a new api project or use an open one and follow thru the steps upove