Ejemplo n.º 1
0
        public IObservable <IChangeSet <TDestination, TKey> > Run()
        {
            return(Observable.Create <IChangeSet <TDestination, TKey> >(observer =>
            {
                var cache = new ChangeAwareCache <TransformedItemContainer, TKey>();

                var transformer = _source.SelectTask(changes => DoTransform(cache, changes));

                if (_forceTransform != null)
                {
                    var locker = new object();
                    var forced = _forceTransform
                                 .Synchronize(locker)
                                 .SelectTask(shouldTransform => DoTransform(cache, shouldTransform));

                    transformer = transformer.Synchronize(locker).Merge(forced);
                }

                return transformer.SubscribeSafe(observer);
            }));
        }
Ejemplo n.º 2
0
        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)

            if (items is IList <KeyValuePair <TKey, TObject> > list)
            {
                //zero allocation enumerator
                var enumerable = EnumerableIList.Create(list);
                foreach (var item in enumerable)
                {
                    ProcessItem(target, sourceLists, item.Value, item.Key);
                }
            }
            else
            {
                foreach (var item in items)
                {
                    ProcessItem(target, sourceLists, item.Value, item.Key);
                }
            }
        }
Ejemplo n.º 3
0
        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))
            {
                TKey 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.Evaluate:
                    cache.Evaluate(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));
        }
Ejemplo n.º 4
0
        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)))
                            .ToArray();
                    }, 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 IChangeSet <TDestination, TKey> ProcessUpdates(ChangeAwareCache <TransformedItemContainer, TKey> cache, IEnumerable <TransformResult> transformedItems)
        {
            foreach (var result in transformedItems)
            {
                if (result.Success)
                {
                    switch (result.Change.Reason)
                    {
                    case ChangeReason.Add:
                    case ChangeReason.Update:
                        cache.AddOrUpdate(result.Container.Value, result.Key);
                        break;

                    case ChangeReason.Remove:
                        cache.Remove(result.Key);
                        break;

                    case ChangeReason.Refresh:
                        cache.Refresh(result.Key);
                        break;
                    }
                }
                else
                {
                    _exceptionCallback(new Error <TSource, TKey>(result.Error, result.Change.Current, result.Change.Key));
                }
            }

            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));
        }
Ejemplo n.º 6
0
        public IObservable <IChangeSet <TObject, TKey> > Run()
        {
            return(Observable.Create <IChangeSet <TObject, TKey> >(
                       observer =>
            {
                var allData = new Cache <TObject, TKey>();
                var filteredData = new ChangeAwareCache <TObject, TKey>();
                Func <TObject, bool> predicate = _ => false;

                var locker = new object();

                var refresher = LatestPredicateObservable().Synchronize(locker).Select(
                    p =>
                {
                    // set the local predicate
                    predicate = p;

                    // reapply filter using all data from the cache
                    return filteredData.RefreshFilteredFrom(allData, predicate);
                });

                var dataChanged = _source.Synchronize(locker).Select(
                    changes =>
                {
                    // maintain all data [required to re-apply filter]
                    allData.Clone(changes);

                    // maintain filtered data
                    filteredData.FilterChanges(changes, predicate);

                    // get latest changes
                    return filteredData.CaptureChanges();
                });

                return refresher.Merge(dataChanged).NotEmpty().SubscribeSafe(observer);
            }));
        }
Ejemplo n.º 7
0
        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);
                }
            }
        }
Ejemplo n.º 8
0
 public CacheUpdater(ChangeAwareCache <TObject, TKey> cache, IKeySelector <TObject, TKey> keySelector = null)
 {
     _cache       = cache ?? throw new ArgumentNullException(nameof(cache));
     _keySelector = keySelector;
 }
Ejemplo n.º 9
0
 /// <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();
 }
Ejemplo n.º 10
0
        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()));
        }
Ejemplo n.º 11
0
 protected virtual TransformResult[] TransformChanges(ChangeAwareCache <TransformedItemContainer, TKey> cache, IEnumerable <Change <TSource, TKey> > changes)
 {
     return(changes.Select(Select).AsArray());
 }
