private Task <bool> ExistsAsync(bool retryOnNotExists, CancellationToken cancellationToken) => Dependencies.ExecutionStrategyFactory.Create().ExecuteAsync( DateTime.UtcNow + RetryTimeout, async(giveUp, ct) => { while (true) { try { using (new TransactionScope(TransactionScopeOption.Suppress, TransactionScopeAsyncFlowOption.Enabled)) { await _connection.OpenAsync(ct, errorsExpected: true); _connection.Close(); } return(true); } catch (PostgresException e) { if (!retryOnNotExists && IsDoesNotExist(e)) { return(false); } if (DateTime.UtcNow > giveUp || !RetryOnExistsFailure(e)) { throw; } await Task.Delay(RetryDelay, ct); } } }, cancellationToken);
public override async Task CreateTablesAsync(CancellationToken cancellationToken = default) { var operations = Dependencies.ModelDiffer.GetDifferences(null, Dependencies.Model.GetRelationalModel()); var commands = Dependencies.MigrationsSqlGenerator.Generate(operations, Dependencies.Model); // If a PostgreSQL extension, enum or range was added, we want Npgsql to reload all types at the ADO.NET level. var reloadTypes = operations.OfType <AlterDatabaseOperation>() .Any(o => o.GetPostgresExtensions().Any() || o.GetPostgresEnums().Any() || o.GetPostgresRanges().Any()); try { await Dependencies.MigrationCommandExecutor.ExecuteNonQueryAsync(commands, _connection, cancellationToken); } catch (PostgresException e) when( e.SqlState == "23505" && e.ConstraintName == "pg_type_typname_nsp_index" ) { // This occurs when two connections are trying to create the same database concurrently // (happens in the tests). Simply ignore the error. } if (reloadTypes) { await _connection.OpenAsync(cancellationToken); try { // TODO: Not async ((NpgsqlConnection)_connection.DbConnection).ReloadTypes(); } catch { _connection.Close(); } } }