public void ExecuteReaderWithUpdates(Action <DbDataReader, DbOperation> processRecord) #endif { lock (_stopLocker) { if (_disposing) { return; } _stopHandle.Reset(); } var useNotifications = _dbBehavior.StartSqlDependencyListener(); var delays = _dbBehavior.UpdateLoopRetryDelays; for (var i = 0; i < delays.Count; i++) { if (i == 0 && useNotifications) { // Reset the state to ProcessingUpdates if this is the start of the loop. // This should be safe to do here without Interlocked because the state is protected // in the other two cases using Interlocked, i.e. there should only be one instance of // this running at any point in time. _notificationState = NotificationState.ProcessingUpdates; } Tuple <int, int> retry = delays[i]; var retryDelay = retry.Item1; var retryCount = retry.Item2; for (var j = 0; j < retryCount; j++) { if (_disposing) { Stop(null); return; } if (retryDelay > 0) { Logger.LogDebug(String.Format("{0}Waiting {1}ms before checking for messages again", LoggerPrefix, retryDelay)); Thread.Sleep(retryDelay); } var recordCount = 0; try { recordCount = ExecuteReader(processRecord); Queried(); } catch (Exception ex) { Logger.LogError(String.Format("{0}Error in SQL receive loop: {1}", LoggerPrefix, ex)); Faulted(ex); } if (recordCount > 0) { Logger.LogDebug(String.Format("{0}{1} records received", LoggerPrefix, recordCount)); // We got records so start the retry loop again i = -1; break; } Logger.LogDebug("{0}No records received", LoggerPrefix); var isLastRetry = i == delays.Count - 1 && j == retryCount - 1; if (isLastRetry) { // Last retry loop iteration if (!useNotifications) { // Last retry loop and we're not using notifications so just stay looping on the last retry delay j = j - 1; } else { #if NET451 // No records after all retries, set up a SQL notification try { Logger.LogDebug("{0}Setting up SQL notification", LoggerPrefix); recordCount = ExecuteReader(processRecord, command => { _dbBehavior.AddSqlDependency(command, e => SqlDependency_OnChange(e, processRecord)); }); Queried(); if (recordCount > 0) { Logger.LogDebug("{0}Records were returned by the command that sets up the SQL notification, restarting the receive loop", LoggerPrefix); i = -1; break; // break the inner for loop } else { var previousState = Interlocked.CompareExchange(ref _notificationState, NotificationState.AwaitingNotification, NotificationState.ProcessingUpdates); if (previousState == NotificationState.AwaitingNotification) { Logger.LogError("{0}A SQL notification was already running. Overlapping receive loops detected, this should never happen. BUG!", LoggerPrefix); return; } if (previousState == NotificationState.NotificationReceived) { // Failed to change _notificationState from ProcessingUpdates to AwaitingNotification, it was already NotificationReceived Logger.LogDebug("{0}The SQL notification fired before the receive loop returned, restarting the receive loop", LoggerPrefix); i = -1; break; // break the inner for loop } } Logger.LogDebug("{0}No records received while setting up SQL notification", LoggerPrefix); // We're in a wait state for a notification now so check if we're disposing lock (_stopLocker) { if (_disposing) { _stopHandle.Set(); } } } catch (Exception ex) { Logger.LogError(String.Format("{0}Error in SQL receive loop: {1}", LoggerPrefix, ex)); Faulted(ex); // Re-enter the loop on the last retry delay j = j - 1; if (retryDelay > 0) { Logger.LogDebug(String.Format("{0}Waiting {1}ms before checking for messages again", LoggerPrefix, retryDelay)); Thread.Sleep(retryDelay); } } #endif } } } } Logger.LogDebug("{0}Receive loop exiting", LoggerPrefix); }
public void ExecuteReaderWithUpdates(Action <IDataRecord, DbOperation> processRecord) { lock (_stopLocker) { if (_disposing) { return; } _stopHandle.Reset(); } var useNotifications = _dbBehavior.StartSqlDependencyListener(); if (useNotifications) { _notificationState = NotificationState.ProcessingUpdates; } var delays = _dbBehavior.UpdateLoopRetryDelays; for (var i = 0; i < delays.Count; i++) { Tuple <int, int> retry = delays[i]; var retryDelay = retry.Item1; var retryCount = retry.Item2; for (var j = 0; j < retryCount; j++) { if (_disposing) { Stop(null); return; } if (retryDelay > 0) { Trace.TraceVerbose("{0}Waiting {1}ms before checking for messages again", TracePrefix, retryDelay); Thread.Sleep(retryDelay); } var recordCount = 0; try { recordCount = ExecuteReader(processRecord); if (OnQuery != null) { OnQuery(); } } catch (Exception ex) { Trace.TraceError("{0}Error in SQL receive loop: {1}", TracePrefix, ex); if (OnError != null) { OnError(ex); } } if (recordCount > 0) { // We got records so start the retry loop again i = -1; break; } Trace.TraceVerbose("{0}No records received", TracePrefix); var isLastRetry = i == delays.Count - 1 && j == retryCount - 1; if (isLastRetry) { // Last retry loop iteration if (!useNotifications) { // Last retry loop and we're not using notifications so just stay looping on the last retry delay j = j - 1; } else { // No records after all retries, set up a SQL notification try { Trace.TraceVerbose("{0}Setting up SQL notification", TracePrefix); recordCount = ExecuteReader(processRecord, command => { _dbBehavior.AddSqlDependency(command, e => SqlDependency_OnChange(e, processRecord)); }); if (OnQuery != null) { OnQuery(); } if (recordCount > 0 || Interlocked.CompareExchange(ref _notificationState, NotificationState.AwaitingNotification, NotificationState.ProcessingUpdates) == NotificationState.NotificationReceived) { Trace.TraceVerbose("{0}Records received while setting up SQL notification, restarting the recieve loop", TracePrefix); i = -1; break; // break the inner for loop } ; Trace.TraceVerbose("{0}No records received while setting up SQL notification", TracePrefix); // We're in a wait state for a notification now so check if we're disposing lock (_stopLocker) { if (_disposing) { _stopHandle.Set(); } } } catch (Exception ex) { Trace.TraceError("{0}Error in SQL receive loop: {1}", TracePrefix, ex); if (OnError != null) { OnError(ex); } // Re-enter the loop on the last retry delay j = j - 1; if (retryDelay > 0) { Trace.TraceVerbose("{0}Waiting {1}ms before checking for messages again", TracePrefix, retryDelay); Thread.Sleep(retryDelay); } } } } } } Trace.TraceVerbose("{0}Receive loop exiting", TracePrefix); }