A DbOperation that continues to execute over and over as new results arrive. Will attempt to use SQL Query Notifications, otherwise falls back to a polling receive loop.
Inheritance: DbOperation, IDisposable, IDbBehavior
        public void CallsOnErrorOnException()
        {
            // Arrange
            var mre = new ManualResetEventSlim(false);
            var onErrorCalled = false;
            var dbProviderFactory = new MockDbProviderFactory();
            var dbBehavior = new Mock<IDbBehavior>();
            var logger = new Mock<ILogger>();
            dbBehavior.Setup(db => db.UpdateLoopRetryDelays).Returns(_defaultRetryDelays);
            dbBehavior.Setup(db => db.StartSqlDependencyListener()).Returns(false);
            dbProviderFactory.MockDataReader.Setup(r => r.Read()).Throws(new ApplicationException("test"));
            var operation = new ObservableDbOperation("test", "test", logger.Object, dbProviderFactory, dbBehavior.Object);
            operation.Faulted += _ =>
            {
                onErrorCalled = true;
                mre.Set();
            };

            // Act
            ThreadPool.QueueUserWorkItem(_ => operation.ExecuteReaderWithUpdates((record, o) => { }));
            mre.Wait();
            operation.Dispose();

            // Assert
            Assert.True(onErrorCalled);
        }
        public void UseSqlNotificationsIfAvailable(bool supportSqlNotifications)
        {
            // Arrange
            var sqlDependencyAdded = false;
            var retryLoopCount = 0;
            var mre = new ManualResetEventSlim();
            var dbProviderFactory = new MockDbProviderFactory();
            var dbBehavior = new Mock<IDbBehavior>();
            dbBehavior.Setup(db => db.UpdateLoopRetryDelays).Returns(_defaultRetryDelays);
            dbBehavior.Setup(db => db.StartSqlDependencyListener()).Returns(supportSqlNotifications);
            dbBehavior.Setup(db => db.AddSqlDependency(It.IsAny<IDbCommand>(), It.IsAny<Action<SqlNotificationEventArgs>>()))
                .Callback(() =>
                {
                    sqlDependencyAdded = true;
                    mre.Set();
                });
            var operation = new ObservableDbOperation("test", "test", new TraceSource("test"), dbProviderFactory, dbBehavior.Object);
            operation.Faulted += _ => mre.Set();
            operation.Queried += () =>
            {
                retryLoopCount++;
                if (retryLoopCount > 1)
                {
                    mre.Set();
                }
            };

            // Act
            ThreadPool.QueueUserWorkItem(_ => operation.ExecuteReaderWithUpdates((record, o) => { }));
            mre.Wait();
            operation.Dispose();

            // Assert
            Assert.Equal(supportSqlNotifications, sqlDependencyAdded);
        }
        public void DoesRetryLoopConfiguredNumberOfTimes(int? length1, int? length2, int? length3)
        {
            // Arrange
            var retryLoopCount = 0;
            var mre = new ManualResetEventSlim();
            var retryLoopArgs = new List<int?>(new[] { length1, length2, length3 }).Where(l => l.HasValue);
            var retryLoopTotal = retryLoopArgs.Sum().Value;
            var retryLoopDelays = new List<Tuple<int, int>>(retryLoopArgs.Select(l => new Tuple<int, int>(0, l.Value)));
            var sqlDependencyCreated = false;
            var dbProviderFactory = new MockDbProviderFactory();
            var dbBehavior = new Mock<IDbBehavior>();
            dbBehavior.Setup(db => db.UpdateLoopRetryDelays).Returns(retryLoopDelays);
            dbBehavior.Setup(db => db.StartSqlDependencyListener()).Returns(true);
            dbBehavior.Setup(db => db.AddSqlDependency(It.IsAny<IDbCommand>(), It.IsAny<Action<SqlNotificationEventArgs>>()))
                .Callback(() =>
                {
                    sqlDependencyCreated = true;
                    mre.Set();
                });
            var operation = new ObservableDbOperation("test", "test", new TraceSource("test"), dbProviderFactory, dbBehavior.Object);
            operation.Faulted += _ => mre.Set();
            operation.Queried += () =>
            {
                if (!sqlDependencyCreated)
                {
                    // Only update the loop count if the SQL dependency hasn't been created yet (we're still in the loop)
                    retryLoopCount++;
                }
                if (retryLoopCount == retryLoopTotal)
                {
                    mre.Set();
                }
            };

            // Act
            ThreadPool.QueueUserWorkItem(_ => operation.ExecuteReaderWithUpdates((record, o) => { }));
            mre.Wait();
            operation.Dispose();

            // Assert
            Assert.Equal(retryLoopTotal, retryLoopCount);
        }
