Example #1
0
        /// <summary>
        /// Projects the values from both notification channels into a new sequence.
        /// </summary>
        /// <typeparam name="TLeft">Type of the left notification channel.</typeparam>
        /// <typeparam name="TRight">Type of the right notification channel.</typeparam>
        /// <typeparam name="TLeftResult">Result type of the left notification channel.</typeparam>
        /// <typeparam name="TRightResult">Result type of the right notification channel.</typeparam>
        /// <param name="source">The observable from which values are projected.</param>
        /// <param name="leftSelector">Projects values from the left notification channel.</param>
        /// <param name="rightSelector">Projects value from the right notification channel.</param>
        /// <returns>An observable of results from the projection of values in both notification channels.</returns>
        public static IObservable <Either <TLeftResult, TRightResult> > Select <TLeft, TRight, TLeftResult, TRightResult>(
            this IObservable <Either <TLeft, TRight> > source,
            Func <TLeft, TLeftResult> leftSelector,
            Func <TRight, TRightResult> rightSelector)
        {
            Contract.Requires(source != null);
            Contract.Requires(leftSelector != null);
            Contract.Requires(rightSelector != null);
            Contract.Ensures(Contract.Result <IObservable <Either <TLeftResult, TRightResult> > >() != null);

            return(Observable2.CreateEither <TLeftResult, TRightResult>(
                       observer =>
            {
                return source.SubscribeEither(
                    left => observer.OnNextLeft(leftSelector(left)),
                    right => observer.OnNextRight(rightSelector(right)),
                    observer.OnError,
                    observer.OnCompleted);
            }));
        }
        /// <summary>
        /// Creates an observable with two pairwise notification channels containing values from the specified
        /// observable sequence projected by the specified function in the left channel and values projected
        /// by the other specified function in the right channel.
        /// </summary>
        /// <typeparam name="TSource">Type of the notifications in the source sequence.</typeparam>
        /// <typeparam name="TLeft">Type of the left notification channel.</typeparam>
        /// <typeparam name="TRight">Type of the right notification channel.</typeparam>
        /// <param name="source">The observable sequence from which values are projected.</param>
        /// <param name="leftSelector">Selects a value for the left channel from each value in the specified observable sequence.</param>
        /// <param name="rightSelector">Selects a value for the right channel from each value in the specified observable sequence.</param>
        /// <returns>The specified observable sequence projected into paired values produced by the specified selector functions.</returns>
        public static IObservable <Either <TLeft, TRight> > Pair <TSource, TLeft, TRight>(
            this IObservable <TSource> source,
            Func <TSource, TLeft> leftSelector,
            Func <TSource, TRight> rightSelector)
        {
            Contract.Requires(source != null);
            Contract.Requires(leftSelector != null);
            Contract.Requires(rightSelector != null);
            Contract.Ensures(Contract.Result <IObservable <Either <TLeft, TRight> > >() != null);

            return(Observable2.CreateEither <TLeft, TRight>(
                       observer =>
            {
                return source.Subscribe(
                    value =>
                {
                    observer.OnNextLeft(leftSelector(value));
                    observer.OnNextRight(rightSelector(value));
                },
                    observer.OnError,
                    observer.OnCompleted);
            }));
        }
        /// <summary>
        /// Creates an observable with two pairwise notification channels from the specified observable sequence
        /// by choosing which channels will receive each value based on the specified function.
        /// </summary>
        /// <typeparam name="TSource">The object that provides notification information.</typeparam>
        /// <param name="source">The observable from which values will be paired based on the specified selector function.</param>
        /// <param name="directionSelector">Selects the channels that will receive notifications for every value in the <paramref name="source"/>.</param>
        /// <returns>An observable sequence with two pairwise notification channels projected from the specified
        /// observable sequence based on the specified selector function.</returns>
        public static IObservable <Either <TSource, TSource> > Pair <TSource>(
            this IObservable <TSource> source,
            Func <TSource, PairDirection> directionSelector)
        {
            return(Observable2.CreateEither <TSource, TSource>(
                       observer =>
            {
                return source.Subscribe(
                    value =>
                {
                    switch (directionSelector(value))
                    {
                    case PairDirection.Left:
                        observer.OnNextLeft(value);
                        break;

                    case PairDirection.Right:
                        observer.OnNextRight(value);
                        break;

                    case PairDirection.Both:
                        observer.OnNextLeft(value);
                        observer.OnNextRight(value);
                        break;

                    case PairDirection.Neither:
                        break;

                    default:
                        throw new InvalidOperationException(Errors.InvalidPairDirectionValue);
                    }
                },
                    observer.OnError,
                    observer.OnCompleted);
            }));
        }
        public static IObservable <Either <IObservable <TSource>, TSource> > Introspect <TSource>(
            this IObservable <TSource> source,
            IScheduler scheduler)
        {
            Contract.Requires(source != null);
            Contract.Requires(scheduler != null);
            Contract.Ensures(Contract.Result <IObservable <Either <IObservable <TSource>, TSource> > >() != null);

            return(Observable2.CreateEither <IObservable <TSource>, TSource>(
                       observer =>
            {
                var subject = new Subject <Tuple <TSource, ISubject <TSource> > >();

                var observations = Subject.Synchronize(subject, scheduler);

                int pendingOnNext = 0;
                bool sourceCompleted = false;
                object gate = new object();

                var observationsSubscription = observations.Subscribe(
                    next =>
                {
                    var value = next.Item1;
                    var introspection = next.Item2;

                    try
                    {
                        lock (gate)
                        {
                            observer.OnNextRight(value);
                        }
                    }
                    catch (Exception ex)
                    {
                        introspection.OnError(ex);
                        return;
                    }

                    introspection.OnCompleted();

                    lock (gate)
                    {
                        if (--pendingOnNext == 0 && sourceCompleted)
                        {
                            observer.OnCompleted();
                        }
                    }
                },
                    ex =>
                {
                    lock (gate)
                    {
                        observer.OnError(ex);
                    }
                },
                    () =>
                {
                    lock (gate)
                    {
                        observer.OnCompleted();
                    }
                });

                var sourceSubscription = source.Subscribe(
                    value =>
                {
                    var introspection = new ReplaySubject <TSource>(1);

                    lock (gate)
                    {
                        observer.OnNextLeft(introspection.AsObservable());

                        pendingOnNext++;
                    }

                    introspection.OnNext(value);

                    observations.OnNext(Tuple.Create(value, (ISubject <TSource>)introspection));
                },
                    observations.OnError,
                    () =>
                {
                    bool completeNow = false;

                    lock (gate)
                    {
                        sourceCompleted = true;
                        completeNow = pendingOnNext == 0;
                    }

                    if (completeNow)
                    {
                        observations.OnCompleted();
                    }
                });

                return new CompositeDisposable(sourceSubscription, observationsSubscription, subject);
            }));
        }
        /// <summary>
        /// Creates an observable with two pairwise notification channels by combining the latest values of the specified
        /// observable sequences and choosing which channels will receive values based on the specified function.
        /// </summary>
        /// <typeparam name="TLeft">Type of the left notification channel.</typeparam>
        /// <typeparam name="TRight">Type of the right notification channel.</typeparam>
        /// <param name="leftSource">The observable sequence that provides notifications for the left channel.</param>
        /// <param name="rightSource">The observable sequence that provides notifications for the right channel.</param>
        /// <param name="directionSelector">Selects the channels that will receive notifications for every pair.</param>
        /// <returns>A new observable sequence containing the latest values in the specified observable sequences.</returns>
        public static IObservable <Either <TLeft, TRight> > Pair <TLeft, TRight>(
            this IObservable <TLeft> leftSource,
            IObservable <TRight> rightSource,
            Func <TLeft, TRight, PairDirection> directionSelector)
        {
            Contract.Requires(leftSource != null);
            Contract.Requires(rightSource != null);
            Contract.Requires(directionSelector != null);
            Contract.Ensures(Contract.Result <IObservable <Either <TLeft, TRight> > >() != null);

            return(Observable2.CreateEither <TLeft, TRight>(
                       observer =>
                       leftSource.Maybe()
                       .CombineLatest(
                           rightSource.Maybe(),
                           (left, right) => new
            {
                left,
                right
            })
                       .Subscribe(
                           pair =>
            {
                if (!pair.left.HasValue)
                {
                    if (pair.right.HasValue)
                    {
                        observer.OnNextRight(pair.right.Value);
                    }
                }
                else if (!pair.right.HasValue)
                {
                    observer.OnNextLeft(pair.left.Value);
                }
                else
                {
                    var left = pair.left.Value;
                    var right = pair.right.Value;

                    switch (directionSelector(left, right))
                    {
                    case PairDirection.Left:
                        observer.OnNextLeft(left);
                        break;

                    case PairDirection.Right:
                        observer.OnNextRight(right);
                        break;

                    case PairDirection.Both:
                        observer.OnNextLeft(left);
                        observer.OnNextRight(right);
                        break;

                    case PairDirection.Neither:
                        break;

                    default:
                        throw new InvalidOperationException(Errors.InvalidPairDirectionValue);
                    }
                }
            },
                           observer.OnError,
                           observer.OnCompleted)));
        }
