@@ -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>
@@ -174,7 +173,41 @@ private IReadOnlyList<MigrationCommand> CreateCreateOperations()
174173 /// doing so can result in application failures when updating to a new Entity Framework Core release.
175174 /// </summary>
176175 public override bool Exists ( )
177- => Exists ( async: false ) . GetAwaiter ( ) . GetResult ( ) ;
176+ => Dependencies . ExecutionStrategy . Execute ( ( ) =>
177+ {
178+ using var _ = new TransactionScope ( TransactionScopeOption . Suppress , TransactionScopeAsyncFlowOption . Enabled ) ;
179+ var opened = false ;
180+
181+ try
182+ {
183+ connection . Open ( errorsExpected : true ) ;
184+ opened = true ;
185+
186+ rawSqlCommandBuilder
187+ . Build ( "SELECT 1" )
188+ . ExecuteNonQuery (
189+ new RelationalCommandParameterObject (
190+ connection ,
191+ parameterValues : null ,
192+ readerColumns : null ,
193+ Dependencies . CurrentContext . Context ,
194+ Dependencies . CommandLogger ,
195+ CommandSource . Migrations ) ) ;
196+
197+ return true ;
198+ }
199+ catch ( Exception e ) when ( IsDoesNotExist ( e ) )
200+ {
201+ return false ;
202+ }
203+ finally
204+ {
205+ if ( opened )
206+ {
207+ connection . Close ( ) ;
208+ }
209+ }
210+ } ) ;
178211
179212 /// <summary>
180213 /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -183,87 +216,61 @@ public override bool Exists()
183216 /// doing so can result in application failures when updating to a new Entity Framework Core release.
184217 /// </summary>
185218 public override Task < bool > ExistsAsync ( CancellationToken cancellationToken = default )
186- => Exists ( async: true , cancellationToken ) ;
187-
188- private async Task < bool > Exists ( bool async , CancellationToken cancellationToken = default )
189- {
190- var logger = connectionLogger ;
191- var startTime = DateTimeOffset . UtcNow ;
192-
193- var interceptionResult = async
194- ? await logger . ConnectionOpeningAsync ( connection , startTime , cancellationToken ) . ConfigureAwait ( false )
195- : logger . ConnectionOpening ( connection , startTime ) ;
196-
197- if ( interceptionResult . IsSuppressed )
219+ => Dependencies . ExecutionStrategy . ExecuteAsync ( async ct =>
198220 {
199- // If the connection attempt was suppressed by an interceptor, assume that the interceptor took care of all the opening
200- // details, and the database exists.
201- return true ;
202- }
221+ using var _ = new TransactionScope ( TransactionScopeOption . Suppress , TransactionScopeAsyncFlowOption . Enabled ) ;
222+ var opened = false ;
203223
204- // When checking whether a database exists, pooling must be off, otherwise we may
205- // attempt to reuse a pooled connection, which may be broken (this happened in the tests).
206- // If Pooling is off, but Multiplexing is on - NpgsqlConnectionStringBuilder.Validate will throw,
207- // so we turn off Multiplexing as well.
208- var unpooledCsb = new NpgsqlConnectionStringBuilder ( connection . ConnectionString ) { Pooling = false , Multiplexing = false } ;
224+ try
225+ {
226+ await connection . OpenAsync ( cancellationToken , errorsExpected : true ) . ConfigureAwait ( false ) ;
227+ opened = true ;
209228
210- using var _ = new TransactionScope ( TransactionScopeOption . Suppress , TransactionScopeAsyncFlowOption . Enabled ) ;
211- var unpooledRelationalConnection =
212- await connection . CloneWith ( unpooledCsb . ToString ( ) , async, cancellationToken ) . ConfigureAwait ( false ) ;
229+ await rawSqlCommandBuilder
230+ . Build ( "SELECT 1" )
231+ . ExecuteNonQueryAsync (
232+ new RelationalCommandParameterObject (
233+ connection ,
234+ parameterValues : null ,
235+ readerColumns : null ,
236+ Dependencies . CurrentContext . Context ,
237+ Dependencies . CommandLogger ,
238+ CommandSource . Migrations ) ,
239+ cancellationToken )
240+ . ConfigureAwait ( false ) ;
213241
214- try
215- {
216- if ( async )
242+ return true ;
243+ }
244+ catch ( Exception e ) when ( IsDoesNotExist ( e ) )
217245 {
218- await unpooledRelationalConnection . OpenAsync ( errorsExpected : true , cancellationToken : cancellationToken )
219- . ConfigureAwait ( false ) ;
246+ return false ;
220247 }
221- else
248+ finally
222249 {
223- unpooledRelationalConnection . Open ( errorsExpected : true ) ;
250+ if ( opened )
251+ {
252+ await connection . CloseAsync ( ) . ConfigureAwait ( false ) ;
253+ }
224254 }
255+ } , cancellationToken ) ;
225256
226- return true ;
227- }
228- catch ( PostgresException e )
257+ private static bool IsDoesNotExist ( Exception exception )
258+ => exception switch
229259 {
230- if ( IsDoesNotExist ( e ) )
231- {
232- return false ;
233- }
260+ // Login failed is thrown when database does not exist (See Issue #776)
261+ PostgresException { SqlState : "3D000" }
262+ => true ,
234263
235- throw ;
236- }
237- catch ( NpgsqlException e ) when (
238264 // This can happen when Npgsql attempts to connect to multiple hosts
239- e . InnerException is AggregateException ae && ae. InnerExceptions. Any( ie => ie is PostgresException pe && IsDoesNotExist ( pe ) ) )
240- {
241- return false;
242- }
243- catch ( NpgsqlException e ) when (
244- e . InnerException is IOException { InnerException : SocketException { SocketErrorCode : SocketError . ConnectionReset } } )
245- {
265+ NpgsqlException { InnerException : AggregateException ae } when ae . InnerExceptions . Any ( ie => ie is PostgresException { SqlState : "3D000" } )
266+ => true ,
267+
246268 // Pretty awful hack around #104
247- return false;
248- }
249- finally
250- {
251- if ( async)
252- {
253- await unpooledRelationalConnection . CloseAsync ( ) . ConfigureAwait ( false ) ;
254- await unpooledRelationalConnection . DisposeAsync ( ) . ConfigureAwait ( false ) ;
255- }
256- else
257- {
258- unpooledRelationalConnection . Close ( ) ;
259- unpooledRelationalConnection. Dispose ( ) ;
260- }
261- }
262- }
269+ NpgsqlException { InnerException : IOException { InnerException : SocketException { SocketErrorCode : SocketError . ConnectionReset } } }
270+ => true ,
263271
264- // Login failed is thrown when database does not exist (See Issue #776)
265- private static bool IsDoesNotExist ( PostgresException exception )
266- => exception . SqlState == "3D000" ;
272+ _ => false
273+ } ;
267274
268275 /// <summary>
269276 /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
0 commit comments