private IChangeSet <TDestination, TKey> ProcessUpdates(ChangeAwareCache <TransformedItemContainer, TKey> cache, TransformResult[] transformedItems) { //check for errors and callback if a handler has been specified var errors = transformedItems.Where(t => !t.Success).ToArray(); if (errors.Any()) { errors.ForEach(t => _exceptionCallback(new Error <TSource, TKey>(t.Error, t.Change.Current, t.Change.Key))); } foreach (var result in transformedItems.Where(t => t.Success)) { var key = result.Key; switch (result.Change.Reason) { case ChangeReason.Add: case ChangeReason.Update: cache.AddOrUpdate(result.Container.Value, key); break; case ChangeReason.Remove: cache.Remove(key); break; case ChangeReason.Refresh: cache.Refresh(key); break; } } var changes = cache.CaptureChanges(); var transformed = changes.Select(change => new Change <TDestination, TKey>(change.Reason, change.Key, change.Current.Destination, change.Previous.Convert(x => x.Destination), change.CurrentIndex, change.PreviousIndex)); return(new ChangeSet <TDestination, TKey>(transformed)); }
private IObservable <IChangeSet <TDestination, TDestinationKey> > CreateWithChangeset() { return(Observable.Create <IChangeSet <TDestination, TDestinationKey> >(observer => { var result = new ChangeAwareCache <TDestination, TDestinationKey>(); var transformed = _source.Transform((t, key) => { //Only skip initial for first time Adds where there is isinitial data records var locker = new object(); var collection = _manyselector(t); var changes = _childChanges(t).Synchronize(locker).Skip(1); return new ManyContainer(() => { lock (locker) return collection.Select(m => new DestinationContainer(m, _keySelector(m))); }, changes); }).Publish(); var outerLock = new object(); var intial = transformed .Synchronize(outerLock) .Select(changes => new ChangeSet <TDestination, TDestinationKey>(new DestinationEnumerator(changes))); var subsequent = transformed .MergeMany(x => x.Changes) .Synchronize(outerLock); var allChanges = intial.Merge(subsequent).Select(changes => { result.Clone(changes); return result.CaptureChanges(); }); return new CompositeDisposable(allChanges.SubscribeSafe(observer), transformed.Connect()); })); }
private void ProcessItem(ChangeAwareCache <TObject, TKey> target, MergeContainer[] sourceLists, TObject item, TKey key) { var cached = target.Lookup(key); var shouldBeInResult = MatchesConstraint(sourceLists, key); if (shouldBeInResult) { if (!cached.HasValue) { target.AddOrUpdate(item, key); } else if (!ReferenceEquals(item, cached.Value)) { target.AddOrUpdate(item, key); } } else { if (cached.HasValue) { target.Remove(key); } } }
public void Initialise() { _cache = new ChangeAwareCache <Person, string>(); _updater = new CacheUpdater <Person, string>(_cache); }
private void UpdateResultList(ChangeAwareCache <TObject, TKey> target, MergeContainer[] sourceLists, IChangeSet <TObject, TKey> changes) { changes.ForEach(change => { ProcessItem(target, sourceLists, change.Current, change.Key); }); }
public IObservable <IChangeSet <TObject, TKey> > Run() { return(Observable.Create <IChangeSet <TObject, TKey> >(observer => { var locker = new object(); //this is the resulting cache which produces all notifications var resultCache = new ChangeAwareCache <TObject, TKey>(); //Transform to a merge container. //This populates a RefTracker when the original source is subscribed to var sourceLists = _source.Connect() .Synchronize(locker) .Transform(changeset => new MergeContainer(changeset)) .AsObservableList(); //merge the items back together var allChanges = sourceLists.Connect() .MergeMany(mc => mc.Source) .Synchronize(locker) .Subscribe(changes => { //Populate result list and chck for changes UpdateResultList(resultCache, sourceLists.Items.AsArray(), changes); var notifications = resultCache.CaptureChanges(); if (notifications.Count != 0) { observer.OnNext(notifications); } }); //when an list is removed, need to var removedItem = sourceLists.Connect() .OnItemRemoved(mc => { //Remove items if required ProcessChanges(resultCache, sourceLists.Items.AsArray(), mc.Cache.KeyValues); if (_type == CombineOperator.And || _type == CombineOperator.Except) { var itemsToCheck = sourceLists.Items.SelectMany(mc2 => mc2.Cache.KeyValues); ProcessChanges(resultCache, sourceLists.Items.AsArray(), itemsToCheck); } var notifications = resultCache.CaptureChanges(); if (notifications.Count != 0) { observer.OnNext(notifications); } }) .Subscribe(); //when an list is added or removed, need to var sourceChanged = sourceLists.Connect() .WhereReasonsAre(ListChangeReason.Add, ListChangeReason.AddRange) .ForEachItemChange(mc => { ProcessChanges(resultCache, sourceLists.Items.AsArray(), mc.Current.Cache.KeyValues); if (_type == CombineOperator.And || _type == CombineOperator.Except) { ProcessChanges(resultCache, sourceLists.Items.AsArray(), resultCache.KeyValues.ToArray()); } var notifications = resultCache.CaptureChanges(); if (notifications.Count != 0) { observer.OnNext(notifications); } }) .Subscribe(); return new CompositeDisposable(sourceLists, allChanges, removedItem, sourceChanged); })); }
private void ProcessChanges(ChangeAwareCache <TObject, TKey> target, MergeContainer[] sourceLists, IEnumerable <KeyValuePair <TKey, TObject> > items) { //check whether the item should be removed from the list (or in the case of And, added) items.ForEach(item => { ProcessItem(target, sourceLists, item.Value, item.Key); }); }
/// <summary> /// Initialises the specified changes. /// </summary> /// <param name="cache">The cache.</param> /// <returns></returns> public void Reset(ChangeAwareCache <TObject, TKey> cache) { _list = cache.KeyValues.OrderBy(kv => kv, _comparer).ToList(); }
public SourceUpdaterFixture() { _cache = new ChangeAwareCache <Person, string>(); _updater = new CacheUpdater <Person, string>(_cache, new KeySelector <Person, string>(p => p.Name)); }
public PLinqFilteredUpdater(ChangeAwareCache <TObject, TKey> cache, Func <TObject, bool> filter, ParallelisationOptions parallelisationOptions) : base(cache, filter) { _parallelisationOptions = parallelisationOptions; }
public static IChangeSet <TObject, TKey> GetInitialUpdates <TObject, TKey>(this ChangeAwareCache <TObject, TKey> source, Func <TObject, bool> filter = null) { var filtered = filter == null ? source.KeyValues : source.KeyValues.Where(kv => filter(kv.Value)); return(new ChangeSet <TObject, TKey>(filtered.Select(i => new Change <TObject, TKey>(ChangeReason.Add, i.Key, i.Value)))); }
private async Task <IChangeSet <TDestination, TKey> > DoTransform(ChangeAwareCache <TransformedItemContainer, TKey> cache, IChangeSet <TSource, TKey> changes) { var transformed = await changes.SelectParallel(Transform, _maximumConcurrency); return(ProcessUpdates(cache, transformed.ToArray())); }
public CacheUpdater(ChangeAwareCache <TObject, TKey> cache, IKeySelector <TObject, TKey> keySelector = null) { _cache = cache ?? throw new ArgumentNullException(nameof(cache)); _keySelector = keySelector; }
public IObservable <IChangeSet <TObject, TKey> > Run() { return(Observable.Create <IChangeSet <TObject, TKey> > ( observer => { var result = new ChangeAwareCache <TObject, TKey>(); var locker = new object(); var paused = _intialPauseState; var timeoutDisposer = new SerialDisposable(); var intervalTimerDisposer = new SerialDisposable(); void ResumeAction() { //publish changes (if there are any) var changes = result.CaptureChanges(); if (changes.Count > 0) { observer.OnNext(changes); } } IDisposable IntervalFunction() { return _intervalTimer .Synchronize(locker) .Finally(() => paused = false) .Subscribe(_ => { paused = false; ResumeAction(); if (_intervalTimer != null) { paused = true; } }); } if (_intervalTimer != null) { intervalTimerDisposer.Disposable = IntervalFunction(); } var pausedHander = _pauseIfTrueSelector // .StartWith(initalp) .Synchronize(locker) .Subscribe(p => { paused = p; if (!p) { //pause window has closed, so reset timer if (_timeOut.HasValue) { timeoutDisposer.Disposable = Disposable.Empty; } ResumeAction(); } else { if (_timeOut.HasValue) { timeoutDisposer.Disposable = Observable.Timer(_timeOut.Value, _scheduler) .Synchronize(locker) .Subscribe(_ => { paused = false; ResumeAction(); }); } } }); var publisher = _source .Synchronize(locker) .Subscribe(changes => { result.Clone(changes); //publish if not paused if (!paused) { observer.OnNext(result.CaptureChanges()); } }); return new CompositeDisposable(publisher, pausedHander, timeoutDisposer, intervalTimerDisposer); } )); }
public void Initialise() { _cache = new ChangeAwareCache <Person, string>(); _updater = new CacheUpdater <Person, string>(_cache, new KeySelector <Person, string>(p => p.Name)); }
public IObservable <IChangeSet <TObject, TKey> > Run() { return(Observable.Create <IChangeSet <TObject, TKey> >(observer => { long orderItemWasAdded = -1; var locker = new object(); if (_expireAfter == null && _limitSizeTo < 1) { return _source.Scan(new ChangeAwareCache <TObject, TKey>(), (state, latest) => { latest.ForEach(t => state.AddOrUpdate(t, _keySelector(t))); return state; }) .Select(state => state.CaptureChanges()) .SubscribeSafe(observer); } var cache = new ChangeAwareCache <ExpirableItem <TObject, TKey>, TKey>(); var sizeLimited = _source.Synchronize(locker) .Scan(cache, (state, latest) => { latest.Select(t => { var key = _keySelector(t); return CreateExpirableItem(t, key, ref orderItemWasAdded); }).ForEach(ei => cache.AddOrUpdate(ei, ei.Key)); if (_limitSizeTo > 0 && state.Count > _limitSizeTo) { var toRemove = state.Count - _limitSizeTo; //remove oldest items cache.KeyValues .OrderBy(exp => exp.Value.Index) .Take(toRemove) .ForEach(ei => cache.Remove(ei.Key)); } return state; }) .Select(state => state.CaptureChanges()) .Publish(); var timeLimited = (_expireAfter == null ? Observable.Never <IChangeSet <ExpirableItem <TObject, TKey>, TKey> >() : sizeLimited) .Filter(ei => ei.ExpireAt != DateTime.MaxValue) .GroupWithImmutableState(ei => ei.ExpireAt) .MergeMany(grouping => { var expireAt = grouping.Key.Subtract(_scheduler.Now.DateTime); return Observable.Timer(expireAt, _scheduler).Select(_ => grouping); }) .Synchronize(locker) .Select(grouping => { cache.Remove(grouping.Keys); return cache.CaptureChanges(); }); var publisher = sizeLimited .Merge(timeLimited) .Cast(ei => ei.Value) .NotEmpty() .SubscribeSafe(observer); return new CompositeDisposable(publisher, sizeLimited.Connect()); })); }
public CacheUpdaterFixture() { _cache = new ChangeAwareCache <Person, string>(); _updater = new CacheUpdater <Person, string>(_cache); }
public FilteredUpdater(ChangeAwareCache <TObject, TKey> cache, Func <TObject, bool> filter) : base(cache, filter) { }
public static void FilterChanges <TObject, TKey>(this ChangeAwareCache <TObject, TKey> cache, IChangeSet <TObject, TKey> changes, Func <TObject, bool> predicate) { foreach (var change in changes) { var key = change.Key; switch (change.Reason) { case ChangeReason.Add: { if (predicate(change.Current)) { cache.AddOrUpdate(change.Current, key); } } break; case ChangeReason.Update: { if (predicate(change.Current)) { cache.AddOrUpdate(change.Current, key); } else { cache.Remove(key); } } break; case ChangeReason.Remove: cache.Remove(key); break; case ChangeReason.Refresh: { var exisiting = cache.Lookup(key); if (predicate(change.Current)) { if (!exisiting.HasValue) { cache.AddOrUpdate(change.Current, key); } else { cache.Refresh(key); } } else { if (exisiting.HasValue) { cache.Remove(key); } } } break; } } }