internal void ThrowExceptionAndWarning(TdsParserStateObject stateObj, bool callerHasConnectionLock = false, bool asyncClose = false) { Debug.Assert(!callerHasConnectionLock || _connHandler._parserLock.ThreadMayHaveLock(), "Caller claims to have lock, but connection lock is not taken"); SqlException exception = null; bool breakConnection; // This function should only be called when there was an error or warning. If there aren't any // errors, the handler will be called for the warning(s). If there was an error, the warning(s) will // be copied to the end of the error collection so that the user may see all the errors and also the // warnings that occurred. // can be deleted) SqlErrorCollection temp = stateObj.GetFullErrorAndWarningCollection(out breakConnection); Debug.Assert(temp.Count > 0, "TdsParser::ThrowExceptionAndWarning called with no exceptions or warnings!"); Debug.Assert(_connHandler != null, "TdsParser::ThrowExceptionAndWarning called with null connectionHandler!"); // Don't break the connection if it is already closed breakConnection &= (TdsParserState.Closed != _state); if (breakConnection) { if ((_state == TdsParserState.OpenNotLoggedIn) && (_connHandler.ConnectionOptions.MultiSubnetFailover || _loginWithFailover) && (temp.Count == 1) && ((temp[0].Number == TdsEnums.TIMEOUT_EXPIRED) || (temp[0].Number == TdsEnums.SNI_WAIT_TIMEOUT))) { // For Multisubnet Failover we slice the timeout to make reconnecting faster (with the assumption that the server will not failover instantaneously) // However, when timeout occurs we need to not doom the internal connection and also to mark the TdsParser as closed such that the login will be will retried breakConnection = false; Disconnect(); } else { _state = TdsParserState.Broken; } } Debug.Assert(temp != null, "TdsParser::ThrowExceptionAndWarning: 0 errors in collection"); if (temp != null && temp.Count > 0) { // Construct the exception now that we've collected all the errors string serverVersion = null; if (_state == TdsParserState.OpenLoggedIn) { serverVersion = _connHandler.ServerVersion; } exception = SqlException.CreateException(temp, serverVersion, _connHandler); } // call OnError outside of _ErrorCollectionLock to avoid deadlock if (exception != null) { if (breakConnection) { // report exception to pending async operation // before OnConnectionClosed overrides the exception // due to connection close notification through references var taskSource = stateObj._networkPacketTaskSource; if (taskSource != null) { taskSource.TrySetException(ADP.ExceptionWithStackTrace(exception)); } } if (asyncClose) { // Wait until we have the parser lock, then try to close var connHandler = _connHandler; Action<Action> wrapCloseAction = closeAction => { Task.Factory.StartNew(() => { connHandler._parserLock.Wait(canReleaseFromAnyThread: false); connHandler.ThreadHasParserLockForClose = true; try { closeAction(); } finally { connHandler.ThreadHasParserLockForClose = false; connHandler._parserLock.Release(); } }); }; _connHandler.OnError(exception, breakConnection, wrapCloseAction); } else { // Let close know that we already have the _parserLock bool threadAlreadyHadParserLockForClose = _connHandler.ThreadHasParserLockForClose; if (callerHasConnectionLock) { _connHandler.ThreadHasParserLockForClose = true; } try { // the following handler will throw an exception or generate a warning event _connHandler.OnError(exception, breakConnection); } finally { if (callerHasConnectionLock) { _connHandler.ThreadHasParserLockForClose = threadAlreadyHadParserLockForClose; } } } } }