Пример #1
0
        private void LoadData(ModelIdentifierBase id, ModelSource source, Subject <IOperationState <Stream> > target, CancellationToken ct = default)
        {
            ICache cache = GetCache(id);

            switch (source)
            {
            case ModelSource.Server:
                target.OnResult(it => cache?.Save(id, it));
                LoadImplementation(id, target, ct);
                break;

            case ModelSource.Disk:
                if (cache == null)
                {
                    target.OnNextError(new OperationError(message: "Disk cache not set!"), 0, ModelSource.Disk);
                    target.OnError(new InvalidOperationException("Disk cache not set!"));
                    //target.OnCompleted();
                }
                else
                {
                    cache.Load(id, target, ct);
                }
                break;

            default:
                throw new Exception("Unknown source: " + source);
            }
        }
Пример #2
0
        public static IDisposable SubscribeStateChange <TResult>(this IObservable <IOperationState <TResult> > self, Action <TResult> onResult = null, Action <double> onProgress = null, Action <OperationError> onError = null, Action <IOperationState <TResult> > onCompleted = null) where TResult : class
        {
            TResult             result = null;
            ModelSource         source = ModelSource.Unknown;
            ModelIdentifierBase id     = null;
            double         progress    = 0;
            OperationError error       = null;
            bool           isCancelled = false;

            return(self
                   .Subscribe(state => {
                TryFire(onProgress, ref progress, state.Progress);
                TryFire(onError, ref error, state.Error);

                if (TryFire(onResult, ref result, state.Result))
                {
                    id = state.ResultIdentifier;
                    source = state.ResultSource;
                }

                isCancelled = state.IsCancelled;
            },
                              () => onCompleted?.Invoke(new OperationState <TResult>(result, progress, error, isCancelled, source, id))
                              ));
        }
Пример #3
0
        private OperationEntry CreateOperationEntry <T>(ModelIdentifierBase id, ModelSource source, CancellationToken ct, OperationKey key) where T : class
        {
            var combinedCts  = new CancellationTokenSource();
            var newOperation = GetModel <T>(id, source, combinedCts.Token);

            IDisposable connectDisposable      = null;
            IDisposable subscriptionDisposable = null;

            void onFinished()
            {
                _ongoingOperations.TryRemove(key, out OperationEntry obj);

                subscriptionDisposable?.Dispose();
                connectDisposable?.Dispose();
            }

            subscriptionDisposable = newOperation.Subscribe(__ => { }, __ => onFinished(), onFinished);

            _operationsObserver.OnNext(newOperation);
            var newEntry = new OperationEntry(newOperation, ct,
                                              () =>
            {
                combinedCts.Cancel();
                onFinished();
            }
                                              );

            return(newEntry);
        }
Пример #4
0
        private IObservable <IOperationState <T> > GetFromCache <T>(ModelIdentifierBase id, CancellationToken ct) where T : class
        {
            var result = _cache?.Get <T>(id);

            return(result != null?
                   Observable.Return(new OperationState <T>(result, 100, id : id, source : ModelSource.Memory)) :
                   Get <T>(id, ModelSource.Disk, ct));
        }
Пример #5
0
        public IObservable <IOperationState <object> > Write(ModelIdentifierBase id, UpdateContainer update, ModelSource target, CancellationToken ct = default)
        {
            var replayStates = new ReplaySubject <IOperationState <object> >();

            WriteImplementation(id, update, target, replayStates, ct);

            return(replayStates);
        }
Пример #6
0
        public T Get <T>(ModelIdentifierBase id) where T : class
        {
            if (_cache.TryGetValue(id, out object item))
            {
                return((T)item);
            }

            return(null);
        }
Пример #7
0
        public override bool Equals(ModelIdentifierBase other)
        {
            if (other == null)
            {
                return(false);
            }

            return(base.Equals(other) || typeof(ModelIdentifier <T>) == other.GetType());
        }
