public void LetsSeeWhetherWeCanRandomlyHitARaceCondition() { var ids = ObservableChangeSet.Create <long, long>(sourceCache => { return(Observable.Range(1, 1000000, Scheduler.Default).Subscribe(x => sourceCache.AddOrUpdate(x))); }, x => x); var itemsCache = new SourceCache <Thing, long>(x => x.Id); itemsCache.AddOrUpdate( new[] { new Thing { Id = 300, Name = "Quick" }, new Thing { Id = 600, Name = "Brown" }, new Thing { Id = 900, Name = "Fox" }, new Thing { Id = 1200, Name = "Hello" }, }); ids.InnerJoin(itemsCache.Connect(), x => x.Id, (_, thing) => thing).Subscribe((z) => { }, ex => { }, () => { }); }
public void LoadsAndDisposeFromObservableCache() { bool isDisposed = false; var observable = ObservableChangeSet.Create <Person, string>(cache => { return(() => { isDisposed = true; }); }, p => p.Name); observable.AsObservableCache().Dispose(); isDisposed.Should().BeTrue(); }
public static IObservable <IChangeSet <int> > FromTask(Func <Task <IEnumerable <int> > > loader) { return(ObservableChangeSet.Create <int>(async list => { var items = await loader(); list.AddRange(items); return () => { }; })); }
/// <summary> /// Create an observable change set from a task /// </summary> /// <returns></returns> public static IObservable <IChangeSet <int> > FromTask() { return(ObservableChangeSet.Create <int>(async list => { var items = await LoadFromTask(); list.AddRange(items); return () => { }; })); }
public static ViewModel.InteractiveCollectionViewModel <T, object> Build <T>(Func <T, object> getkey, IObservable <IEnumerable <T> > elems, IObservable <IFilter> filter, IObservable <T> DeletedSubject, IObservable <object> ClearedSubject, System.Reactive.Concurrency.DispatcherScheduler UI, IObservable <Func <T, object> > getkeys = null, bool isReadOnly = false) { var dx = Observable.Create <string>(_ => () => { }); ViewModel.InteractiveCollectionViewModel <T, object> interactivecollection = null; ISubject <Exception> exs = new Subject <Exception>(); var sx = ObservableChangeSet.Create(cache => { var dels = DeletedSubject /*.WithLatestFrom(RemoveSubject.StartWith(Remove).DistinctUntilChanged(), (d, r) => new { d, r })*/.Subscribe(_ => { try { cache.Remove(_); } catch (Exception ex) { Console.WriteLine("error removing " + _.ToString() + " from cache"); Console.WriteLine(ex.Message); exs.OnNext(ex); //ArgumentNullException } }); ClearedSubject.Subscribe(_ => cache.Clear()); elems.Subscribe(_ => { foreach (var g in _) { try { cache.AddOrUpdate(g); } catch (Exception ex) { Console.WriteLine("error adding " + g.ToString() + " from cache"); Console.WriteLine(ex); exs.OnNext(ex); } } }); return(new System.Reactive.Disposables.CompositeDisposable(dels)); }, getkey) .Filter(filter.Select(_ => { Func <T, bool> f = aa => _.Filter(aa); return(f); }).StartWith(ft)); getkeys?.Subscribe(_ => { sx.ChangeKey(_); }); interactivecollection = new ViewModel.InteractiveCollectionViewModel <T, object>(sx, UI, DeletedSubject, _ => (IConvertible)getkey(_)); exs.Subscribe(ex => (interactivecollection.Errors as System.Reactive.Subjects.ISubject <Exception>).OnNext(ex)); return(interactivecollection); }
/// <summary> /// Converts this directory's incoming change observables into a /// single <see cref="IChangeSet{DirectoryEntry}" /> observable. /// <para> /// This observer is then used to feed <see cref="Files" />, /// which in turn feeds the UI view of the directory contents. /// </para> /// </summary> /// <returns> /// An observable that reports changes in a directory in the form /// of <see cref="IChangeSet{DirectoryEntry}" /> objects. /// </returns> private IObservable <IChangeSet <DirectoryEntry> > DirectoryChanges() { return(ObservableChangeSet.Create <DirectoryEntry>(list => { var adder = ThisDirectoryFileAdd.Subscribe(x => list.Insert((int)x.Index, new DirectoryEntry(x.DirectoryId, x.Description))); var clearer = ThisDirectoryPrepare.Subscribe(_ => list.Clear()); return new CompositeDisposable(adder, clearer); })); }
/// <summary> /// Reload data and maintain list using edit diff which calculates a diff set from the previous load which can significantly reduce noise by poreventing /// unnecessary updates /// </summary> public static IObservable <IChangeSet <int> > ReloadableWithEditDiff(IObservable <Unit> loadObservable, Func <Task <IEnumerable <int> > > loader) { return(ObservableChangeSet.Create <int>(list => { return loadObservable .StartWith(Unit.Default) //ensure inital load .SelectMany(_ => loader()) .Subscribe(items => list.EditDiff(items, EqualityComparer <int> .Default)); })); }
public void Create() { Task <T> CreateTask <T>(T value) => Task.FromResult(value); SubscribeAndAssert(ObservableChangeSet.Create <int>(async list => { var value = await CreateTask <int>(10); list.Add(value); return(() => { }); })); }
public LocalLibraryService( ITracksRepository tracksRepository, ITrackFactory trackFactory, IPlaylistsRepository playlistsRepository, IPlaylistFactory playlistFactory) { this._tracksRepository = tracksRepository ?? throw new ArgumentNullException(nameof(tracksRepository)); this._trackFactory = trackFactory ?? throw new ArgumentNullException(nameof(trackFactory)); this._playlistsRepository = playlistsRepository ?? throw new ArgumentNullException(nameof(playlistsRepository)); this._playlistFactory = playlistFactory ?? throw new ArgumentNullException(nameof(playlistFactory)); this._playlistBaseChangesSubscription = new SerialDisposable().DisposeWith(this._disposables); this._tracksChangesSubscription = new SerialDisposable().DisposeWith(this._disposables); this.TracksChanges = ObservableChangeSet.Create <Track, uint>( async cache => { var items = await this._tracksRepository.GetAllAsync(); cache.AddOrUpdate(items); //return new CompositeDisposable( // //this._tracksRepository.TracksAddeded.Subscribe(addedItems => cache.Edit(cacheUpdater => cacheUpdater.AddOrUpdate(addedItems))), // //this._tracksRepository.TracksRemoved.Subscribe(addedItems => cache.Edit(cacheUpdater => cacheUpdater.Remove(addedItems))), // //this._tracksRepository.TracksUpdated.Subscribe(addedItems => cache.Edit(cacheUpdater => cacheUpdater.AddOrUpdate(addedItems))) // ); }, x => x.Id) // TODO: add synchronization to handle multiple subscriptions? .RefCount() //.Multicast(new ReplaySubject<IChangeSet<Track, uint>>()) //.AutoConnect(1, subscription => this._tracksChangesSubscription.Disposable = subscription) ; this.PlaylistsChanges = ObservableChangeSet.Create <PlaylistBase, uint>( async cache => { var items = await this._playlistsRepository.GetAllPlaylistsAsync(); cache.AddOrUpdate(items); //return new CompositeDisposable( //// this._playlistsRepository.Addeded.Subscribe(addedItems => cache.Edit(cacheUpdater => cacheUpdater.AddOrUpdate(addedItems))), //// this._playlistsRepository.Removed.Subscribe(addedItems => cache.Edit(cacheUpdater => cacheUpdater.Remove(addedItems))), //// this._playlistsRepository.Updated.Subscribe(addedItems => cache.Edit(cacheUpdater => cacheUpdater.AddOrUpdate(addedItems))) //); }, x => x.Id) .RefCount() //.Multicast(new ReplaySubject<IChangeSet<PlaylistBase, uint>>()) //.AutoConnect(1, subscription => this._playlistBaseChangesSubscription.Disposable = subscription) ; }
public void LoadsAndDisposeUsingDisposable() { bool isDisposed = false; SubscribeAndAssert(ObservableChangeSet.Create <Person, string>(cache => { Person[] people = Enumerable.Range(1, 100).Select(i => new Person($"Name.{i}", i)).ToArray(); cache.AddOrUpdate(people); return(Disposable.Create(() => { isDisposed = true; })); }, p => p.Name), checkContentAction: result => result.Count.Should().Be(100)); isDisposed.Should().BeTrue(); }
/// <summary> /// Create an observable change set from 2 observables i) the initial load 2) a subscriber /// </summary> public static IObservable <IChangeSet <int> > FromObservable(IObservable <IEnumerable <int> > initialLoad, IObservable <int> subscriptions) { return(ObservableChangeSet.Create <int>(list => { //in an enterprise app, would have to account for the gap between load and subscribe var initialSubscriber = initialLoad .Take(1) .Subscribe(list.AddRange); var subscriber = subscriptions .Subscribe(list.Add); return new CompositeDisposable(initialSubscriber, subscriber); })); }
public void LoadsAndDisposeUsingActionAsync() { Task <Person[]> CreateTask() => Task.FromResult(Enumerable.Range(1, 100).Select(i => new Person($"Name.{i}", i)).ToArray()); bool isDisposed = false; SubscribeAndAssert(ObservableChangeSet.Create <Person, string>(async cache => { var people = await CreateTask(); cache.AddOrUpdate(people); return(() => { isDisposed = true; }); }, p => p.Name), checkContentAction: result => result.Count.Should().Be(100)); isDisposed.Should().BeTrue(); }
public TracksService(TracksRepository tracksRepository) { this._tracksRepository = tracksRepository ?? throw new ArgumentNullException(nameof(tracksRepository)); this.TracksChangeSets = ObservableChangeSet.Create <Track, uint>( async sourceCache => { var load = this._tracksRepository.GetAllAsync(); var delayedLoad = Task.WhenAll(load, Task.Delay(TimeSpan.FromSeconds(5))); await delayedLoad; var tracks = await load; sourceCache.AddOrUpdate(tracks); }, b => b.Id) .RefCount(); }
public TrackViewModelsProxy() { this.TracksChangeSets = ObservableChangeSet.Create <Track, uint>( async sourceCache => { var tracks = await this.GetAllTracksAsync( TimeSpan.FromSeconds(3) ); sourceCache.AddOrUpdate(tracks); }, x => x.Id) .RefCount(); this.TrackViewModelsChangeSets = this.TracksChangeSets .Transform(b => new TrackViewModel(b)) .DisposeMany() .RefCount(); }
public LocalLibraryService( ITracksRepository tracksRepository, ITrackFactory trackFactory, IPlaylistsRepository playlistsRepository, IPlaylistFactory playlistFactory) { this._tracksRepository = tracksRepository ?? throw new ArgumentNullException(nameof(tracksRepository)); this._trackFactory = trackFactory ?? throw new ArgumentNullException(nameof(trackFactory)); this._playlistsRepository = playlistsRepository ?? throw new ArgumentNullException(nameof(playlistsRepository)); this._playlistFactory = playlistFactory ?? throw new ArgumentNullException(nameof(playlistFactory)); this.TracksChanges = ObservableChangeSet.Create <Track, uint>( async cache => { var items = await this.GetTracksAsync( //TimeSpan.FromSeconds(3) ); GC.Collect(); cache.AddOrUpdate(items); GC.Collect(); //return new CompositeDisposable( // this._tracksRepository.Addeded.Subscribe(addedItems => cache.Edit(cacheUpdater => cacheUpdater.AddOrUpdate(addedItems))), // this._tracksRepository.Removed.Subscribe(addedItems => cache.Edit(cacheUpdater => cacheUpdater.Remove(addedItems))), // this._tracksRepository.Updated.Subscribe(addedItems => cache.Edit(cacheUpdater => cacheUpdater.AddOrUpdate(addedItems)))); }, x => x.Id) // TODO: add synchronization to handle multiple subscriptions? .RefCount(); this.PlaylistsChanges = ObservableChangeSet.Create <PlaylistBase, uint>( async cache => { var items = await this._playlistsRepository.GetAllPlaylistsAsync(); cache.AddOrUpdate(items); //return new CompositeDisposable( // this._playlistsRepository.Addeded.Subscribe(addedItems => cache.Edit(cacheUpdater => cacheUpdater.AddOrUpdate(addedItems))), // this._playlistsRepository.Removed.Subscribe(addedItems => cache.Edit(cacheUpdater => cacheUpdater.Remove(addedItems))), // this._playlistsRepository.Updated.Subscribe(addedItems => cache.Edit(cacheUpdater => cacheUpdater.AddOrUpdate(addedItems)))); }, x => x.Id) .RefCount(); }
public ConnectionService() { mServerDiscovery = new ServerDiscovery(); AvailableServers = ObservableChangeSet.Create <Server, int>(list => { return(Observable.Interval(TimeSpan.FromSeconds(1)) .StartWith(0) .Select(_ => mServerDiscovery.Scan()) .Subscribe(servers => list.EditDiff(servers, (s1, s2) => s1.ProcessId == s2.ProcessId))); }, server => server.ProcessId); mConnectionSubject = new BehaviorSubject <IConnectionModel>(null); mProfilersLocation = Path.Combine( Path.GetDirectoryName( new Uri(Assembly.GetEntryAssembly().CodeBase).LocalPath), "profiler"); }
public void HandlesAsyncError() { Exception error = null; Task <IEnumerable <Person> > Loader() { throw new Exception("Broken"); } var observable = ObservableChangeSet.Create <Person, string>(async cache => { var people = await Loader(); cache.AddOrUpdate(people); return(() => { }); }, p => p.Name); using (var dervived = observable.AsObservableCache()) using (dervived.Connect().Subscribe(_ => { }, ex => error = ex)) { error.Should().NotBeNull(); } }
public virtual ViewModel.InteractiveCollectionViewModel <object, IConvertible> React(/*string key,*/ string childrenpath, IEnumerable enumerable, IObservable <bool> ischecked, System.Reactive.Concurrency.DispatcherScheduler UI, System.Windows.Threading.Dispatcher dispatcher) { var sx = ObservableChangeSet.Create <object, IConvertible>(cache => { foreach (var val in enumerable) { cache.AddOrUpdate(val); } return(System.Reactive.Disposables.Disposable.Empty); }, GetKey); var kx = new ViewModel.InteractiveCollectionViewModel <object, IConvertible>(sx, ChildrenPath, ischecked, ExpandSubject.StartWith(Expand).DistinctUntilChanged(), UI, dispatcher); kx.GetChecked(); kx.GetSelectedItem(ischecked).Subscribe(_ => { this.Dispatcher.InvokeAsync(() => SelectedItem = _, System.Windows.Threading.DispatcherPriority.Background, default(System.Threading.CancellationToken)); }); kx.GetCheckedChildItems(ischecked, childrenpath).Subscribe(_ => { this.Dispatcher.InvokeAsync(() => CheckedItems = _, System.Windows.Threading.DispatcherPriority.Background, default(System.Threading.CancellationToken)); }); AllCheckedItems = kx.@checked; //kx.GetSelected().WithLatestFrom(ischecked,(a,b)=>new { a, b }).Subscribe(_=> //{ // if (@checked.Contains(_) || _.b==false) // { // this.Dispatcher.InvokeAsync(() => SelectedItem = _.a, System.Windows.Threading.DispatcherPriority.Background, default(System.Threading.CancellationToken)); // this.Dispatcher.InvokeAsync(() => CheckedItems = ReflectionHelper.RecursivePropValues(_.a, childrenpath).Cast<object>().Where(a => @checked.Contains(a)).ToList(), System.Windows.Threading.DispatcherPriority.Background, default(System.Threading.CancellationToken)); // } //}); //kx.ChildSubject.Where(_=>_.Value.Interaction==Interaction.Select &&((int) _.Value.Value)>0).WithLatestFrom(ischecked, (a, b) => new { a, b }).Subscribe(_ => //{ // if (@checked.Contains(_.a.Key) || _.b == false) // { // this.Dispatcher.InvokeAsync(() => SelectedItem = _.a.Key, System.Windows.Threading.DispatcherPriority.Background, default(System.Threading.CancellationToken)); // this.Dispatcher.InvokeAsync(() => CheckedItems = ReflectionHelper.RecursivePropValues(_.a.Key, childrenpath).Cast<object>().Where(a => @checked.Contains(a)).ToList(), System.Windows.Threading.DispatcherPriority.Background, default(System.Threading.CancellationToken)); // } //}); //kx.ChildSubject.Where(_ => _.Value.Interaction == Interaction.Check).Subscribe(_ => //{ // if (!((bool)_.Value.Value)) // if (@checked.Contains(_.Key)) // { // @checked.Remove(_.Key); // this.Dispatcher.InvokeAsync(() => SelectedItem = null, System.Windows.Threading.DispatcherPriority.Background, default(System.Threading.CancellationToken)); // this.Dispatcher.InvokeAsync(() => CheckedItems = null, System.Windows.Threading.DispatcherPriority.Background, default(System.Threading.CancellationToken)); // } // else if (((bool)_.Value.Value)) // if (@unchecked.Contains(_.Key)) // { // @unchecked.Remove(_.Key); // this.Dispatcher.InvokeAsync(() => SelectedItem = _.Key, System.Windows.Threading.DispatcherPriority.Background, default(System.Threading.CancellationToken)); // this.Dispatcher.InvokeAsync(() => CheckedItems = ReflectionHelper.RecursivePropValues(_.Key, childrenpath).Cast<object>().Where(a => @checked.Contains(a)).ToList(), System.Windows.Threading.DispatcherPriority.Background, default(System.Threading.CancellationToken)); // } //}); //kx.DoubleClicked.Subscribe(_ => //{ // this.Dispatcher.InvokeAsync(() => DoubleClickedItem = _, System.Windows.Threading.DispatcherPriority.Background, default(System.Threading.CancellationToken)); //}); //SelectedItemSubject.Subscribe(_ => //kx.Deleted.Subscribe(_ => //{ // this.Dispatcher.InvokeAsync(() => Deleted = _, System.Windows.Threading.DispatcherPriority.Background, default(System.Threading.CancellationToken)); //}); return(kx); }
public LocalSuppliersService( IBillingUnitOfWorkFactory _billingUnitOfWorkFactory ) { this._billingUnitOfWorkFactory = _billingUnitOfWorkFactory ?? throw new ArgumentNullException(nameof(_billingUnitOfWorkFactory)); this._added_Subject = new Subject <SupplierDto>().DisposeWith(this._disposables); this._updated_Subject = new Subject <IReadOnlyCollection <SupplierDto> >().DisposeWith(this._disposables); this._removed_Subject = new Subject <IReadOnlyCollection <long> >().DisposeWith(this._disposables); //this._isBusy_BehaveiorSubject = new BehaviorSubject<bool>(false).DisposeWith(this._disposables); //this.IsBusyChanged = this._isBusy_BehaveiorSubject.DistinctUntilChanged(); this._suppliersChangesSubscription = new SerialDisposable().DisposeWith(this._disposables); var supplierDtoChangeSets = ObservableChangeSet.Create <SupplierDto, long>( async cache => { //await Task.Delay(3_000); var supplierDTOs = await this.GetAllAsync().ConfigureAwait(false); cache.AddOrUpdate(supplierDTOs); var crudSubscriptions = new CompositeDisposable(); this.Added .Subscribe(supplierDto => cache.Edit(cacheUpdater => cacheUpdater.AddOrUpdate(supplierDto))) .DisposeWith(crudSubscriptions); this.Updated .Subscribe(e => { cache.Edit(cacheUpdater => { var updates = e .Select( updatedSupplierDto => { var oldVersion = cacheUpdater.Lookup(updatedSupplierDto.Id); return(new Change <SupplierDto, long>( ChangeReason.Update, updatedSupplierDto.Id, updatedSupplierDto, oldVersion)); }) .ToArray(); var changes = new ChangeSet <SupplierDto, long>(updates); cacheUpdater.Clone(changes); }); }) .DisposeWith(crudSubscriptions); this.Removed .Subscribe(e => cache.Edit(cacheUpdater => cacheUpdater.Remove(e))) .DisposeWith(crudSubscriptions); return(crudSubscriptions); }, x => x.Id) //this.SuppliersChanges = supplierDtoChangeSets // //.RefCount() // //.Transform(supplier => this._supplierViewModelFactoryMethod.Invoke(supplier), new ParallelisationOptions(ParallelType.Parallelise)) // //.DisposeMany() // .Sort(SortExpressionComparer<SupplierDto>.Ascending(vm => vm.Name)) //.Multicast(new ReplaySubject<IChangeSet<SupplierDto, int>>()) //.AutoConnect(1, subscription => this._suppliersChangesSubscription.Disposable = subscription) ; this.Suppliers = supplierDtoChangeSets //.Sort(SortExpressionComparer<SupplierDto>.Ascending(vm => vm.Name)) .RefCount() .AsObservableCache(); // applyLocking: false); }