Esempio n. 4
0
        private void Receive(object state)
        {
            var tcs = (DispatchingTaskCompletionSource <object>)state;

            if (!_lastPayloadId.HasValue)
            {
                var lastPayloadIdOperation = new DbOperation(_connectionString, _maxIdSql, _trace)
                {
                    TracePrefix = _tracePrefix
                };

                try
                {
                    _lastPayloadId = (long?)lastPayloadIdOperation.ExecuteScalar();
                    Queried();

                    _trace.TraceVerbose("{0}SqlReceiver started, initial payload id={1}", _tracePrefix, _lastPayloadId);

                    // Complete the StartReceiving task as we've successfully initialized the payload ID
                    tcs.TrySetResult(null);
                }
                catch (Exception ex)
                {
                    _trace.TraceError("{0}SqlReceiver error starting: {1}", _tracePrefix, ex);

                    tcs.TrySetException(ex);
                    return;
                }
            }

            // NOTE: This is called from a BG thread so any uncaught exceptions will crash the process
            lock (this)
            {
                if (_disposed)
                {
                    return;
                }

                var parameter = _dbProviderFactory.CreateParameter();
                parameter.ParameterName = "PayloadId";
                parameter.Value         = _lastPayloadId.Value;

                _dbOperation = new ObservableDbOperation(_connectionString, _selectSql, _trace, parameter)
                {
                    TracePrefix = _tracePrefix
                };
            }

            _dbOperation.Queried += () => Queried();
            _dbOperation.Faulted += ex => Faulted(ex);
            _dbOperation.Changed += () =>
            {
                _trace.TraceInformation("{0}Starting receive loop again to process updates", _tracePrefix);

                _dbOperation.ExecuteReaderWithUpdates(ProcessRecord);
            };

            _trace.TraceVerbose("{0}Executing receive reader, initial payload ID parameter={1}", _tracePrefix, _dbOperation.Parameters[0].Value);

            _dbOperation.ExecuteReaderWithUpdates(ProcessRecord);

            _trace.TraceInformation("{0}SqlReceiver.Receive returned", _tracePrefix);
        }
        public void ExecuteReaderSetsNotificationStateCorrectlyWhenNotificationReceivedBeforeChangingStateToAwaitingNotification()
        {
            // Arrange
            var mre = new ManualResetEventSlim(false);
            var retryLoopDelays = new[] { Tuple.Create(0, 1) };
            var dbProviderFactory = new MockDbProviderFactory();
            var sqlDependencyAdded = false;
            var dbBehavior = new Mock<IDbBehavior>();
            dbBehavior.Setup(db => db.UpdateLoopRetryDelays).Returns(retryLoopDelays);
            dbBehavior.Setup(db => db.StartSqlDependencyListener()).Returns(true);
            dbBehavior.Setup(db => db.AddSqlDependency(It.IsAny<IDbCommand>(), It.IsAny<Action<SqlNotificationEventArgs>>()))
                      .Callback(() => sqlDependencyAdded = true);
            var operation = new ObservableDbOperation("test", "test", new TraceSource("test"), dbProviderFactory, dbBehavior.Object);
            dbProviderFactory.MockDataReader.Setup(r => r.Read()).Returns(() =>
            {
                if (sqlDependencyAdded)
                {
                    // Fake the SQL dependency firing while we're setting it up
                    operation.CurrentNotificationState = ObservableDbOperation.NotificationState.NotificationReceived;
                    sqlDependencyAdded = false;
                }
                return false;
            });
            long? stateOnLoopRestart = null;
            var queriedCount = 0;
            operation.Queried += () =>
            {
                queriedCount++;

                if (queriedCount == 3)
                {
                    // First query after the loop starts again, capture the state
                    stateOnLoopRestart = operation.CurrentNotificationState;
                    mre.Set();
                }
            };

            // Act
            ThreadPool.QueueUserWorkItem(_ => operation.ExecuteReaderWithUpdates((__, ___) => { }));

            mre.Wait();

            Assert.True(stateOnLoopRestart.HasValue);
            Assert.Equal(ObservableDbOperation.NotificationState.ProcessingUpdates, stateOnLoopRestart.Value);

            operation.Dispose();
        }
        public void ExecuteReaderSetsNotificationStateCorrectlyUpToAwaitingNotification()
        {
            // Arrange
            var retryLoopDelays = new [] { Tuple.Create(0, 1) };
            var dbProviderFactory = new MockDbProviderFactory();
            var dbBehavior = new Mock<IDbBehavior>();
            dbBehavior.Setup(db => db.UpdateLoopRetryDelays).Returns(retryLoopDelays);
            dbBehavior.Setup(db => db.StartSqlDependencyListener()).Returns(true);
            dbBehavior.Setup(db => db.AddSqlDependency(It.IsAny<IDbCommand>(), It.IsAny<Action<SqlNotificationEventArgs>>()));
            var operation = new ObservableDbOperation("test", "test", new TraceSource("test"), dbProviderFactory, dbBehavior.Object);
            operation.Queried += () =>
            {
                // Currently in the query loop
                Assert.Equal(ObservableDbOperation.NotificationState.ProcessingUpdates, operation.CurrentNotificationState);
            };

            // Act
            operation.ExecuteReaderWithUpdates((_, __) => { });

            // Assert
            Assert.Equal(ObservableDbOperation.NotificationState.AwaitingNotification, operation.CurrentNotificationState);

            operation.Dispose();
        }