Пример #8
0
        private IObservable <IOperationState <T> > GetModel <T>(ModelIdentifierBase id, ModelSource source, CancellationToken ct = default) where T : class
        {
            var operation = _loader.Load(id, source, ct: ct);

            operation
            .WhereResultChanged()
            .Where(state => state.ResultProgress == 100)
            .Subscribe(state =>
            {
                var result   = state.Result;
                var resultId = state.ResultIdentifier;

                if (id.Equals(resultId))
                {
                    // We want to run the updates within the synced AddOrUpdate, but only if we actually have updates for this model.
                    UpdateContainer _ = null;
                    if (_updates.TryGetValue(resultId, out _) && _ != null)
                    {
                        _updates.AddOrUpdate(resultId, (UpdateContainer)null,
                                             (__, modelUpdates) =>
                        {
                            var updateableResult  = result as IUpdateableModel <T>;
                            modelUpdates.Original = updateableResult.CloneForUpdate();
                            modelUpdates.ForEach(entry => result = entry.Update(result) as T);
                            modelUpdates.Updated = result;
                            return(modelUpdates);
                        }
                                             );
                    }
                }

                // Result from disk probably shouldn't overwrite result from server in the memory cache?
                if (_cache != null)
                {
                    _cache.Set(resultId, result);
                }
            });

            return(operation
                   // TODO: Should we start with an empty operationstate ?
                   .Select(state =>
            {
                var isMatch = id.Equals(state.ResultIdentifier);
                return new OperationState <T>(
                    isMatch ? state.Result as T : null,
                    state.Progress,
                    state.Error,
                    state.IsCancelled,
                    state.ResultSource,
                    isMatch ? state.ResultIdentifier : null,
                    isMatch ? state.ResultProgress : 0
                    );
            })
                   .TakeWhile(s => s.Progress <= 100));
        }
Пример #9
0
        private IObservable <IOperationState <T> > GetFirstFromCacheThenServer <T>(ModelIdentifierBase id, CancellationToken ct) where T : class
        {
            var resultFromCache = GetFromCache <T>(id, ct)
                                  .Where(it => it.Error == null && !it.IsCancelled)
                                  .Select(it => new OperationState <T>(it.Result, 0.5 * it.Progress, it.Error, it.IsCancelled, it.ResultSource));

            var resultFromServer = Get <T>(id, ModelSource.Server, ct)
                                   .Select(it => new OperationState <T>(it.Result, 0.5 * (100 + it.Progress), it.Error, it.IsCancelled, it.ResultSource));

            return(resultFromCache.Merge(resultFromServer));
        }
Пример #10
0
 protected override object ParseImplementation(ModelIdentifierBase id, Stream data)
 {
     using (var reader = XmlReader.Create(data, new XmlReaderSettings {
         IgnoreProcessingInstructions = true, DtdProcessing = DtdProcessing.Ignore
     }))
     {
         var parser = new DataContractSerializer(type);
         var obj    = parser.ReadObject(reader);
         return(obj);
     }
 }
Пример #11
0
        public IObservable <IOperationState <object> > Load(ModelIdentifierBase id, ModelSource source, double loadOperationProgressShare = 0.8, CancellationToken ct = default)
        {
            var loadOperationStates  = new Subject <IOperationState <Stream> >();
            var parseOperationStates = new Subject <IOperationState <object> >();

            // Loading and parsing might be running in parallel so we need some logic to combine the state updates
            var combinedReplayStates = new ReplaySubject <IOperationState <object> >();

            Observable.CombineLatest(
                loadOperationStates
                .OnResult(buffer => ParseImplementation(id, buffer, parseOperationStates, ct))
                .StartWith(null as IOperationState <Stream>),

                parseOperationStates
                .StartWith(null as IOperationState <object>),

                (load, parse) => Tuple.Create(load, parse)
                )
            .Buffer(2, 1)
            .Where(pairs => pairs.Count == 2)     // Last buffer
            .Select(pairs =>
            {
                var olds     = pairs[0];
                var oldLoad  = olds?.Item1;
                var oldParse = olds?.Item2;

                var news     = pairs[1];
                var newLoad  = news.Item1;
                var newParse = news.Item2;

                var loadError   = newLoad.Error ?? oldLoad?.Error;
                var parseError  = newParse?.Error ?? oldParse?.Error;
                var latestError = oldLoad != newLoad
                                        ? loadError ?? parseError
                                        : parseError ?? loadError;

                return(new OperationState <object>(
                           newParse?.Result,
                           newParse?.Progress == 100
                            ? 100
                            : newLoad.Progress *loadOperationProgressShare + ((newParse?.Progress ?? 0) * (1.0 - loadOperationProgressShare)),
                           latestError,
                           false, // TODO: Cancelled?
                           source,
                           newParse?.ResultIdentifier,
                           newParse?.ResultProgress ?? 0));
            })
            .Subscribe(combinedReplayStates);

            LoadData(id, source, loadOperationStates, ct);

            return(combinedReplayStates);
        }
