public void DoNotThrowAWobblyWhenRemovingaMutatedValue() { var pageController = new PageController(); var sortController = new SortController<TestVm>(SortExpressionComparer<TestVm>.Ascending(t => t.DateFavorited ?? DateTime.MinValue)); var filterController = new FilterController<TestVm>(myVm => myVm.Id != 0); var items = new ObservableCollectionExtended<TestVm>(); var itemCache = new SourceCache<TestVm, int>(myVm => myVm.Id); var item1 = new TestVm(1) { DateFavorited = DateTime.Now }; var item2 = new TestVm(2) { DateFavorited = DateTime.Now }; itemCache.AddOrUpdate(item1); itemCache.AddOrUpdate(item2); bool error = false; itemCache.Connect() .Filter(filterController) .Sort(sortController) .Page(pageController)//error doesnt occur with paging disabled .Bind(items) .Subscribe(changes => { }, ex => error = true); pageController.Change(new PageRequest(1, 100)); //NB: never errored if it was the first item which was removed item2.DateFavorited = null; itemCache.Remove(item2); //ERROR! Assert.IsFalse(error, "Error has been thrown"); }
public SearchInfoCollection(ISearchMetadataCollection searchMetadataCollection, IFileWatcher fileWatcher) { _metadataCollection = searchMetadataCollection; _fileWatcher = fileWatcher; //Add a complete file display All = fileWatcher.Latest.Index().Replay(1).RefCount(); //create a collection with 1 item, which is used to show entire file var systemSearches = new SourceCache<SearchInfo, CaseInsensitiveString>(t => (CaseInsensitiveString)t.SearchText); systemSearches.AddOrUpdate(new SearchInfo("<All>", All, SearchType.All)); //create a collection of all possible user filters var userSearches = searchMetadataCollection.Metadata .Connect(meta => meta.Filter) .IgnoreUpdateWhen((current,previous)=>current.Filter == previous.Filter) .Transform(meta => { var latest = _fileWatcher.Latest .Search(s => s.Contains(meta.SearchText, StringComparison.OrdinalIgnoreCase)) .Replay(1).RefCount(); return new SearchInfo(meta.SearchText, latest, SearchType.User); }); //combine te results into a single collection Searches = systemSearches.Connect() .Or(userSearches) .AsObservableCache(); _cleanUp = new CompositeDisposable(Searches, systemSearches); }
public SearchInfoCollection(ISearchMetadataCollection searchMetadataCollection, ISearchMetadataFactory searchMetadataFactory, IFileWatcher fileWatcher) { _metadataCollection = searchMetadataCollection; _searchMetadataFactory = searchMetadataFactory; _fileWatcher = fileWatcher; //Add a complete file display All = _fileWatcher.Latest.Index().Replay(1).RefCount(); //create a collection with 1 item, which is used to show entire file var systemSearches = new SourceCache<SearchInfo, string>(t => t.SearchText); systemSearches.AddOrUpdate(new SearchInfo("<All>", All, SearchType.All)); //create a collection of all possible user filters var userSearches = searchMetadataCollection.Metadata .Connect(meta => meta.Filter) .IgnoreUpdateWhen((current,previous)=> SearchMetadata.EffectsFilterComparer.Equals(current, previous)) .Transform(meta => { var latest = _fileWatcher.Latest .Search(meta.BuildPredicate()) .Replay(1).RefCount(); return new SearchInfo(meta.SearchText, latest, SearchType.User); }); //combine te results into a single collection Searches = systemSearches.Connect() .Or(userSearches) .AsObservableCache(); _cleanUp = new CompositeDisposable(Searches, systemSearches); }
public LineScroller(FileInfo file, IObservable<ILineProvider> latest, IObservable<ScrollRequest> scrollRequest, ILogger logger, IScheduler scheduler = null) { if (file == null) throw new ArgumentNullException(nameof(file)); if (latest == null) throw new ArgumentNullException(nameof(latest)); if (logger == null) throw new ArgumentNullException(nameof(logger)); logger.Info($"Constructing file tailer for {file.FullName}"); var lines = new SourceCache<Line, LineKey>(l=>l.Key); Lines = lines.AsObservableCache(); var locker = new object(); scrollRequest = scrollRequest.Synchronize(locker); var aggregator = latest.CombineLatest(scrollRequest, (currentLines, scroll) => currentLines.ReadLines(scroll).ToArray()) .Subscribe(currentPage => { var previous = lines.Items.ToArray(); var added = currentPage.Except(previous,Line.TextStartComparer).ToArray(); var removed = previous.Except(currentPage, Line.TextStartComparer).ToArray(); lines.Edit(innerCache => { if (removed.Any()) innerCache.Remove(removed); if (added.Any()) innerCache.AddOrUpdate(added); }); }); _cleanUp = new CompositeDisposable(Lines, lines, aggregator); }
public CombinedSearchMetadataCollection([NotNull] ISearchMetadataCollection metadataCollection, [NotNull] IGlobalSearchOptions globalSearchOptions) { if (metadataCollection == null) throw new ArgumentNullException(nameof(metadataCollection)); if (globalSearchOptions == null) throw new ArgumentNullException(nameof(globalSearchOptions)); Local = metadataCollection; Global = globalSearchOptions.Metadata; var cache = new SourceCache<SearchMetadata, string>(t => t.SearchText); ////Prioritise local before global and renumber var localItems = metadataCollection.Metadata.Connect().ToCollection().Select(items => items.ToArray()).StartWith(Enumerable.Empty<SearchMetadata>()); var globalItems = globalSearchOptions.Metadata.Metadata.Connect().ToCollection().Select(items => items.ToArray()).StartWith(Enumerable.Empty<SearchMetadata>()); var combiner = localItems.CombineLatest(globalItems, (local, global) => new {local, global}).Select(x => Combine(x.local, x.global)).Subscribe(uppdatedItems => { cache.Edit(innerCache => { var toRemove = innerCache.Items.Except(uppdatedItems,SearchMetadata.SearchTextComparer).ToArray(); innerCache.Remove(toRemove); innerCache.AddOrUpdate(uppdatedItems); }); }); Combined = cache.Connect().IgnoreUpdateWhen((current, previous) => current.Equals(previous)).AsObservableCache(); _cleanUp = new CompositeDisposable(Combined, cache, combiner); }
public void Does_Not_Reuse_Disposed_Wrappers() { var subject = new SourceCache <Widget, int>(w => w.Id); var activeObservable = new ObservableCollectionExtended <WidgetWrapper>(); var inactiveObservable = new ObservableCollectionExtended <WidgetWrapper>(); var activeFilter = new FilterController <WidgetWrapper>(wrapper => wrapper.Wrapped.Active); var inactiveFilter = new FilterController <WidgetWrapper>(wrapper => !wrapper.Wrapped.Active); subject.Connect() .Transform(w => new WidgetWrapper(w)) .Filter(activeFilter) .Bind(activeObservable) .DisposeMany() .Subscribe(); subject.Connect() .Transform(w => new WidgetWrapper(w)) .Filter(inactiveFilter) .Bind(inactiveObservable) .DisposeMany() .Subscribe(); var w1 = new Widget() { Id = 0, Active = true }; var w2 = new Widget() { Id = 1, Active = true }; subject.AddOrUpdate(w1); subject.AddOrUpdate(w2); Assert.AreEqual(2, activeObservable.Count); Assert.AreEqual(0, inactiveObservable.Count); Assert.IsFalse(activeObservable[0].IsDisposed); Assert.IsFalse(activeObservable[1].IsDisposed); //This needs to be done twice to trigger the behavior w1.Active = !w1.Active; activeFilter.Reevaluate(); inactiveFilter.Reevaluate(); w1.Active = !w1.Active; activeFilter.Reevaluate(); inactiveFilter.Reevaluate(); Assert.AreEqual(2, activeObservable.Count); Assert.False(activeObservable[0].IsDisposed); Assert.False(activeObservable[1].IsDisposed); }
public void TransformToNull() { using var source = new SourceCache <Person, string>(p => p.Name); using var results = new ChangeSetAggregator <PersonWithGender?, string>(source.Connect() .Transform((Func <Person, PersonWithGender?>)(p => null), new ParallelisationOptions(ParallelType.Parallelise))); source.AddOrUpdate(new Person("Adult1", 50)); results.Messages.Count.Should().Be(1, "Should be 1 updates"); results.Data.Count.Should().Be(1, "Should be 1 item in the cache"); results.Data.Items.First().Should().Be(null, "Should be same person"); }
public CatalogProvider( IChatLoader chatLoader, IChatUpdater chatUpdater ) { _chatLoader = chatLoader; _chatUpdater = chatUpdater; _entryStore = new Dictionary <long, EntryModel>(); _chats = new SourceCache <EntryModel, long>(m => m.Id); }
public void Filter_ItemsBeforeSubscribe() { using (var list = new SourceCache <Person, string>(i => i.Name)) { foreach (var item in People) { list.AddOrUpdate(item); } list.Connect().Filter(v => v.Age % 2 == 0).Subscribe(v => { }); } }
public void StocksEmptyResultTotalNumberZeroTotalMarketValueZeroTotalStockWeightZero() { var scheduler = new TestScheduler(); var stocks = new SourceCache <Stock, long>(t => t.Id); var fundStocks = new SourceCache <Stock, string>(t => t.Type.ToString() + t.Id); var target = new BaseSummaryService <long, string>(stocks, fundStocks, scheduler); Assert.AreEqual(0, target.TotalMarketValue); Assert.AreEqual(0, target.TotalNumber); Assert.AreEqual(0, target.TotalStockWeight); }
public void TransformManyWithKey() { var children = Enumerable.Range(1, 100).Select(i => new Person("Name" + i, i)).ToArray(); int childIndex = 0; var parents = Enumerable.Range(1, 50) .Select(i => { var parent = new Parent(i, new[] { children[childIndex], children[childIndex + 1] }); childIndex = childIndex + 2; return(parent); }).ToArray(); using (var source = new SourceCache <Parent, int>(x => x.Id)) using (var aggregator = source.Connect() .TransformMany(p => p.Children, c => c.Name) .AsAggregator()) { source.AddOrUpdate(parents); aggregator.Data.Count.Should().Be(100); //add a child to an observable collection and check the new item is added parents[0].Children.Add(new Person("NewlyAddded", 100)); aggregator.Data.Count.Should().Be(101); ////remove first parent and check children have gone source.RemoveKey(1); aggregator.Data.Count.Should().Be(98); //check items can be cleared and then added back in var childrenInZero = parents[1].Children.ToArray(); parents[1].Children.Clear(); aggregator.Data.Count.Should().Be(96); parents[1].Children.AddRange(childrenInZero); aggregator.Data.Count.Should().Be(98); //replace produces an update var replacedChild = parents[1].Children[0]; parents[1].Children[0] = new Person("Replacement", 100); aggregator.Data.Count.Should().Be(98); aggregator.Data.Lookup(replacedChild.Name).HasValue.Should().BeFalse(); aggregator.Data.Lookup("Replacement").HasValue.Should().BeTrue(); } }
public void Grouping() { var domainCache = new SourceCache <DomainDto, int>(d => d.Id); var projectCache = new SourceCache <ProjectDto, int>(p => p.Id); //if domain has a project id, it is very efficient to use Group var domainWithInnerGroup = domainCache.Connect().Group(d => d.ProjectId); IObservable <IChangeSet <ProjectWithDomainCache, int> > combind = projectCache.Connect() .InnerJoin(domainWithInnerGroup, domain => domain.Key, (key, left, right) => new ProjectWithDomainCache(left, right.Cache)); }
public void ReapplyFilterDoesntThrow() { using (var source = new SourceCache <Person, string>(p => p.Key)) { source.AddOrUpdate(Enumerable.Range(1, 100).Select(i => new Person("P" + i, i)).ToArray()); var ex = Record.Exception(() => source.Connect() .Filter(Observable.Return(Unit.Default)) .AsObservableCache()); Assert.Null(ex); } }
private void LoadLinkedGames() { _linkedGames = new SourceCache <LinkedGameModel, Guid>(p => p.Id); foreach (var linkedGameEntity in _linkedGameAccessor.GetAll()) { var linkedGame = _mapper.Map <LinkedGameModel>(linkedGameEntity); _linkedGames.AddOrUpdate(linkedGame); } LinkedGames.WhenAnyPropertyWithAttributeChanged(typeof(ReactiveAttribute)) .Subscribe(p => AddOrUpdate(p).Wait()); }
public TradeTask(IEnumerable <OrderTrade> exchangeTrades = null) { Trades = new SourceCache <OrderTrade, string>(x => x.Id); Events = new Dictionary <DateTime, string>(); if (exchangeTrades != null) { Trades.Edit(innerList => innerList.AddOrUpdate(exchangeTrades)); } Jobs = new List <OrderTask>(); FinishedJobs = new Queue <OrderTask>(); Events = new Dictionary <DateTime, string>(); }
public EmployeesService(IDbContextFactory <SampleAvaloniaApplicationClientContext> contextFactory, IMapper mapper) { _mapper = mapper; _contextFactory = contextFactory; _employees = new SourceCache <EmployeeModel, Guid>(e => e.Id); using (var context = _contextFactory.Create()) { _employees.AddOrUpdate(_mapper.ProjectTo <EmployeeModel>(context.Employees)); } }
public void OnItemAddCalled() { var called = false; var source = new SourceCache <Person, int>(x => x.Age); source.Connect().OnItemAdded(_ => called = true).Subscribe(); var person = new Person("A", 1); source.AddOrUpdate(person); Assert.True(called); }
public ChangeSetToCache(Func <TValue, TKey> keySelector, IObservable <IChangeSet <TValue> > set) { _cache = new SourceCache <TValue, TKey>(keySelector); _subscription = set.OnItemAdded(_cache.AddOrUpdate).OnItemRemoved(_cache.Remove).Subscribe(); //_subscription = set.Receive(cs => //{ // foreach (var change in cs) // { // switch (change.Reason) // { // case ListChangeReason.Add: // _cache.AddOrUpdate(change.Item.Current); // break; // case ListChangeReason.AddRange: // _cache.AddOrUpdate(change.Range); // break; // case ListChangeReason.Replace: // change.Item.Previous.IfHasValue(tv => _cache.Remove(tv)); // _cache.AddOrUpdate(change.Item.Current); // break; // case ListChangeReason.Remove: // _cache.Remove(change.Item.Current); // break; // case ListChangeReason.RemoveRange: // _cache.Remove(change.Range); // break; // case ListChangeReason.Refresh: // switch (change.Type) // { // case ChangeType.Item: // _cache.Refresh(change.Item.Current); // break; // case ChangeType.Range: // _cache.Refresh(change.Range); // break; // default: // throw new ArgumentOutOfRangeException(); // } // break; // case ListChangeReason.Clear: // _cache.Clear(); // break; // case ListChangeReason.Moved: // break; // default: // throw new ArgumentOutOfRangeException(); // } // } //}); }
public LineScroller(FileInfo file, IObservable <ILineProvider> latest, IObservable <ScrollRequest> scrollRequest, ILogger logger, IScheduler scheduler = null) { if (file == null) { throw new ArgumentNullException(nameof(file)); } if (latest == null) { throw new ArgumentNullException(nameof(latest)); } if (logger == null) { throw new ArgumentNullException(nameof(logger)); } logger.Info($"Constructing file tailer for {file.FullName}"); var lines = new SourceCache <Line, LineKey>(l => l.Key); Lines = lines.AsObservableCache(); var locker = new object(); scrollRequest = scrollRequest.Synchronize(locker); var aggregator = latest.CombineLatest(scrollRequest, (currentLines, scroll) => currentLines.ReadLines(scroll).ToArray()) .RetryWithBackOff <Line[], Exception>((ex, i) => TimeSpan.FromSeconds(1)) .Subscribe(currentPage => { var previous = lines.Items.ToArray(); var added = currentPage.Except(previous, Line.TextStartComparer).ToArray(); var removed = previous.Except(currentPage, Line.TextStartComparer).ToArray(); lines.Edit(innerCache => { if (removed.Any()) { innerCache.Remove(removed); } if (added.Any()) { innerCache.AddOrUpdate(added); } }); }); _cleanUp = new CompositeDisposable(Lines, lines, aggregator); }
public void AddModpack() { new TestScheduler().With(scheduler => { var cache = new SourceCache <Modpack, Guid>(_ => Guid.NewGuid()); var vm = new LibraryViewModel(cache); vm.Modpacks.Should().BeEmpty(); cache.AddOrUpdate(new Modpack(new Common.Models.DTO.Repo.Modpack())); scheduler.AdvanceBy(1); vm.Modpacks.Should().HaveCount(1); }); }
public void RefreshAllTest() { using (var refresher = new Subject <Unit>()) using (var cache = new SourceCache <Valuable <string>, Guid>(x => x.Key)) using (var aggregator = cache.Connect().AutoRefreshOnObservable(x => refresher).AsAggregator()) { cache.AddOrUpdate(_items); refresher.OnNext(Unit.Default); Assert.AreEqual(101, aggregator.Messages.Count); EnumerableAssert.All(aggregator.Messages.SelectMany(x => x), x => x.Reason == ChangeReason.Refresh); } }
public Domain0Model() { UserProfiles = new SourceCache <UserProfile, int>(x => x.Id); Roles = new SourceCache <Role, int>(x => x.Id.Value); Permissions = new SourceCache <Permission, int>(x => x.Id.Value); Applications = new SourceCache <Application, int>(x => x.Id.Value); Environments = new SourceCache <Environment, int>(x => x.Id.Value); MessageTemplates = new SourceCache <MessageTemplate, int>(x => x.Id.Value); RolePermissions = new SourceList <RolePermission>(); UserRoles = new SourceList <UserRole>(); UserPermissions = new SourceList <UserPermission>(); }
public void AddPresynaptic(SourceCache <NeuronDto, int> cache, NeuronDto neuronDto) { var presyn = new NeuronDto( Guid.NewGuid().GetHashCode(), neuronDto.Id, Guid.NewGuid().ToString(), neuronDto.ParentNeuronId, "New Presynaptic", ChildType.Presynaptic ); cache.AddOrUpdate(presyn); }
private void LoadPlays() { _plays = new SourceCache <PlayModel, Guid>(p => p.Id); foreach (var entity in _playAccessor.GetAll()) { var play = _mapper.Map <PlayModel>(entity); _plays.AddOrUpdate(play); } Plays.WhenAnyPropertyWithAttributeChanged(typeof(ReactiveAttribute)) .Subscribe(p => AddOrUpdate(p).Wait()); }
public void ImageExplorerViewModel_SyncsIntialItemsFromCache_WhenItemsAvailable() => new TestScheduler().With(scheduler => { var source = new SourceCache <ImageHandle, string>(x => x.LoadingSettings.FilePath); source.AddOrUpdate(GetDummyHandle("foo")); var sut = new ImageExplorerViewModel(source.AsObservableCache()); sut.Activator.Activate(); scheduler.AdvanceBy(2); Assert.Equal(1, sut.Children.Count); });
public void GroupTest_Add() { using (var cache = new SourceCache <Groupable <int, string>, Guid>(x => x.Key)) using (var aggregator = cache.Connect() .Group(x => x.GroupKey) .AsAggregator()) { cache.AddOrUpdate(_items); cache.AddOrUpdate(new Groupable <int, string>(1, "Group 2")); Assert.AreEqual(2, aggregator.Messages.Count); } }
public void PopulateCache_Observers() { var items = Enumerable.Range(1, 10_000).Select(j => new Person("P" + j, j)).ToList(); var cache = new SourceCache <Person, string>(p => p.Name); var subscribed = cache.Connect().Subscribe(); var result = Allocations.Run(() => { cache.AddOrUpdate(items); }); Console.WriteLine(result); }
public void Setup() { _scheduler = new TestScheduler(); var routingState = new RoutingState(); var screenMock = new Mock <IScreen>() { DefaultValue = DefaultValue.Mock }; screenMock.SetupGet(x => x.Router).Returns(routingState); _screen = screenMock.Object; _movieService = new Mock <IMovieService>() { DefaultValue = DefaultValue.Mock }; _movieService.Setup(x => x.LoadUpcomingMovies(It.Is <int>(y => y >= 0 && y < 100))).Returns(() => Observable.Return(Unit.Default)); _movieService.Setup(x => x.LoadUpcomingMovies(It.Is <int>(y => y >= 100))).Returns(() => throw new Exception("Boom!")); var movies = new List <Movie>(); for (int i = 0; i < 20; i++) { movies.Add(new Movie { Id = i, Overview = $"Overview {i}", PosterBig = "PosterPath/", PosterSmall = "PosterPath/", ReleaseDate = DateTime.Now, Title = $"Title {i}" }); } _moviesSourceCache = new SourceCache <Movie, int>(x => x.Id); _moviesSourceCache.AddOrUpdate(movies); _movieService.Setup(x => x.UpcomingMovies).Returns(_moviesSourceCache); _target = new UpcomingMoviesListViewModel(_scheduler, _scheduler, _movieService.Object, _screen); _alertOutput = null; _target.ShowAlert.RegisterHandler(handler => { _alertOutput = handler.Input; handler.SetOutput(Unit.Default); }); _screen.Router.NavigateAndReset.Execute(_target); }
public void GroupTest_Update() { using (var cache = new SourceCache <Groupable <int, string>, Guid>(x => x.Key)) using (var aggregator = cache.Connect() .Group(x => x.GroupKey) .AsAggregator()) { cache.AddOrUpdate(_items); cache.AddOrUpdate(_items[0]); Assert.AreEqual(2, aggregator.Messages.Count); EnumerableAssert.All(aggregator.Messages.SelectMany(x => x), x => x.Reason == ChangeReason.Refresh); } }
public void Activate() { if (active != ActiveState.NotStarted) throw new InvalidOperationException("Wrong state"); active = ActiveState.Active; dbg = zm.Debug(); src = new SourceCache(sourcePath); io.PutString("ZLR Debugger\n"); dbg.Restart(); ShowStatus(); }
protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { // dispose this.cache.Dispose(); } // large fields, unmanaged this.cache = null; _disposedValue = true; } }
public static bool TryLookup <TValue, TKey>(this SourceCache <TValue, TKey> cache, TKey key, [MaybeNullWhen(false)] out TValue value) { var option = cache.Lookup(key); if (option.HasValue) { value = option.Value; return(true); } value = default; return(false); }
public PriceService() { _availableEntitiesCache = new SourceCache <IPrice, String>(price => price.EntityCacheKey); _availableEntities = _availableEntitiesCache.AsObservableCache(); TestHelper.Assets.ForEach(asset => _availableEntitiesCache.AddOrUpdate(new Price(DateTime.Now.Ticks, asset, asset.StartingPrice))); Observable .Interval(TimeSpan.FromMilliseconds(500)) .Subscribe((_) => { var price = CreatePrice(); _availableEntitiesCache.AddOrUpdate(price); }); }
public Cp77Controller(ILoggerService loggerService, IProjectManager projectManager, ISettingsManager settingsManager, IHashService hashService, ModTools modTools ) { _loggerService = loggerService; _projectManager = projectManager; _settingsManager = settingsManager; _hashService = hashService; _modTools = modTools; _rootCache = new SourceCache <GameFileTreeNode, string>(t => t.FullPath); }
public void ChangeData(SourceCache <NeuronDto, int> cache, NeuronDto dto, string value) { // TODO: update data source // TODO: retrieve updated value and replace value in children var newValue = new NeuronDto( dto.Id, dto.ParentId, dto.NeuronId, dto.ParentNeuronId, value, dto.Type ); cache.AddOrUpdate(newValue); }
public void GroupTest_RefreshGroupsWithoutGroupKeyChanged() { using (var refresher = new Subject <Unit>()) using (var cache = new SourceCache <Groupable <int, string>, Guid>(x => x.Key)) using (var aggregator = cache.Connect() .Group(x => x.GroupKey, refresher) .AsAggregator()) { cache.AddOrUpdate(_items); refresher.OnNext(Unit.Default); Assert.AreEqual(1, aggregator.Messages.Count); EnumerableAssert.None(aggregator.Messages.SelectMany(x => x), x => x.Reason == ChangeReason.Refresh); } }
public void ErrorUpdatingStreamIsHandled() { bool completed = false; bool error = false; var feeder = new SourceCache<ErrorInKey, int>(p=>p.Key); var subscriber = feeder.Connect().Finally(() => completed = true) .Subscribe(updates => { Console.WriteLine(); }, ex => error = true); feeder.BatchUpdate(updater => updater.AddOrUpdate(new ErrorInKey())); subscriber.Dispose(); Assert.IsTrue(error, "Error has not been invoked"); Assert.IsTrue(completed, "Completed has not been called"); }
public void Initialise() { _source = new SourceCache<Person, string>(p => p.Name); _accumulator = _source.Connect().ForAggregation() .Scan(0, (current, items) => { items.ForEach(x => { if (x.Type == AggregateType.Add) current = current + x.Item.Age; else current = current - x.Item.Age; }); return current; }); }
public LineScroller([NotNull] IObservable<ILineProvider> latest, [NotNull] IObservable<ScrollRequest> scrollRequest) { if (latest == null) throw new ArgumentNullException(nameof(latest)); if (scrollRequest == null) throw new ArgumentNullException(nameof(scrollRequest)); var lines = new SourceCache<Line, LineKey>(l => l.Key); Lines = lines.AsObservableCache(); var locker = new object(); scrollRequest = scrollRequest.Synchronize(locker); latest = latest.Synchronize(locker); var aggregator = latest .CombineLatest(scrollRequest, (currentLines, scroll) => { try { var x = currentLines.ReadLines(scroll).ToArray(); return x; } catch (Exception ex) { throw ex; } }) .Subscribe(currentPage => { var previous = lines.Items.ToArray(); var added = currentPage.Except(previous, Line.TextStartComparer).ToArray(); var removed = previous.Except(currentPage, Line.TextStartComparer).ToArray(); lines.Edit(innerCache => { if (removed.Any()) innerCache.Remove(removed); if (added.Any()) innerCache.AddOrUpdate(added); }); }); _cleanUp = new CompositeDisposable(Lines, lines, aggregator); }
public void FilterError() { bool completed = false; bool error = false; var feeder = new SourceCache<TransformEntityWithError, int>(e=>e.Key); var subscriber = feeder.Connect() .Filter(x=>true) .Finally(() => completed = true) .Subscribe(updates => { Console.WriteLine(); }, ex => error = true); feeder.BatchUpdate(updater => updater.AddOrUpdate(new TransformEntityWithError(new Entity()))); subscriber.Dispose(); Assert.IsTrue(error, "Error has not been invoked"); Assert.IsTrue(completed, "Completed has not been called"); }
public void SkipInitialDoesNotReturnTheFirstBatchOfData() { bool updateReceived = false; var cache = new SourceCache<Person, string>(p => p.Name); var deferStream = cache.Connect().SkipInitial() .Subscribe(changes => updateReceived = true); Assert.IsFalse(updateReceived, "No update should be received"); cache.AddOrUpdate(new Person("P1", 1)); Assert.IsFalse(updateReceived, "No update should be received for initial batch of changes"); cache.AddOrUpdate(new Person("P2", 2)); Assert.IsTrue(updateReceived, "Replace should be received"); deferStream.Dispose(); }
public void CachePerformance(int n) { /* Tricks to make it fast = 1) use cache.AddRange(Enumerable.Range(0, n)) instead of for (int i = 0; i < n; i++) cache.AddOrUpdate(i); 2) Uncomment Buffer(n/10).FlattenBufferResult() or just use buffer by time functions With both of these the speed can be almost negligable */ var cache = new SourceCache<int, int>(i => i); double calculated = 0; var sw = Stopwatch.StartNew(); var summation = cache.Connect() .StdDev(i => i) .Subscribe(result => calculated = result); //1. this is very slow if there are loads of updates (each updates causes a new summation) for (int i = 1; i < n; i++) cache.AddOrUpdate(i); //2. much faster to to this (whole range is 1 update and 1 calculation): // cache.AddOrUpdate(Enumerable.Range(0,n)); sw.Stop(); summation.Dispose(); cache.Dispose(); Console.WriteLine("Total items: {0}. Value = {1}", n, calculated); Console.WriteLine("Cache: {0} updates took {1} ms {2:F3} ms each. {3}", n, sw.ElapsedMilliseconds, sw.Elapsed.TotalMilliseconds / n, DateTime.Now.ToShortDateString()); }
public void TransformError() { bool completed = false; bool error = false; var feeder = new SourceCache<Entity, int>(e => e.Key); var subscriber = feeder.Connect() .Transform(e => new TransformEntityWithError(e)) .Finally(() => completed = true) .Subscribe(updates => { Console.WriteLine(); }, ex => error = true); feeder.BatchUpdate(updater => updater.AddOrUpdate(Enumerable.Range(0, 10000).Select(_ => new Entity()).ToArray())); feeder.BatchUpdate(updater => updater.AddOrUpdate(new Entity())); subscriber.Dispose(); Assert.IsTrue(error, "Error has not been invoked"); Assert.IsTrue(completed, "Completed has not been called"); }
public LineScroller([NotNull] IObservable<ILineProvider> latest, [NotNull] IObservable<ScrollRequest> scrollRequest) { if (latest == null) throw new ArgumentNullException(nameof(latest)); if (scrollRequest == null) throw new ArgumentNullException(nameof(scrollRequest)); var lines = new SourceCache<Line, LineKey>(l => l.Key); Lines = lines.Connect().IgnoreUpdateWhen((current,previous)=> current.Key==previous.Key).AsObservableCache(); var locker = new object(); scrollRequest = scrollRequest.Synchronize(locker); latest = latest.Synchronize(locker); var aggregator = latest .CombineLatest(scrollRequest, (currentLines, scroll) => new { currentLines, scroll}) .Sample(TimeSpan.FromMilliseconds(50)) .Select(x => { if (x.scroll== ScrollRequest.None || x.scroll.PageSize == 0 || x.currentLines.Count == 0) return new Line[0]; return x.currentLines.ReadLines(x.scroll).ToArray(); }) .RetryWithBackOff<Line[], Exception>((ex, i) => TimeSpan.FromSeconds(1)) .Subscribe(currentPage => { var previous = lines.Items.ToArray(); var added = currentPage.Except(previous, Line.TextStartComparer).ToArray(); var removed = previous.Except(currentPage, Line.TextStartComparer).ToArray(); lines.Edit(innerCache => { if (currentPage.Length == 0) innerCache.Clear(); if (removed.Any()) innerCache.Remove(removed); if (added.Any()) innerCache.AddOrUpdate(added); }); }); _cleanUp = new CompositeDisposable(Lines, lines, aggregator); }
public void DeferUntilLoadedDoesNothingUntilDataHasBeenReceived() { bool updateReceived = false; IChangeSet<Person, string> result = null; var cache = new SourceCache<Person, string>(p => p.Name); var deferStream = cache.Connect().DeferUntilLoaded() .Subscribe(changes => { updateReceived = true; result = changes; }); Assert.IsFalse(updateReceived,"No update should be received"); cache.AddOrUpdate(new Person("Test",1)); Assert.IsTrue(updateReceived,"Replace should be received"); Assert.AreEqual(1,result.Adds); Assert.AreEqual(new Person("Test",1), result.First().Current); deferStream.Dispose(); }
public FileSearcher([NotNull] IObservable<FileSegmentCollection> fileSegments, [NotNull] Func<string, bool> predicate, int arbitaryNumberOfMatchesBeforeWeBailOutBecauseMemoryGetsHammered = 50000, Encoding encoding = null, IScheduler scheduler =null) { if (fileSegments == null) throw new ArgumentNullException(nameof(fileSegments)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); _predicate = predicate; _arbitaryNumberOfMatchesBeforeWeBailOutBecauseMemoryGetsHammered = arbitaryNumberOfMatchesBeforeWeBailOutBecauseMemoryGetsHammered; _scheduler = scheduler ?? Scheduler.Default; var shared = fileSegments.Replay(1).RefCount(); var infoSubscriber = shared.Select(segments => segments.Info) .Take(1) .Subscribe(info => { Info = info; Encoding = encoding ?? info.GetEncoding(); }); //Create a cache of segments which are to be searched var segmentCache = shared.Select(s => s.Segments) .ToObservableChangeSet(s => s.Key) .IgnoreUpdateWhen((current,previous)=>current==previous) .AsObservableCache(); //manually maintained search results and status var searchData= new SourceCache<FileSegmentSearch, FileSegmentKey>(s=>s.Key); SearchResult = searchData.Connect() .Flatten() .Select(change=>change.Current) .Scan((FileSearchResult)null, (previous, current) => previous==null ? new FileSearchResult(current, Info, Encoding, _arbitaryNumberOfMatchesBeforeWeBailOutBecauseMemoryGetsHammered) : new FileSearchResult(previous, current, Info, Encoding, _arbitaryNumberOfMatchesBeforeWeBailOutBecauseMemoryGetsHammered)) .StartWith(FileSearchResult.None) .Replay(1).RefCount(); //initialise a pending state for all segments var loader = segmentCache.Connect() .Transform(fs => new FileSegmentSearch(fs)) .WhereReasonsAre(ChangeReason.Add) .PopulateInto(searchData); //scan end of file, then tail var tailSearch = segmentCache.WatchValue(FileSegmentKey.Tail) .Scan((FileSegmentSearch) null, (previous, current) => { if (previous == null) { var result = Search(current.Start, current.End); return new FileSegmentSearch(current, result); } else { var result = Search(previous.Segment.End, current.End); return result == null ? previous : new FileSegmentSearch(previous, result); } }) .DistinctUntilChanged() .Publish(); //start tailing var tailSubscriber = tailSearch.Subscribe(tail => searchData.AddOrUpdate(tail)); //load the rest of the file segment by segment, reporting status after each search var headSubscriber = tailSearch.Take(1).WithContinuation(() => { var locker = new object(); return searchData.Connect(fss=>fss.Segment.Type == FileSegmentType.Head ) .Do(head => Debug.WriteLine(head.First().Current)) .WhereReasonsAre(ChangeReason.Add) .SelectMany(changes=>changes.Select(c=>c.Current).OrderByDescending(c=>c.Segment.Index).ToArray()) .ObserveOn(_scheduler) .Synchronize(locker) .Do(head => searchData.AddOrUpdate(new FileSegmentSearch(head.Segment,FileSegmentSearchStatus.Searching) )) .Select(fileSegmentSearch => { /* This hack imposes a limitation on the number of items returned as memory can be absolutely hammered [I have seen 20MB memory when searching a 1 GB file - obviously not an option] TODO: A proper solution. 1. How about index to file? 2. Allow auto pipe of large files 3. Allow user to have some control here */ var sum = searchData.Items.Sum(fss => fss.Lines.Length); if (sum >= _arbitaryNumberOfMatchesBeforeWeBailOutBecauseMemoryGetsHammered) { return new FileSegmentSearch(fileSegmentSearch, FileSegmentSearchStatus.Complete); } var result = Search(fileSegmentSearch.Segment.Start, fileSegmentSearch.Segment.End); return new FileSegmentSearch(fileSegmentSearch, result); }); }) .Subscribe(head => searchData.AddOrUpdate(head)); _cleanUp = new CompositeDisposable( segmentCache, loader, tailSubscriber, headSubscriber, tailSearch.Connect(), infoSubscriber); }
public FileSearch([NotNull] IObservable<FileSegmentCollection> fileSegments, [NotNull] Func<string, bool> predicate, Encoding encoding = null, IScheduler scheduler =null) { if (fileSegments == null) throw new ArgumentNullException(nameof(fileSegments)); if (predicate == null) throw new ArgumentNullException(nameof(predicate)); _predicate = predicate; _scheduler = scheduler ?? Scheduler.Default; var shared = fileSegments.Replay(1).RefCount(); var infoSubscriber = shared.Select(segments => segments.Info) .Take(1) .Subscribe(info => { Info = info; Encoding = encoding ?? info.GetEncoding(); }); //Create a cache of segments which are to be searched var segmentCache = shared.Select(s => s.Segments) .ToObservableChangeSet(s => s.Key) .IgnoreUpdateWhen((current,previous)=>current==previous) .AsObservableCache(); //manually maintained search results and status var searchData= new SourceCache<FileSegmentSearch, FileSegmentKey>(s=>s.Key); SearchResult = searchData.Connect() .Flatten() .Select(change=>change.Current) .Scan((FileSearchResult)null, (previous, current) => previous==null ? new FileSearchResult(current, Info, Encoding) : new FileSearchResult(previous, current, Info, Encoding)) .StartWith(FileSearchResult.None) .Replay(1).RefCount(); //initialise a pending state for all segments var loader = segmentCache.Connect() .Transform(fs => new FileSegmentSearch(fs)) .WhereReasonsAre(ChangeReason.Add) .PopulateInto(searchData); //scan end of file, then tail var tailSearch = segmentCache.WatchValue(FileSegmentKey.Tail) .Scan((FileSegmentSearch) null, (previous, current) => { if (previous == null) { var result = Search(current.Start, current.End); return new FileSegmentSearch(current, result); } else { var result = Search(previous.Segment.End, current.End); return result == null ? previous : new FileSegmentSearch(previous, result); } }) .DistinctUntilChanged() .Publish(); //start tailing var tailSubscriber = tailSearch.Subscribe(tail => searchData.AddOrUpdate(tail)); //load the rest of the file segment by segment, reporting status after each search var headSubscriber = tailSearch.Take(1).WithContinuation(() => { var locker = new object(); return searchData.Connect(fss=>fss.Segment.Type == FileSegmentType.Head ) .Do(head => Debug.WriteLine(head.First().Current)) .WhereReasonsAre(ChangeReason.Add) .SelectMany(changes=>changes.Select(c=>c.Current).OrderByDescending(c=>c.Segment.Index).ToArray()) .ObserveOn(_scheduler) .Synchronize(locker) .Do(head => searchData.AddOrUpdate(new FileSegmentSearch(head.Segment,FileSegmentSearchStatus.Searching) )) .Select(fss => { // Debug.WriteLine($"Running Search For: {fss.Key}"); var result = Search(fss.Segment.Start, fss.Segment.End); return new FileSegmentSearch(fss, result); }); }) .Subscribe(head => searchData.AddOrUpdate(head)); _cleanUp = new CompositeDisposable( segmentCache, loader, tailSubscriber, headSubscriber, tailSearch.Connect(), infoSubscriber); }
public void Initialise() { _source = new SourceCache<Person, string>(p => p.Name); }
public SearchInfoCollection(ICombinedSearchMetadataCollection combinedSearchMetadataCollection, ISearchMetadataFactory searchMetadataFactory, IFileWatcher fileWatcher) { _localMetadataCollection = combinedSearchMetadataCollection.Local; _combinedSearchMetadataCollection = combinedSearchMetadataCollection; _searchMetadataFactory = searchMetadataFactory; _fileWatcher = fileWatcher; var exclusionPredicate = combinedSearchMetadataCollection.Combined.Connect() .IncludeUpdateWhen((current, previous) => !SearchMetadata.EffectsFilterComparer.Equals(current, previous)) .Filter(meta=> meta.IsExclusion) .ToCollection() .Select(searchMetadataItems => { Func<string, bool> predicate = null; if (searchMetadataItems.Count == 0) return predicate; var predicates = searchMetadataItems.Select(meta => meta.BuildPredicate()).ToArray(); predicate = str => { return !predicates.Any(item => item(str)); }; return predicate; }).StartWith((Func<string, bool>)null) .Replay(1).RefCount(); All = exclusionPredicate.Select(predicate => { if (predicate==null) return _fileWatcher.Latest.Index(); return _fileWatcher.Latest.Search(predicate); }).Switch().Replay(1).RefCount(); //create a collection with 1 item, which is used to show entire file var systemSearches = new SourceCache<SearchInfo, string>(t => t.SearchText); systemSearches.AddOrUpdate(new SearchInfo("<All>", false, All, SearchType.All)); //create a collection of all possible user filters var userSearches = combinedSearchMetadataCollection.Combined .Connect(meta => meta.Filter) .IgnoreUpdateWhen((current,previous)=> SearchMetadata.EffectsFilterComparer.Equals(current, previous)) .Transform(meta => { var latest = exclusionPredicate .Select(exclpredicate => { Func<string, bool> resultingPredicate; if (exclpredicate == null) { resultingPredicate = meta.BuildPredicate(); } else { var toMatch = meta.BuildPredicate(); resultingPredicate = str=> toMatch(str) && exclpredicate(str); } return _fileWatcher.Latest.Search(resultingPredicate); }) .Switch() .Replay(1).RefCount(); return new SearchInfo(meta.SearchText, meta.IsGlobal, latest, SearchType.User); }); //combine te results into a single collection Searches = systemSearches.Connect() .Or(userSearches) .AsObservableCache(); _cleanUp = new CompositeDisposable(Searches, systemSearches); }
public void Initialise() { _source = new SourceCache<ObjectWithObservable, int>(p => p.Id); }