Ejemplo n.º 12
0
        protected AbstractFilter(ChangeAwareCache <TObject, TKey> cache, Func <TObject, bool>?filter)
        {
            _cache = cache ?? throw new ArgumentNullException(nameof(cache));

            Filter = filter ?? (_ => true);
        }
        public IObservable <IChangeSet <TObject, TKey> > Run()
        {
            return(Observable.Create <IChangeSet <TObject, TKey> >(
                       observer =>
            {
                long orderItemWasAdded = -1;
                var locker = new object();

                if (_expireAfter is null && _limitSizeTo < 1)
                {
                    return _source.Scan(
                        new ChangeAwareCache <TObject, TKey>(),
                        (state, latest) =>
                    {
                        if (latest is IList <TObject> list)
                        {
                            // zero allocation enumerator
                            var enumerableList = EnumerableIList.Create(list);
                            if (!_singleValueSource)
                            {
                                state.Remove(state.Keys.Except(enumerableList.Select(_keySelector)).ToList());
                            }

                            foreach (var item in enumerableList)
                            {
                                state.AddOrUpdate(item, _keySelector(item));
                            }
                        }
                        else
                        {
                            var enumerable = latest.ToList();
                            if (!_singleValueSource)
                            {
                                state.Remove(state.Keys.Except(enumerable.Select(_keySelector)).ToList());
                            }

                            foreach (var item in enumerable)
                            {
                                state.AddOrUpdate(item, _keySelector(item));
                            }
                        }

                        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 is null ? Observable.Never <IChangeSet <ExpirableItem <TObject, TKey>, TKey> >() : sizeLimited).Filter(ei => ei.ExpireAt != DateTime.MaxValue).MergeMany(
                    grouping =>
                {
                    var expireAt = grouping.ExpireAt.Subtract(_scheduler.Now.DateTime);
                    return Observable.Timer(expireAt, _scheduler).Select(_ => grouping);
                }).Synchronize(locker).Select(
                    item =>
                {
                    cache.Remove(item.Key);
                    return cache.CaptureChanges();
                });

                var publisher = sizeLimited.Merge(timeLimited).Cast(ei => ei.Value).NotEmpty().SubscribeSafe(observer);

                return new CompositeDisposable(publisher, sizeLimited.Connect());
            }));
        }
Ejemplo n.º 14
0
        public IObservable <IChangeSet <TDestination, TKey> > Run()
        {
            return(_source.Scan((ChangeAwareCache <TDestination, TKey>)null, (cache, changes) =>
            {
                if (cache == null)
                {
                    cache = new ChangeAwareCache <TDestination, TKey>(changes.Count);
                }

                var concreteType = changes.ToConcreteType();
                foreach (var change in concreteType)
                {
                    switch (change.Reason)
                    {
                    case ChangeReason.Add:
                    case ChangeReason.Update:
                        {
                            TDestination transformed;
                            if (_exceptionCallback != null)
                            {
                                try
                                {
                                    transformed = _transformFactory(change.Current, change.Previous, change.Key);
                                    cache.AddOrUpdate(transformed, change.Key);
                                }
                                catch (Exception ex)
                                {
                                    _exceptionCallback(new Error <TSource, TKey>(ex, change.Current, change.Key));
                                }
                            }
                            else
                            {
                                transformed = _transformFactory(change.Current, change.Previous, change.Key);
                                cache.AddOrUpdate(transformed, change.Key);
                            }
                        }

                        break;

                    case ChangeReason.Remove:
                        cache.Remove(change.Key);
                        break;

                    case ChangeReason.Refresh:
                        {
                            if (_transformOnRefresh)
                            {
                                var transformed = _transformFactory(change.Current, change.Previous, change.Key);
                                cache.AddOrUpdate(transformed, change.Key);
                            }
                            else
                            {
                                cache.Refresh(change.Key);
                            }
                        }

                        break;

                    case ChangeReason.Moved:
                        //Do nothing !
                        break;
                    }
                }

                return cache;
            })
                   .Select(cache => cache.CaptureChanges())
                   .NotEmpty());
        }
Ejemplo n.º 15
0
        private IChangeSet <TDestination, TKey> DoTransform(ChangeAwareCache <TDestination, TKey> cache, IChangeSet <TSource, TKey> changes)
        {
            var transformed = changes.Select(ToDestination);

            return(ProcessUpdates(cache, transformed));
        }
Ejemplo n.º 16
0
        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();

                var sharedLists = sourceLists.Connect().Publish();

                //merge the items back together
                var allChanges = sharedLists
                                 .MergeMany(mc => mc.Source)
                                 .Synchronize(locker)
                                 .Subscribe(changes =>
                {
                    //Populate result list and check 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 = sharedLists
                                  .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 = sharedLists
                                    .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, sharedLists.Connect());
            }));
        }
