Преглед на файлове

Started work on QueueUpdater mechanism to allow for cached data updates

Frank van den Bos преди 9 месеца
родител
ревизия
8c623ac010

+ 3 - 0
InABox.Mobile/InABox.Mobile.Shared/DataModels/IModelHost.cs

@@ -1,4 +1,5 @@
 using System;
+using InABox.Core;
 
 namespace InABox.Mobile
 {
@@ -31,5 +32,7 @@ namespace InABox.Mobile
         bool IsBackgroundUpdateStatusActive { get; }
         
         event BackgroundUpdateStatusEvent BackgroundUpdateStatusChanged;
+        void AddUpdateQueue<TEntity>(QueueUpdater<TEntity> queueUpdater) where TEntity : Entity, IPersistent, IRemotable, new();
+        void RemoveUpdateQueue<TEntity>(QueueUpdater<TEntity> queueUpdater) where TEntity : Entity, IPersistent, IRemotable, new();
     }
 }

+ 137 - 0
InABox.Mobile/InABox.Mobile.Shared/DataModels/QueuedUpdater/IQueuedUpdater.cs

@@ -0,0 +1,137 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Threading;
+using System.Threading.Tasks;
+using InABox.Clients;
+using InABox.Core;
+using Newtonsoft.Json.Linq;
+
+namespace InABox.Mobile
+{
+
+    public interface IQueueUpdater
+    {
+        void ProcessQueue();
+    }
+    
+    public interface IQueueUpdater<TEntity> : IQueueUpdater
+        where TEntity : Entity, IPersistent, IRemotable, new()
+    {
+        void Add(TEntity entity);
+
+        TEntity[] Items { get; }
+    }
+
+    public class QueuedUpdaterState
+    {
+        public Guid ID { get; set; }
+        public string OriginalState { get; set; }
+        public Dictionary<string,object> Changes { get; set; } = new();
+        
+        public bool DisableUpdates { get; set; }
+    }
+    
+    public class QueueUpdater<TEntity> : IDisposable, IQueueUpdater<TEntity> 
+        where TEntity : Entity, IPersistent, IRemotable, new()
+    {
+        
+        private static String StateFile(Guid id) => $"{id}.{typeof(TEntity).Name.Split('.').Last()}";
+ 
+        private Dictionary<TEntity, QueuedUpdaterState> _cache = new();
+
+        private string _path;
+        private IModelHost _host;
+
+        public QueueUpdater(IModelHost host, string path)
+        {
+            _host = host;
+            _path = path;
+            LoadCache();
+            _host.AddUpdateQueue(this);
+        }
+        
+        public void Add(TEntity entity)
+        {
+            var _state = new QueuedUpdaterState();
+            _state.ID = Guid.NewGuid();
+            _state.OriginalState = Serialization.Serialize(entity);
+            Save(_state);
+            _cache[entity] = _state;
+            
+            entity.PropertyChanged += (sender, args) =>
+            {
+                var state = _cache[entity];
+                state.Changes[args.PropertyName] = CoreUtils.GetPropertyValue(entity,args.PropertyName);
+                Save(state);
+            };
+        }
+
+        public void Update(TEntity entity, Action<TEntity> action)
+        {
+            var state = _cache[entity];
+            state.DisableUpdates = true;
+            action(entity);
+            Save(state);
+            state.DisableUpdates = false;
+        }
+
+        private void Save(QueuedUpdaterState state)
+        {
+            var _text = Serialization.Serialize(state);
+            File.WriteAllText(StateFile(state.ID), _text);
+        }
+
+        public TEntity[] Items => _cache.Keys.ToArray();
+        
+        private void LoadCache()
+        {
+            _cache.Clear();
+            var files = Directory.GetFiles(_path, $"*.{typeof(TEntity).Name.Split('.').Last()}");
+            foreach (var file in files)
+            {
+                if (!Guid.TryParse(Path.GetFileNameWithoutExtension(file), out Guid id))
+                    continue;
+                
+                var _text = File.ReadAllText(file);
+                if (string.IsNullOrWhiteSpace(_text))
+                    continue;
+                
+                var _state = Serialization.Deserialize<QueuedUpdaterState>(_text);
+                if (_state == null)
+                    continue;
+                
+                var _entity = Serialization.Deserialize<TEntity>(_state.OriginalState);
+                if (_entity == null)
+                    continue;
+                
+                foreach (var change in _state.Changes) 
+                    CoreUtils.SetPropertyValue(_entity,change.Key,change.Value);        
+                
+                _cache[_entity] = _state;
+            }
+        }
+
+        public void ProcessQueue()
+        {
+            var item = Items.FirstOrDefault();
+            if (item != null)
+            {
+                if (item.IsChanged() && _host.IsConnected())
+                {
+                    new Client<TEntity>().Save(item, "Updated from Mobile Device");
+                    var state = Serialization.Serialize(item);
+                    File.WriteAllText(StateFile(_cache[item].ID),state);
+                }
+            }
+        }
+        
+        public void Dispose()
+        {
+            _host.RemoveUpdateQueue(this);
+        }
+    }
+    
+}