Esempio n. 7
0
        private void Receive(object state)
        {
            var tcs = (TaskCompletionSource<object>)state;

            if (!_lastPayloadId.HasValue)
            {
                var lastPayloadIdOperation = new DbOperation(_connectionString, _maxIdSql, _logger)
                {
                    LoggerPrefix = _loggerPrefix
                };

                try
                {
                    _lastPayloadId = (long?)lastPayloadIdOperation.ExecuteScalar();
                    Queried();

                    _logger.LogVerbose(String.Format("{0}SqlReceiver started, initial payload id={1}", _loggerPrefix, _lastPayloadId));

                    // Complete the StartReceiving task as we've successfully initialized the payload ID
                    tcs.TrySetResult(null);
                }
                catch (Exception ex)
                {
                    _logger.LogError(String.Format("{0}SqlReceiver error starting: {1}", _loggerPrefix, ex));

                    tcs.TrySetException(ex);
                    return;
                }
            }

            // NOTE: This is called from a BG thread so any uncaught exceptions will crash the process
            lock (this)
            {
                if (_disposed)
                {
                    return;
                }

                var parameter = _dbProviderFactory.CreateParameter();
                parameter.ParameterName = "PayloadId";
                parameter.Value = _lastPayloadId.Value;

                _dbOperation = new ObservableDbOperation(_connectionString, _selectSql, _logger, parameter)
                {
                    LoggerPrefix = _loggerPrefix
                };
            }

            _dbOperation.Queried += () => Queried();
            _dbOperation.Faulted += ex => Faulted(ex);
#if DNX451
            _dbOperation.Changed += () =>
            {
                _logger.LogInformation("{0}Starting receive loop again to process updates", _loggerPrefix);

                _dbOperation.ExecuteReaderWithUpdates(ProcessRecord);
            };
#endif
            _logger.LogVerbose(String.Format("{0}Executing receive reader, initial payload ID parameter={1}", _loggerPrefix, _dbOperation.Parameters[0].Value));

            _dbOperation.ExecuteReaderWithUpdates(ProcessRecord);

            _logger.LogInformation("{0}SqlReceiver.Receive returned", _loggerPrefix);
        }