Example #6
0
        /// <summary>
        /// Repeats the source observable sequence when it throws the specified type of exception
        /// consecutively using the specified back-off algorithm until it produces a value, successfully terminates
        /// or the specified count has been reached and pairs it with an error channel.
        /// </summary>
        /// <typeparam name="TSource">The object that provides notification information.</typeparam>
        /// <typeparam name="TException">The type of exception to catch.</typeparam>
        /// <param name="source">The observable to be repeated.</param>
        /// <param name="consecutiveRetryCount">The maximum number of times to retry the sequence consecutively
        /// when it's faulted.</param>
        /// <param name="backOffSelector">Selects the amount of time to delay before repeating when the sequence has faulted.</param>s
        /// <remarks>
        /// <see cref="RetryConsecutive{TSource,TException}(IObservable{TSource},int,Func{TException,int,TimeSpan})"/> is appropriate when permanent recovery is required for sequences
        /// that experience ephemeral consecutive errors at unpredictable intervals, such as those originating
        /// from network streams.  For example, it can produce a sequence that automatically reconnects upon
        /// consecutive network failures up to the specified <paramref name="consecutiveRetryCount"/> number of
        /// times; furthermore, if the sequence is able to successfully generate a value after an error, then
        /// the retry count is reset for subsequent consecutive failures.
        /// </remarks>
        /// <returns>The specified observable sequence with an error channel.</returns>
        /// <seealso href="http://en.wikipedia.org/wiki/Exponential_backoff">
        /// Exponential backoff
        /// </seealso>
        public static IObservable <Either <TSource, TException> > RetryConsecutive <TSource, TException>(
            this IObservable <TSource> source,
            int consecutiveRetryCount,
            Func <TException, int, TimeSpan> backOffSelector)
            where TException : Exception
        {
            Contract.Requires(source != null);
            Contract.Requires(consecutiveRetryCount >= 0);
            Contract.Requires(backOffSelector != null);
            Contract.Ensures(Contract.Result <IObservable <Either <TSource, TException> > >() != null);

            return(Observable2.CreateEither <TSource, TException>(
                       observer =>
            {
                int attemptCount = 1;
                bool decremented = false;
                bool resetRequired = false;

                var sources = Enumerable.Repeat(source, consecutiveRetryCount).GetEnumerator();

                return sources
                .Catch <TSource, TException>(
                    ex =>
                {
                    if (resetRequired)
                    {
                        if (!decremented)

                        /* This behavior matches the Rx behavior of the retryCount parameter in the Retry method.
                         * The first iteration always counts as the first "retry", even though technically it's
                         * not a "retry" because it's first.  (If consecutiveRetryCount is set to zero, then the
                         * sequence will end because the enumerator's MoveNext method called below will return false.)
                         */
                        {
                            Contract.Assume(consecutiveRetryCount > 0);

                            consecutiveRetryCount--;
                            decremented = true;
                        }

                        attemptCount = 1;
                        resetRequired = false;

                        sources = Enumerable.Repeat(source, consecutiveRetryCount).GetEnumerator();
                    }

                    return sources;
                },
                    ex => backOffSelector(ex, attemptCount++))
                .SubscribeEither(
                    value =>
                {
                    resetRequired = true;

                    observer.OnNextLeft(value);
                },
                    observer.OnNextRight,
                    observer.OnError,
                    observer.OnCompleted);
            }));
        }
