override public void Close() { ConnectionState previousState = State; Guid operationId; Guid clientConnectionId; // during the call to Dispose() there is a redundant call to // Close(). because of this, the second time Close() is invoked the // connection is already in a closed state. this doesn't seem to be a // problem except for logging, as we'll get duplicate Before/After/Error // log entries if (previousState != ConnectionState.Closed) { operationId = s_diagnosticListener.WriteConnectionCloseBefore(this); // we want to cache the ClientConnectionId for After/Error logging, as when the connection // is closed then we will lose this identifier // // note: caching this is only for diagnostics logging purposes clientConnectionId = ClientConnectionId; } SqlStatistics statistics = null; Exception e = null; try { statistics = SqlStatistics.StartTimer(Statistics); Task reconnectTask = _currentReconnectionTask; if (reconnectTask != null && !reconnectTask.IsCompleted) { CancellationTokenSource cts = _reconnectionCancellationSource; if (cts != null) { cts.Cancel(); } AsyncHelper.WaitForCompletion(reconnectTask, 0, null, rethrowExceptions: false); // we do not need to deal with possible exceptions in reconnection if (State != ConnectionState.Open) { // if we cancelled before the connection was opened OnStateChange(DbConnectionInternal.StateChangeClosed); } } CancelOpenAndWait(); CloseInnerConnection(); GC.SuppressFinalize(this); if (null != Statistics) { ADP.TimerCurrent(out _statistics._closeTimestamp); } } catch (Exception ex) { e = ex; throw; } finally { SqlStatistics.StopTimer(statistics); // we only want to log this if the previous state of the // connection is open, as that's the valid use-case if (previousState != ConnectionState.Closed) { if (e != null) { s_diagnosticListener.WriteConnectionCloseError(operationId, clientConnectionId, this, e); } else { s_diagnosticListener.WriteConnectionCloseAfter(operationId, clientConnectionId, this); } } } }