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);
        }
Esempio n. 2
0
        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);
        }