Example #7
0
        public static IObservable <Either <TSource, TException> > OnErrorResumeNext <TSource, TException>(
            this IEnumerator <IObservable <TSource> > sources,
            Func <TException, IEnumerator <IObservable <TSource> > > handler,
            Func <TException, TimeSpan> backOffSelector)
            where TException : Exception
        {
            Contract.Requires(sources != null);
            Contract.Requires(handler != null);
            Contract.Requires(backOffSelector != null);
            Contract.Ensures(Contract.Result <IObservable <Either <TSource, TException> > >() != null);

            return(Observable2.CreateEither <TSource, TException>(
                       observer =>
            {
                bool movedNext;
                IObservable <TSource> current = null;

                Func <bool> moveNext = () =>
                {
                    try
                    {
                        movedNext = sources.MoveNext();

                        if (movedNext)
                        {
                            current = sources.Current;

                            Contract.Assume(current != null);

                            return true;
                        }
                    }
                    catch (Exception ex)
                    {
                        observer.OnError(ex);
                    }

                    return false;
                };

                if (!moveNext())
                {
                    observer.OnCompleted();

                    return sources;
                }

                var subscription = new SerialDisposable();
                var sourcesDisposable = new SerialDisposable();

                sourcesDisposable.Disposable = sources;

                var disposable = Scheduler.CurrentThread.Schedule(
                    TimeSpan.Zero,
                    self =>
                {
                    subscription.SetDisposableIndirectly(() =>
                                                         current.Subscribe(
                                                             observer.OnNextLeft,
                                                             ex =>
                    {
                        var typedError = ex as TException;

                        if (typedError == null)
                        {
                            observer.OnError(ex);
                        }
                        else
                        {
                            observer.OnNextRight(typedError);

                            IEnumerator <IObservable <TSource> > next;

                            try
                            {
                                next = handler(typedError);
                            }
                            catch (Exception ex2)
                            {
                                observer.OnError(ex2);
                                return;
                            }

                            Contract.Assume(next != null);

                            if (sources != next)
                            {
                                sources = next;

                                sourcesDisposable.Disposable = sources;
                            }

                            if (moveNext())
                            {
                                TimeSpan delay;

                                try
                                {
                                    delay = backOffSelector(typedError);
                                }
                                catch (Exception ex2)
                                {
                                    observer.OnError(ex2);
                                    return;
                                }

                                if (delay < TimeSpan.Zero)
                                /* Feature that allows callers to indicate when an exception is fatal based on its type */
                                {
                                    observer.OnError(ex);
                                }
                                else
                                {
                                    self(delay);
                                }
                            }
                            else
                            {
                                observer.OnCompleted();
                            }
                        }
                    },
                                                             () =>
                    {
                        if (moveNext())
                        {
                            self(TimeSpan.Zero);
                        }
                        else
                        {
                            observer.OnCompleted();
                        }
                    }));
                });

                return new CompositeDisposable(subscription, disposable, sourcesDisposable);
            }));
        }