From 41d6bce67aab3295ca78b1d59c299683037bba94 Mon Sep 17 00:00:00 2001 From: Arthur Vickers Date: Thu, 18 Jun 2020 13:29:46 -0700 Subject: [PATCH] Avoid 10 second timeout when doing existence checks with SqlClient Fixes #7283 See also https://github.com/dotnet/efcore/issues/21327 --- .../Storage/RelationalConnection.cs | 29 +++++++++++++++---- .../Storage/Internal/SqlServerConnection.cs | 20 +++++++++++++ 2 files changed, 43 insertions(+), 6 deletions(-) diff --git a/src/EFCore.Relational/Storage/RelationalConnection.cs b/src/EFCore.Relational/Storage/RelationalConnection.cs index 7e382a2e090..beae06fd603 100644 --- a/src/EFCore.Relational/Storage/RelationalConnection.cs +++ b/src/EFCore.Relational/Storage/RelationalConnection.cs @@ -630,7 +630,7 @@ public virtual bool Open(bool errorsExpected = false) { CurrentTransaction?.Dispose(); ClearTransactions(clearAmbient: false); - OpenDbConnection(errorsExpected); + OpenInternal(errorsExpected); wasOpened = true; } @@ -668,7 +668,7 @@ public virtual async Task OpenAsync(CancellationToken cancellationToken, b } ClearTransactions(clearAmbient: false); - await OpenDbConnectionAsync(errorsExpected, cancellationToken).ConfigureAwait(false); + await OpenInternalAsync(errorsExpected, cancellationToken).ConfigureAwait(false); wasOpened = true; } @@ -701,7 +701,7 @@ private void ClearTransactions(bool clearAmbient) } } - private void OpenDbConnection(bool errorsExpected) + private void OpenInternal(bool errorsExpected) { var startTime = DateTimeOffset.UtcNow; var stopwatch = Stopwatch.StartNew(); @@ -712,7 +712,7 @@ private void OpenDbConnection(bool errorsExpected) { if (!interceptionResult.IsSuppressed) { - DbConnection.Open(); + OpenDbConnection(errorsExpected); } Dependencies.ConnectionLogger.ConnectionOpened(this, startTime, stopwatch.Elapsed); @@ -730,7 +730,15 @@ private void OpenDbConnection(bool errorsExpected) } } - private async Task OpenDbConnectionAsync(bool errorsExpected, CancellationToken cancellationToken) + /// + /// Template method that by default calls but can be overriden + /// by providers to make a different call instead. + /// + /// Indicates if the connection errors are expected and should be logged as debug message. + protected virtual void OpenDbConnection(bool errorsExpected) + => DbConnection.Open(); + + private async Task OpenInternalAsync(bool errorsExpected, CancellationToken cancellationToken) { var startTime = DateTimeOffset.UtcNow; var stopwatch = Stopwatch.StartNew(); @@ -743,7 +751,7 @@ var interceptionResult { if (!interceptionResult.IsSuppressed) { - await DbConnection.OpenAsync(cancellationToken).ConfigureAwait(false); + await OpenDbConnectionAsync(errorsExpected, cancellationToken).ConfigureAwait(false); } await Dependencies.ConnectionLogger.ConnectionOpenedAsync(this, startTime, stopwatch.Elapsed, cancellationToken) @@ -769,6 +777,15 @@ await Dependencies.ConnectionLogger.ConnectionErrorAsync( } } + /// + /// Template method that by default calls but can be overriden + /// by providers to make a different call instead. + /// + /// Indicates if the connection errors are expected and should be logged as debug message. + /// A to observe while waiting for the task to complete. + protected virtual Task OpenDbConnectionAsync(bool errorsExpected, CancellationToken cancellationToken) + => DbConnection.OpenAsync(cancellationToken); + private void HandleAmbientTransactions() { var current = Transaction.Current; diff --git a/src/EFCore.SqlServer/Storage/Internal/SqlServerConnection.cs b/src/EFCore.SqlServer/Storage/Internal/SqlServerConnection.cs index 9ca472e131d..cb28b251d0b 100644 --- a/src/EFCore.SqlServer/Storage/Internal/SqlServerConnection.cs +++ b/src/EFCore.SqlServer/Storage/Internal/SqlServerConnection.cs @@ -39,6 +39,26 @@ public SqlServerConnection([NotNull] RelationalConnectionDependencies dependenci { } + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + protected override void OpenDbConnection(bool errorsExpected) + { + // Note: Not needed for the Async overload: see https://github.com/dotnet/SqlClient/issues/615 + if (errorsExpected + && DbConnection is SqlConnection sqlConnection) + { + sqlConnection.Open(SqlConnectionOverrides.OpenWithoutRetry); + } + else + { + DbConnection.Open(); + } + } + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in