public ReplayLastPerKeySubject(Func <T, TKey> keySelector) { _keySelector = keySelector; _subjects = new ReplaySubject <ReplaySubject <T> >(); _mergedSubjects = _subjects.Merge(); _perKey = new Dictionary <TKey, ReplaySubject <T> >(); }
ReplaySubject <IObservable <TSource> > CreateInspectorSubject <TSource>() { var subject = new ReplaySubject <IObservable <TSource> >(1); Output = subject.Select(ys => ys.Select(xs => (object)xs)); Error = subject.Merge().IgnoreElements().Select(xs => Unit.Default); ErrorEx = subject.SelectMany(xs => xs .IgnoreElements() .Select(x => default(Exception)) .Catch <Exception, Exception>(ex => Observable.Return(ex))); return(subject); }
/// <summary> /// Fires a stream of RoleIds when you should reload. /// This happens whenever an Observer is active and the RoleId has changed. /// </summary> /// <returns> /// An Observable that fires the RoleId whenever data should be updated. /// </returns> protected IObservable<Guid> SetupExecuteObservable() { var loadQuery = Manager.Context.RoleIdObservable; var combinedObservationStateChanges = this.ObservationState; //loadQuery.Subscribe(s => Debug.WriteLine(String.Format("{0} RoleId {1}", queryKey, s))); //combinedObservationStateChanges.Subscribe(s => Debug.WriteLine(String.Format("{0} ObservationState {1}", queryKey, s))); //See http://stackoverflow.com/questions/7716114/rx-window-join-groupjoin for explanation var loadQueryUpdatesWhileInactive = new ReplaySubject<Guid>(1); var disposer = new SerialDisposable(); //the getSwitch will //a) publish loadQueryUpdates if the observation state is active //b) track loadQueryUpdates when ObservationState = suspended, then publish the loadQueryUpdate once active Func<ObservationState, IObservable<ObservationState>, IObservable<Guid>, IObservable<Guid>> getSwitch = (observationStateUpdate, observationStateUpdates, loadQueryUpdates) => { //a) because the observationState is active publish the loadQueryUpdatesWhileInactive & the loadQueryUpdates if (observationStateUpdate == Common.Models.ObservationState.Active) { //Merge the loadQueryUpdatesWhileInactive with the loadQueryUpdates return loadQueryUpdatesWhileInactive.Merge(loadQueryUpdates); } //b) because the ObservationState is suspended: // setup loadQueryUpdatesWhileInactive to keep track of the loadQueryUpdates while inactive //dispose the last loadQueryUpdatesWhileInactive subscription loadQueryUpdatesWhileInactive.Dispose(); //setup loadQueryUpdatesWhileInactive to track 1 (the last) loadQuery update loadQueryUpdatesWhileInactive = new ReplaySubject<Guid>(1); //dispose the temporary subscription to loadQueryUpdates disposer.Disposable = //track loadQueryUpdates until the next observationStateUpdates loadQueryUpdates.TakeUntil(observationStateUpdates) .Subscribe(loadQueryUpdatesWhileInactive); //setup loadQueryUpdatesWhileInactive //return an Empty Guid Observable so that executeQuery does not publish anything return Observable.Empty<Guid>(); }; //Create an Observable that fires the RoleId whenever data should be updated //The logic for when it should fire is defined in the getSwitch var executeQuery = combinedObservationStateChanges.DistinctUntilChanged() //whenever the combineObservationState changes .Publish(observationStateUpdates => loadQuery //publish the observationStateUpdates .Publish(loadQueryUpdates => //publish the loadQueryUpdates observationStateUpdates.Select( //select according to the getSwitch logic observationStateUpdate => getSwitch(observationStateUpdate, observationStateUpdates, loadQueryUpdates)))) .Switch() .Throttle(new TimeSpan(0, 0, 0, 0, 200));//Throttle for .2 second so it does not execute too often executeQuery.Subscribe(s => Debug.WriteLine(String.Format("Load data for {0} with RoleId {1}", GetType(), s))); return executeQuery; }