/// <summary> /// Calls OnCompleted on all Subscribers, unsubscribes them, /// and stops receiving messages on the queue. /// </summary> /// <exception cref="System.InvalidOperationException"></exception> /// <returns>A result containing any Exceptions from removing Subscribers</returns> public StopReceivingResult StopReceiving() { if (_observers.IsNone || _workers.IsNone || _tokenSource.IsNone) { throw new InvalidOperationException($"{nameof(BrokerClient)} has not started receiving"); } var tokenSource = _tokenSource | CancellationTokenSource.CreateLinkedTokenSource(CancellationToken.None); var workers = _workers | new ConcurrentBag <Task>(); var observers = _observers | new ConcurrentBag <IBrokerObserver>(); return(new StopReceivingResult(Try(() => tokenSource.Cancel()).Try()) .Append(Try(() => Task.WaitAll(workers.ToArray(), tokenSource.Token)).Try()) .Append(Try(() => _logger.Debug(message: "Receiver stopped")).Try()) .Append(Try(() => tokenSource.Dispose()).Try()) .Append( _logger.Debug(observers, "Calling Observers OnCompleted").Aggregate( new StopReceivingResult(), (prev, next) => prev.Append(Try(() => next.OnCompleted()).Try()))) .Append( new StopReceivingResult(Try(() => { IBrokerObserver removing; while (observers.TryTake(out removing)) { } ; _logger.Debug("All Observers Removed"); }).Try()))); }
/// <summary> /// Adds error handling to IObserver.OnError /// </summary> /// <param name="self"></param> /// <param name="exception"></param> /// <param name="message"></param> /// <param name="logger"></param> /// <returns></returns> public static Unit SendError(this IBrokerObserver self, Exception exception, Maybe <BrokerMessage> message, Maybe <Log> logger) => Try(() => self.OnError(exception, message)) .Match(_ => _, e => logger.Error <Unit>( new AggregateException( exception, logger.Debug(e, $"{nameof(self.OnNext)} failed. Calling {nameof(self.OnError)}")).GetExceptionChainMessagesWithSql()));
/// <summary> /// Adds a new observer to receive future queue messages /// </summary> /// <remarks>Be aware that <see cref="IObserver{T}.OnNext(T)"/>, <see cref="IObserver{T}.OnError(Exception)"/>, /// and <see cref="IObserver{T}.OnCompleted()"/> are called asynchronously; /// so appropriate state sharing precautions should be taken to avoid race conditions.</remarks> /// <param name="observer">New addition that needs to implement <see cref="IBrokerObserver"/></param> /// <exception cref="System.ArgumentNullException"></exception> /// <exception cref="System.InvalidOperationException">When <paramref name="observer"/> is not an <see cref="IBrokerObserver"/></exception> /// <returns>A subscription that can be unsubscribed from by calling Dispose</returns> public IDisposable Subscribe(IBrokerObserver observer) { if (!_observers.Contains(observer.AssertValue())) { _observers.Add(observer); _logger.Debug($"Added {observer}"); } return(new Subscription(_observers, observer, _logger)); }
protected virtual void Dispose(bool disposing) { if (!disposedValue) { if (disposing && _observer != null && _observers != null && _observers.Contains(_observer)) { IBrokerObserver removing; if (_observers.TryTake(out removing)) { _logger.Debug($"Removed Observer: {ToString()}"); } else { _logger.Error($"Failed to remove Observer: {ToString()}"); } } disposedValue = true; } }
/// <summary> /// Adds error handling to IObserver.OnNext /// </summary> /// <param name="self"></param> /// <param name="next"></param> /// <param name="logger"></param> /// <returns></returns> public static Unit SendNext(this IBrokerObserver self, BrokerMessage next, Maybe <Log> logger) => Try(() => self.OnNext(next)).Match( _ => _, e => self.SendError(logger.Debug(e, $"{nameof(self.OnNext)} failed. Calling {nameof(self.OnError)}"), next, logger));