Esempio n. 8
0
        private void Receive(object state)
        {
            var tcs = (TaskCompletionSource<object>)state;

            if (!_lastPayloadId.HasValue)
            {
                var lastPayloadIdOperation = new DbOperation(_connectionString, _maxIdSql, _trace)
                {
                    TracePrefix = _tracePrefix
                };

                try
                {
                    _lastPayloadId = (long?)lastPayloadIdOperation.ExecuteScalar();
                    OnQuery();

                    // Complete the StartReceiving task as we've successfully initialized the payload ID
                    tcs.TrySetResult(null);
                }
                catch (Exception ex)
                {
                    tcs.TrySetException(ex);
                    return;
                }
            }

            // NOTE: This is called from a BG thread so any uncaught exceptions will crash the process
            lock (this)
            {
                if (_disposed)
                {
                    return;
                }

                var parameter = _dbProviderFactory.CreateParameter();
                parameter.ParameterName = "PayloadId";
                parameter.Value = _lastPayloadId.Value;

                _dbOperation = new ObservableDbOperation(_connectionString, _selectSql, _trace, parameter)
                {
                    OnError = ex => _onError(ex),
                    OnQuery = () => OnQuery(),
                    TracePrefix = _tracePrefix
                };
            }

            _dbOperation.ExecuteReaderWithUpdates((rdr, o) => ProcessRecord(rdr, o));

            _trace.TraceWarning("{0}SqlReceiver.Receive returned", _tracePrefix);
        }
        public void ExecuteReaderSetsNotificationStateCorrectlyWhenRecordsReceivedWhileSettingUpSqlDependency()
        {
            // Arrange
            var mre = new ManualResetEventSlim(false);
            var retryLoopDelays = new[] { Tuple.Create(0, 1) };
            var dbProviderFactory = new MockDbProviderFactory();
            var readCount = 0;
            var sqlDependencyAddedCount = 0;
            dbProviderFactory.MockDataReader.Setup(r => r.Read()).Returns(() => ++readCount == 2 && sqlDependencyAddedCount == 1);
            var dbBehavior = new Mock<IDbBehavior>();
            var logger = new Mock<ILogger>();
            dbBehavior.Setup(db => db.UpdateLoopRetryDelays).Returns(retryLoopDelays);
            dbBehavior.Setup(db => db.StartSqlDependencyListener()).Returns(true);
            dbBehavior.Setup(db => db.AddSqlDependency(It.IsAny<IDbCommand>(), It.IsAny<Action<SqlNotificationEventArgs>>())).Callback(() => sqlDependencyAddedCount++);
            var operation = new ObservableDbOperation("test", "test", logger.Object, dbProviderFactory, dbBehavior.Object);
            long? stateOnLoopRestart = null;
            var queriedCount = 0;
            operation.Queried += () =>
            {
                queriedCount++;

                if (queriedCount == 3)
                {
                    // First query after the loop starts again, check the state is reset
                    stateOnLoopRestart = operation.CurrentNotificationState;
                    mre.Set();
                }
            };

            // Act
            ThreadPool.QueueUserWorkItem(_ => operation.ExecuteReaderWithUpdates((__, ___) => { }));

            mre.Wait();

            Assert.True(stateOnLoopRestart.HasValue);
            Assert.Equal(ObservableDbOperation.NotificationState.ProcessingUpdates, stateOnLoopRestart.Value);

            operation.Dispose();
        }