Ejemplo n.º 17
0
        public IObservable <ChangeSet <TObject, TKey> > Run()
        {
            return(Observable.Create <ChangeSet <TObject, TKey> >
                   (
                       observer =>
            {
                var localCache = 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 = localCache.CaptureChanges();
                    if (changes.Count > 0)
                    {
                        observer.OnNext(changes);
                    }
                    localCache = new ChangeAwareCache <TObject, TKey>();
                }

                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
                                   .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 =>
                {
                    localCache.Clone(changes);

                    //publish if not paused
                    if (!paused)
                    {
                        ResumeAction();
                    }
                });

                return new CompositeDisposable(publisher, pausedHander, timeoutDisposer, intervalTimerDisposer);
            }
                   ));
        }
        private IChangeSet <TDestination, TKey> DoTransform(ChangeAwareCache <TransformedItemContainer, TKey> cache, IChangeSet <TSource, TKey> changes)
        {
            var transformed = TransformChanges(changes);

            return(ProcessUpdates(cache, transformed.ToArray()));
        }
Ejemplo n.º 19
0
 private void UpdateResultList(ChangeAwareCache <TObject, TKey> target, MergeContainer[] sourceLists, IChangeSet <TObject, TKey> changes)
 {
     changes.ForEach(change => { ProcessItem(target, sourceLists, change.Current, change.Key); });
 }
Ejemplo n.º 20
0
 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); });
 }
Ejemplo n.º 21
0
        public static void FilterChanges <TObject, TKey>(this ChangeAwareCache <TObject, TKey> cache, IChangeSet <TObject, TKey> changes, Func <TObject, bool> predicate)
            where TKey : notnull
        {
            foreach (var change in changes.ToConcreteType())
            {
                var key = change.Key;
                switch (change.Reason)
                {
                case ChangeReason.Add:
                {
                    var current = change.Current;
                    if (predicate(current))
                    {
                        cache.AddOrUpdate(current, key);
                    }
                }

                break;

                case ChangeReason.Update:
                {
                    var current = change.Current;
                    if (predicate(current))
                    {
                        cache.AddOrUpdate(current, key);
                    }
                    else
                    {
                        cache.Remove(key);
                    }
                }

                break;

                case ChangeReason.Remove:
                    cache.Remove(key);
                    break;

                case ChangeReason.Refresh:
                {
                    var existing = cache.Lookup(key);
                    if (predicate(change.Current))
                    {
                        if (!existing.HasValue)
                        {
                            cache.AddOrUpdate(change.Current, key);
                        }
                        else
                        {
                            cache.Refresh(key);
                        }
                    }
                    else
                    {
                        if (existing.HasValue)
                        {
                            cache.Remove(key);
                        }
                    }
                }

                break;
                }
            }
        }
Ejemplo n.º 22
0
        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))));
        }
Ejemplo n.º 23
0
 public FilteredUpdater(ChangeAwareCache <TObject, TKey> cache, Func <TObject, bool> filter)
     : base(cache, filter)
 {
 }
Ejemplo n.º 24
0
        private async Task <IChangeSet <TDestination, TKey> > DoTransform(ChangeAwareCache <TransformedItemContainer, TKey> cache, IChangeSet <TSource, TKey> changes)
        {
            var transformed = await Task.WhenAll(changes.Select(Transform)).ConfigureAwait(false);

            return(ProcessUpdates(cache, transformed));
        }
Ejemplo n.º 25
0
 public ReaderWriter(Func <TObject, TKey> keySelector = null)
 {
     _keySelector      = keySelector;
     _changeAwareCache = new ChangeAwareCache <TObject, TKey>(_data);
 }
Ejemplo n.º 26
0
 /// <summary>
 /// Initialises the specified changes.
 /// </summary>
 /// <param name="cache">The cache.</param>
 /// <returns></returns>
 public void Reset(ChangeAwareCache <TObject, TKey> cache)
 {
     _list = new LinkedList <KeyValuePair <TKey, TObject> >(cache.KeyValues.OrderBy(kv => kv, _comparer));
 }