Browse Source

Pulled some of the DocumentCache system from DataEntry into the Core DocumentCache file.

Kenric Nugteren 7 months ago
parent
commit
e147655aa8
2 changed files with 146 additions and 6 deletions
  1. 133 1
      InABox.Core/DocumentCache.cs
  2. 13 5
      inabox.wpf/TemplateGenerator.cs

+ 133 - 1
InABox.Core/DocumentCache.cs

@@ -86,7 +86,8 @@ namespace InABox.Core
     }
 
     /// <summary>
-    /// A cache of documents that is saved in the AppData folder under a specific tag.
+    /// A cache of documents that is saved in the AppData folder under a specific tag. Contrary to the name, this isn't just a <see cref="Document"/> cache, but
+    /// in fact a way to cache any kind of object that implements <see cref="ICachedDocument"/>.
     /// </summary>
     /// <remarks>
     /// The files are stored with the name "&lt;ID&gt;.document", and they contain a binary serialised <see cref="CachedDocument"/>.
@@ -431,4 +432,135 @@ namespace InABox.Core
 
         #endregion
     }
+
+    /// <summary>
+    /// An implementation of <see cref="ICachedDocument"/> for use with entities of type <see cref="Document"/>. The <see cref="Document.TimeStamp"/>
+    /// is saved along with the document, allowing us to refresh updated documents.
+    /// </summary>
+    public class DocumentCachedDocument : ICachedDocument
+    {
+        public DateTime TimeStamp { get; set; }
+
+        public Document? Document { get; set; }
+
+        public Guid ID => Document?.ID ?? Guid.Empty;
+
+        public DocumentCachedDocument() { }
+
+        public DocumentCachedDocument(Document document)
+        {
+            Document = document;
+            TimeStamp = document.TimeStamp;
+        }
+
+        public void DeserializeBinary(CoreBinaryReader reader, bool full)
+        {
+            TimeStamp = reader.ReadDateTime();
+            if (full)
+            {
+                Document = reader.ReadObject<Document>();
+            }
+        }
+
+        public void SerializeBinary(CoreBinaryWriter writer)
+        {
+            writer.Write(TimeStamp);
+            if (Document is null)
+            {
+                throw new Exception("Cannot serialize incomplete CachedDocument");
+            }
+            writer.WriteObject(Document);
+        }
+    }
+
+    /// <summary>
+    /// Implements a <see cref="DocumentCache{T}"/> for use with <see cref="Document"/>.
+    /// </summary>
+    public abstract class DocumentCache : DocumentCache<DocumentCachedDocument>
+    {
+        public DocumentCache(string tag): base(tag) { }
+
+        protected override DocumentCachedDocument? LoadDocument(Guid id)
+        {
+            var document = Client.Query(new Filter<Document>(x => x.ID).IsEqualTo(id))
+                .ToObjects<Document>().FirstOrDefault();
+            if(document != null)
+            {
+                return new DocumentCachedDocument(document);
+            }
+            else
+            {
+                return null;
+            }
+        }
+        
+        /// <summary>
+        /// Fetch a bunch of documents from the cache or the database, optionally checking against the timestamp listed in the database.
+        /// </summary>
+        /// <param name="ids"></param>
+        /// <param name="checkTimestamp">
+        /// If <see langword="true"/>, then loads <see cref="Document.TimeStamp"/> from the database for all cached documents,
+        /// and if they are older, updates the cache.
+        /// </param>
+        public IEnumerable<Document> LoadDocuments(IEnumerable<Guid> ids, bool checkTimestamp = false)
+        {
+            var cached = new List<Guid>();
+            var toLoad = new List<Guid>();
+            foreach (var docID in ids)
+            {
+                if (Has(docID))
+                {
+                    cached.Add(docID);
+                }
+                else
+                {
+                    toLoad.Add(docID);
+                }
+            }
+
+            var loadedCached = new List<Document>();
+            if (cached.Count > 0)
+            {
+                var docs = Client.Query(
+                    new Filter<Document>(x => x.ID).InList(cached.ToArray()),
+                    Columns.None<Document>().Add(x => x.TimeStamp, x => x.ID));
+                foreach (var doc in docs.ToObjects<Document>())
+                {
+                    try
+                    {
+                        var timestamp = GetHeader(doc.ID).Document.TimeStamp;
+                        if (doc.TimeStamp > timestamp)
+                        {
+                            toLoad.Add(doc.ID);
+                        }
+                        else
+                        {
+                            loadedCached.Add(GetFull(doc.ID).Document.Document!);
+                        }
+                    }
+                    catch (Exception e)
+                    {
+                        CoreUtils.LogException("", e, "Error loading cached file");
+                        toLoad.Add(doc.ID);
+                    }
+                }
+            }
+
+            if (toLoad.Count > 0)
+            {
+                var loaded = Client.Query(new Filter<Document>(x => x.ID).InList(toLoad.ToArray()))
+                    .ToObjects<Document>().ToList();
+                foreach (var loadedDoc in loaded)
+                {
+                    Add(new DocumentCachedDocument(loadedDoc));
+                }
+                return loaded.Concat(loadedCached);
+            }
+            else
+            {
+                return loadedCached;
+            }
+        }
+    }
 }
+

+ 13 - 5
inabox.wpf/TemplateGenerator.cs

@@ -1,4 +1,5 @@
-using System;
+using Org.BouncyCastle.Asn1.Mozilla;
+using System;
 using System.Windows;
 using System.Windows.Controls;
 
@@ -30,17 +31,24 @@ namespace InABox.WPF
             if (controlType == null)
                 throw new ArgumentNullException("controlType");
 
-            if (factory == null)
-                throw new ArgumentNullException("factory");
-
             var frameworkElementFactory = new FrameworkElementFactory(typeof(_TemplateGeneratorControl));
             frameworkElementFactory.SetValue(_TemplateGeneratorControl.FactoryProperty, factory);
 
             var controlTemplate = new ControlTemplate(controlType);
-            controlTemplate.VisualTree = frameworkElementFactory;
+            controlTemplate.VisualTree = CreateFactory(factory);
             return controlTemplate;
         }
 
+        public static FrameworkElementFactory CreateFactory(Func<object?> factory)
+        {
+            if (factory == null)
+                throw new ArgumentNullException("factory");
+
+            var frameworkElementFactory = new FrameworkElementFactory(typeof(_TemplateGeneratorControl));
+            frameworkElementFactory.SetValue(_TemplateGeneratorControl.FactoryProperty, factory);
+            return frameworkElementFactory;
+        }
+
         private sealed class _TemplateGeneratorControl :
             ContentControl
         {