Пример #12
0
 public void Parse(ModelIdentifierBase id, Stream data, IObserver <IOperationState <object> > target)
 {
     Task.Run(() => {
         try
         {
             ParseImplementation(id, data, target);
         }
         catch (Exception e)
         {
             target.OnNextError(new OperationError(e), 100, ModelSource.Server);
             target.OnCompleted();
         }
     });
 }
Пример #13
0
        private IObservable <IOperationState <T> > Get <T>(ModelIdentifierBase id, ModelSource source, CancellationToken ct = default) where T : class
        {
            var key = new OperationKey(id, source);

            var entry = _ongoingOperations.AddOrUpdate(key,
                                                       _ => CreateOperationEntry <T>(id, source, ct, key),
                                                       (_, oldEntry) => oldEntry.TryRegisterCancellation(ct)
                                    ? oldEntry
                                    : CreateOperationEntry <T>(id, source, ct, key)
                                                       );

            var operation = (IObservable <IOperationState <T> >)entry.Operation;

            return(operation
                   .TakeWhile(_ => !ct.IsCancellationRequested)
                   .Concat(Observable.Defer(() => ct.IsCancellationRequested
                                                ? Observable.Return(new OperationState <T>(isCancelled: true))
                                                : Observable.Empty <OperationState <T> >())));
        }
Пример #14
0
 protected abstract void LoadImplementation(ModelIdentifierBase id, IObserver <IOperationState <Stream> > target, CancellationToken ct = default);
Пример #15
0
        private IObservable <IOperationState <T> > GetFromCacheWithServerFallback <T>(ModelIdentifierBase id, CancellationToken ct) where T : class
        {
            var resultFromCache = GetFromCache <T>(id, ct);

            return(resultFromCache.WithFallback(() => Get <T>(id, ModelSource.Server, ct)));
        }
Пример #16
0
 protected abstract void WriteImplementation(ModelIdentifierBase id, UpdateContainer update, ModelSource target, IObserver <IOperationState <object> > operation, CancellationToken ct = default);
Пример #17
0
 public UpdateKey(ModelIdentifierBase identifier, object updateId)
 {
     Identifier = identifier;
     UpdateId   = updateId;
 }
Пример #18
0
 protected override void ParseImplementation(ModelIdentifierBase id, Stream data, IObserver <IOperationState <object> > target)
 {
     target.OnCompleteResult(ParseImplementation(id, data), id, 100, ModelSource.Server);
     target.OnCompleted();
 }
Пример #19
0
 public void Set <T>(ModelIdentifierBase id, T model) where T : class
 {
     _cache.AddOrUpdate(id, model, (_, __) => model);
 }
Пример #20
0
 protected abstract void ParseImplementation(ModelIdentifierBase id, Stream data, IObserver <IOperationState <object> > target);
Пример #21
0
 public static IObserver <IOperationState <TResult> > OnIncompleteResult <TResult>(this IObserver <IOperationState <TResult> > self, TResult result, ModelIdentifierBase id, double progress, double resultProgress, ModelSource source = ModelSource.Unknown) where TResult : class
 {
     self.OnNext(new OperationState <TResult>(result: result, id: id, progress: progress, source: source, resultProgress: resultProgress));
     return(self);
 }
Пример #22
0
 protected abstract object ParseImplementation(ModelIdentifierBase id, Stream data);
Пример #23
0
 public OperationKey(ModelIdentifierBase identifier, ModelSource source)
 {
     Identifier = identifier;
     Source     = source;
 }
Пример #24
0
 protected abstract void ParseImplementation(ModelIdentifierBase id, Stream data, IObserver <IOperationState <object> > target, CancellationToken ct = default);
Пример #25
0
 public override bool Equals(ModelIdentifierBase other)
 {
     return(this == other || (other as KeyedModelIdentifier <T, TKey>)?.Key.Equals(Key) == true);
 }
Пример #26
0
 protected virtual ICache GetCache(ModelIdentifierBase id)
 {
     return(_cache);
 }