From b1b426427cd33d58f87cff2979159d58163820dc Mon Sep 17 00:00:00 2001 From: Robert McRackan Date: Wed, 27 Nov 2019 16:57:35 -0500 Subject: [PATCH] Bugfix: initial bottom counts can throw error when a book was moved since Libation was last run --- DataLayer/UNTESTED/LibationContext.cs | 7 +- .../UNTESTED/TagPersistenceInterceptor.cs | 20 ++--- FileManager/UNTESTED/FilePathCache.cs | 84 +++++++++---------- LibationWinForm/UNTESTED/Form1.cs | 18 +--- 4 files changed, 55 insertions(+), 74 deletions(-) diff --git a/DataLayer/UNTESTED/LibationContext.cs b/DataLayer/UNTESTED/LibationContext.cs index 986d9e6d..1e9dd0a6 100644 --- a/DataLayer/UNTESTED/LibationContext.cs +++ b/DataLayer/UNTESTED/LibationContext.cs @@ -64,7 +64,8 @@ namespace DataLayer .Entity() .HasData(Contributor.GetEmpty()); - // views are now supported via "query types" (instead of "entity types"): https://docs.microsoft.com/en-us/ef/core/modeling/query-types - } - } + // views are now supported via "keyless entity types" (instead of "entity types" or the prev "query types"): + // https://docs.microsoft.com/en-us/ef/core/modeling/keyless-entity-types + } + } } diff --git a/DataLayer/UNTESTED/TagPersistenceInterceptor.cs b/DataLayer/UNTESTED/TagPersistenceInterceptor.cs index fcead7ac..bb6ff35d 100644 --- a/DataLayer/UNTESTED/TagPersistenceInterceptor.cs +++ b/DataLayer/UNTESTED/TagPersistenceInterceptor.cs @@ -4,7 +4,6 @@ using System.Linq; using Dinah.Core.Collections.Generic; using Dinah.EntityFrameworkCore; using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.ChangeTracking; namespace DataLayer { @@ -14,24 +13,17 @@ namespace DataLayer public void Executing(DbContext context) { - // persist tags: - var modifiedEntities = context + var tagsCollection + = context .ChangeTracker .Entries() - .Where(p => p.State.In(EntityState.Modified, EntityState.Added)) - .ToList(); - - persistTags(modifiedEntities); - } - - private static void persistTags(List modifiedEntities) - { - var tagsCollection = modifiedEntities + .Where(e => e.State.In(EntityState.Modified, EntityState.Added)) .Select(e => e.Entity as UserDefinedItem) - // filter by null but NOT by blank. blank is the valid way to show the absence of tags - .Where(a => a != null) + .Where(udi => udi != null) + // do NOT filter out entires with blank tags. blank is the valid way to show the absence of tags .Select(t => (t.Book.AudibleProductId, t.Tags)) .ToList(); + FileManager.TagsPersistence.Save(tagsCollection); } } diff --git a/FileManager/UNTESTED/FilePathCache.cs b/FileManager/UNTESTED/FilePathCache.cs index 2a8d9319..efc208ea 100644 --- a/FileManager/UNTESTED/FilePathCache.cs +++ b/FileManager/UNTESTED/FilePathCache.cs @@ -2,11 +2,12 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using Dinah.Core.Collections.Immutable; using Newtonsoft.Json; namespace FileManager { - public static class FilePathCache + public static class FilePathCache { internal class CacheEntry { @@ -15,22 +16,25 @@ namespace FileManager public string Path { get; set; } } - static List inMemoryCache { get; } = new List(); + static Cache cache { get; } = new Cache(); public static string JsonFile => Path.Combine(Configuration.Instance.LibationFiles, "FilePaths.json"); static FilePathCache() { - // load json into memory. if file doesn't exist, nothing to do. save() will create if needed - if (FileUtility.FileExists(JsonFile)) - inMemoryCache = JsonConvert.DeserializeObject>(File.ReadAllText(JsonFile)); + // load json into memory. if file doesn't exist, nothing to do. save() will create if needed + if (FileUtility.FileExists(JsonFile)) + { + var list = JsonConvert.DeserializeObject>(File.ReadAllText(JsonFile)); + cache = new Cache(list); + } } public static bool Exists(string id, FileType type) => GetPath(id, type) != null; public static string GetPath(string id, FileType type) { - var entry = inMemoryCache.SingleOrDefault(i => i.Id == id && i.FileType == type); + var entry = cache.SingleOrDefault(i => i.Id == id && i.FileType == type); if (entry == null) return null; @@ -44,51 +48,47 @@ namespace FileManager return entry.Path; } - private static object locker { get; } = new object(); - private static void remove(CacheEntry entry) - { - lock (locker) - { - inMemoryCache.Remove(entry); - save(); - } - } + { + cache.Remove(entry); + save(); + } public static void Upsert(string id, FileType type, string path) { if (!FileUtility.FileExists(path)) throw new FileNotFoundException("Cannot add path to cache. File not found"); - lock (locker) - { - var entry = inMemoryCache.SingleOrDefault(i => i.Id == id && i.FileType == type); - if (entry != null) - entry.Path = path; - else - { - entry = new CacheEntry { Id = id, FileType = type, Path = path }; - inMemoryCache.Add(entry); - } - save(); - } - } + var entry = cache.SingleOrDefault(i => i.Id == id && i.FileType == type); - // ONLY call this within lock() - private static void save() - { - // create json if not exists - void resave() => File.WriteAllText(JsonFile, JsonConvert.SerializeObject(inMemoryCache, Formatting.Indented)); - try { resave(); } - catch (IOException) - { - try { resave(); } - catch (IOException ex) + if (entry is null) + cache.Add(new CacheEntry { Id = id, FileType = type, Path = path }); + else + entry.Path = path; + + save(); + } + + // cache is thread-safe and lock free. but file saving is not + private static object locker { get; } = new object(); + private static void save() + { + // create json if not exists + static void resave() => File.WriteAllText(JsonFile, JsonConvert.SerializeObject(cache.ToList(), Formatting.Indented)); + + lock (locker) + { + try { resave(); } + catch (IOException) { - Serilog.Log.Logger.Error(ex, "Error saving FilePaths.json"); - throw; - } - } + try { resave(); } + catch (IOException ex) + { + Serilog.Log.Logger.Error(ex, "Error saving FilePaths.json"); + throw; + } + } + } } } } diff --git a/LibationWinForm/UNTESTED/Form1.cs b/LibationWinForm/UNTESTED/Form1.cs index 6404ec50..8cb83201 100644 --- a/LibationWinForm/UNTESTED/Form1.cs +++ b/LibationWinForm/UNTESTED/Form1.cs @@ -109,21 +109,9 @@ namespace LibationWinForm .Select(sp => sp.Book) .ToList(); - // will often fail once if book has been moved while libation is closed. just retry once for now - // fix actual issue later - // FilePathCache.GetPath() :: inMemoryCache.SingleOrDefault(i => i.Id == id && i.FileType == type) - try - { - setBookBackupCounts(books); - setPdfBackupCounts(books); - } - catch (Exception ex) - { - System.Threading.Thread.Sleep(100); - setBookBackupCounts(books); - setPdfBackupCounts(books); - } - } + setBookBackupCounts(books); + setPdfBackupCounts(books); + } enum AudioFileState { full, aax, none } private void setBookBackupCounts(IEnumerable books) {