@@ -13,8 +13,7 @@ namespace Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal;
1313public class NpgsqlDatabaseCreator (
1414 RelationalDatabaseCreatorDependencies dependencies ,
1515 INpgsqlRelationalConnection connection ,
16- IRawSqlCommandBuilder rawSqlCommandBuilder ,
17- IRelationalConnectionDiagnosticsLogger connectionLogger )
16+ IRawSqlCommandBuilder rawSqlCommandBuilder )
1817 : RelationalDatabaseCreator ( dependencies )
1918{
2019 /// <summary>
@@ -175,7 +174,41 @@ private IReadOnlyList<MigrationCommand> CreateCreateOperations()
175174 /// doing so can result in application failures when updating to a new Entity Framework Core release.
176175 /// </summary>
177176 public override bool Exists ( )
178- => Exists ( async: false ) . GetAwaiter ( ) . GetResult ( ) ;
177+ => Dependencies . ExecutionStrategy . Execute ( ( ) =>
178+ {
179+ using var _ = new TransactionScope ( TransactionScopeOption . Suppress , TransactionScopeAsyncFlowOption . Enabled ) ;
180+ var opened = false ;
181+
182+ try
183+ {
184+ connection . Open ( errorsExpected : true ) ;
185+ opened = true ;
186+
187+ rawSqlCommandBuilder
188+ . Build ( "SELECT 1" )
189+ . ExecuteNonQuery (
190+ new RelationalCommandParameterObject (
191+ connection ,
192+ parameterValues : null ,
193+ readerColumns : null ,
194+ Dependencies . CurrentContext . Context ,
195+ Dependencies . CommandLogger ,
196+ CommandSource . Migrations ) ) ;
197+
198+ return true ;
199+ }
200+ catch ( Exception e ) when ( IsDoesNotExist ( e ) )
201+ {
202+ return false ;
203+ }
204+ finally
205+ {
206+ if ( opened )
207+ {
208+ connection . Close ( ) ;
209+ }
210+ }
211+ } ) ;
179212
180213 /// <summary>
181214 /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -184,85 +217,61 @@ public override bool Exists()
184217 /// doing so can result in application failures when updating to a new Entity Framework Core release.
185218 /// </summary>
186219 public override Task < bool > ExistsAsync ( CancellationToken cancellationToken = default )
187- => Exists ( async: true , cancellationToken ) ;
188-
189- private async Task < bool > Exists ( bool async , CancellationToken cancellationToken = default )
190- {
191- var logger = connectionLogger ;
192- var startTime = DateTimeOffset . UtcNow ;
193-
194- var interceptionResult = async
195- ? await logger . ConnectionOpeningAsync ( connection , startTime , cancellationToken ) . ConfigureAwait ( false )
196- : logger . ConnectionOpening ( connection , startTime ) ;
197-
198- if ( interceptionResult . IsSuppressed )
220+ => Dependencies . ExecutionStrategy . ExecuteAsync ( async ct =>
199221 {
200- // If the connection attempt was suppressed by an interceptor, assume that the interceptor took care of all the opening
201- // details, and the database exists.
202- return true ;
203- }
204-
205- // When checking whether a database exists, pooling must be off, otherwise we may
206- // attempt to reuse a pooled connection, which may be broken (this happened in the tests).
207- // If Pooling is off, but Multiplexing is on - NpgsqlConnectionStringBuilder.Validate will throw,
208- // so we turn off Multiplexing as well.
209- var unpooledCsb = new NpgsqlConnectionStringBuilder ( connection . ConnectionString ) { Pooling = false , Multiplexing = false } ;
222+ using var _ = new TransactionScope ( TransactionScopeOption . Suppress , TransactionScopeAsyncFlowOption . Enabled ) ;
223+ var opened = false ;
210224
211- using var _ = new TransactionScope ( TransactionScopeOption . Suppress , TransactionScopeAsyncFlowOption . Enabled ) ;
212- var unpooledRelationalConnection = connection . CloneWith ( unpooledCsb . ToString ( ) ) ;
213- try
214- {
215- if ( async)
225+ try
216226 {
217- await unpooledRelationalConnection . OpenAsync ( errorsExpected : true , cancellationToken : cancellationToken )
227+ await connection . OpenAsync ( cancellationToken , errorsExpected : true ) . ConfigureAwait ( false ) ;
228+ opened = true ;
229+
230+ await rawSqlCommandBuilder
231+ . Build ( "SELECT 1" )
232+ . ExecuteNonQueryAsync (
233+ new RelationalCommandParameterObject (
234+ connection ,
235+ parameterValues : null ,
236+ readerColumns : null ,
237+ Dependencies . CurrentContext . Context ,
238+ Dependencies . CommandLogger ,
239+ CommandSource . Migrations ) ,
240+ cancellationToken )
218241 . ConfigureAwait ( false ) ;
242+
243+ return true ;
219244 }
220- else
245+ catch ( Exception e ) when ( IsDoesNotExist ( e ) )
221246 {
222- unpooledRelationalConnection . Open ( errorsExpected : true ) ;
247+ return false ;
223248 }
224-
225- return true ;
226- }
227- catch ( PostgresException e )
228- {
229- if ( IsDoesNotExist ( e ) )
249+ finally
230250 {
231- return false ;
251+ if ( opened )
252+ {
253+ await connection . CloseAsync ( ) . ConfigureAwait ( false ) ;
254+ }
232255 }
256+ } , cancellationToken ) ;
233257
234- throw ;
235- }
236- catch ( NpgsqlException e ) when (
237- // This can happen when Npgsql attempts to connect to multiple hosts
238- e . InnerException is AggregateException ae && ae. InnerExceptions. Any( ie => ie is PostgresException pe && IsDoesNotExist ( pe ) ) )
239- {
240- return false;
241- }
242- catch ( NpgsqlException e ) when (
243- e . InnerException is IOException { InnerException : SocketException { SocketErrorCode : SocketError . ConnectionReset } } )
258+ private static bool IsDoesNotExist ( Exception exception )
259+ => exception switch
244260 {
261+ // Login failed is thrown when database does not exist (See Issue #776)
262+ PostgresException { SqlState : "3D000" }
263+ => true ,
264+
265+ // This can happen when Npgsql attempts to connect to multiple hosts
266+ NpgsqlException { InnerException : AggregateException ae } when ae . InnerExceptions . Any ( ie => ie is PostgresException { SqlState : "3D000" } )
267+ => true ,
268+
245269 // Pretty awful hack around #104
246- return false;
247- }
248- finally
249- {
250- if ( async)
251- {
252- await unpooledRelationalConnection . CloseAsync ( ) . ConfigureAwait ( false ) ;
253- await unpooledRelationalConnection . DisposeAsync ( ) . ConfigureAwait ( false ) ;
254- }
255- else
256- {
257- unpooledRelationalConnection . Close ( ) ;
258- unpooledRelationalConnection. Dispose ( ) ;
259- }
260- }
261- }
270+ NpgsqlException { InnerException : IOException { InnerException : SocketException { SocketErrorCode : SocketError . ConnectionReset } } }
271+ => true ,
262272
263- // Login failed is thrown when database does not exist (See Issue #776)
264- private static bool IsDoesNotExist ( PostgresException exception )
265- => exception . SqlState == "3D000" ;
273+ _ => false
274+ } ;
266275
267276 /// <summary>
268277 /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
0 commit comments