예제 #1
0
        public IObservable <IChangeSet <TObject, TKey> > Run()
        {
            return(Observable.Create <IChangeSet <TObject, TKey> >(observer =>
            {
                long orderItemWasAdded = -1;
                var locker = new object();

                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());
            }));
        }
예제 #2
0
        public void Remove(TObject item)
        {
            if (_keySelector == null)
            {
                throw new KeySelectorException("A key selector must be specified");
            }

            var key = _keySelector.GetKey(item);

            _cache.Remove(key);
        }
예제 #3
0
        public IChangeSet<TObject, TKey> Change(IChangeSet<ExpirableItem<TObject, TKey>, TKey> updates)
        {
            _cache.Clone(updates);

            var itemstoexpire = _cache.KeyValues
                                      .OrderByDescending(exp => exp.Value.ExpireAt)
                                      .Skip(_sizeLimit)
                                      .Select(exp => new Change<TObject, TKey>(ChangeReason.Remove, exp.Key, exp.Value.Value))
                                      .ToList();

            if (itemstoexpire.Count > 0)
            {
                _cache.Remove(itemstoexpire.Select(exp => exp.Key));
            }

            var notifications = _cache.CaptureChanges();
            var changed = notifications.Select(update => new Change<TObject, TKey>
                                                   (
                                                   update.Reason,
                                                   update.Key,
                                                   update.Current.Value,
                                                   update.Previous.HasValue ? update.Previous.Value.Value : Optional<TObject>.None
                                                   ));

            return new ChangeSet<TObject, TKey>(changed);
        }
예제 #4
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.Length > 0)
            {
                errors.ForEach(t => _exceptionCallback?.Invoke(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.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));
        }
예제 #5
0
        public static IChangeSet <TObject, TKey> RefreshFilteredFrom <TObject, TKey>(this ChangeAwareCache <TObject, TKey> filtered, Cache <TObject, TKey> allData, Func <TObject, bool> predicate)
            where TKey : notnull
        {
            if (allData.Count == 0)
            {
                return(ChangeSet <TObject, TKey> .Empty);
            }

            foreach (var kvp in allData.KeyValues)
            {
                var existing = filtered.Lookup(kvp.Key);
                var matches  = predicate(kvp.Value);

                if (matches)
                {
                    if (!existing.HasValue)
                    {
                        filtered.Add(kvp.Value, kvp.Key);
                    }
                }
                else
                {
                    if (existing.HasValue)
                    {
                        filtered.Remove(kvp.Key);
                    }
                }
            }

            return(filtered.CaptureChanges());
        }
예제 #6
0
        private void ProcessItem(ChangeAwareCache <TObject, TKey> target, MergeContainer[] sourceLists, TObject item, TKey key)
        {
            //TODO: Check whether individual items should be updated

            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);
                }
            }
        }
예제 #7
0
        private IChangeSet <TDestination, TKey> ProcessUpdates(ChangeAwareCache <TDestination, 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.Destination.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));
                }
            }

            return(cache.CaptureChanges());
        }
        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));
        }
예제 #9
0
        private IChangeSet <TObject, TKey> UpdateCombined(IChangeSet <TObject, TKey> updates)
        {
            //child caches have been updated before we reached this point.

            foreach (var update in updates)
            {
                TKey key = update.Key;
                switch (update.Reason)
                {
                case ChangeReason.Add:
                case ChangeReason.Update:
                {
                    // get the current key.
                    //check whether the item should belong to the cache
                    var cached    = _combinedCache.Lookup(key);
                    var contained = cached.HasValue;
                    var match     = MatchesConstraint(key);

                    if (match)
                    {
                        if (contained)
                        {
                            if (!ReferenceEquals(update.Current, cached.Value))
                            {
                                _combinedCache.AddOrUpdate(update.Current, key);
                            }
                        }
                        else
                        {
                            _combinedCache.AddOrUpdate(update.Current, key);
                        }
                    }
                    else
                    {
                        if (contained)
                        {
                            _combinedCache.Remove(key);
                        }
                    }
                }

                break;

                case ChangeReason.Remove:
                {
                    var  cached           = _combinedCache.Lookup(key);
                    var  contained        = cached.HasValue;
                    bool shouldBeIncluded = MatchesConstraint(key);

                    if (shouldBeIncluded)
                    {
                        var firstOne = _sourceCaches.Select(s => s.Lookup(key))
                                       .SelectValues()
                                       .First();

                        if (!cached.HasValue)
                        {
                            _combinedCache.AddOrUpdate(firstOne, key);
                        }
                        else if (!ReferenceEquals(firstOne, cached.Value))
                        {
                            _combinedCache.AddOrUpdate(firstOne, key);
                        }
                    }
                    else
                    {
                        if (contained)
                        {
                            _combinedCache.Remove(key);
                        }
                    }
                }

                break;

                case ChangeReason.Refresh:
                {
                    _combinedCache.Refresh(key);
                }

                break;
                }
            }

            return(_combinedCache.CaptureChanges());
        }
        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());
            }));
        }
예제 #11
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;
                }
            }
        }