diff --git a/Schaad.Accounting.Db/Repositories/AccountRepository.cs b/Schaad.Accounting.Db/Repositories/AccountRepository.cs
index 1061dc9..11a7070 100644
--- a/Schaad.Accounting.Db/Repositories/AccountRepository.cs
+++ b/Schaad.Accounting.Db/Repositories/AccountRepository.cs
@@ -11,20 +11,16 @@ namespace Schaad.Accounting.Repositories
{
private readonly string ACCOUNTS = "Accounts.xml";
- public AccountRepository(ISettingsService settingsService) : base(settingsService)
+ public AccountRepository(ISettingsService settingsService, RepositoryCache cache)
+ : base(settingsService, cache)
{
EnsureAccountsFile();
}
-
///
/// Load accounts
///
- public List GetAccountList()
- {
- var accounts = Load>(ACCOUNTS);
- return accounts ?? new List();
- }
+ public List GetAccountList() => LoadList(ACCOUNTS);
///
/// Save an account (insert/update)
diff --git a/Schaad.Accounting.Db/Repositories/BankTransactionRepository.cs b/Schaad.Accounting.Db/Repositories/BankTransactionRepository.cs
index 10d3c6a..9b3c970 100644
--- a/Schaad.Accounting.Db/Repositories/BankTransactionRepository.cs
+++ b/Schaad.Accounting.Db/Repositories/BankTransactionRepository.cs
@@ -10,18 +10,15 @@ namespace Schaad.Accounting.Repositories
{
private readonly string BANK_TRANSACTIONS = "BankTransactions.xml";
- public BankTransactionRepository(ISettingsService settingsService) : base(settingsService)
+ public BankTransactionRepository(ISettingsService settingsService, RepositoryCache cache)
+ : base(settingsService, cache)
{
}
///
/// Load bank transactions
///
- public List GetBankTransactionList()
- {
- var transactions = Load>(BANK_TRANSACTIONS);
- return transactions != null ? transactions : new List();
- }
+ public List GetBankTransactionList() => LoadList(BANK_TRANSACTIONS);
///
/// Save new bank transactions, ignore existing ones (no update)
diff --git a/Schaad.Accounting.Db/Repositories/BaseRepository.cs b/Schaad.Accounting.Db/Repositories/BaseRepository.cs
index 4411e15..3813fa8 100644
--- a/Schaad.Accounting.Db/Repositories/BaseRepository.cs
+++ b/Schaad.Accounting.Db/Repositories/BaseRepository.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Concurrent;
+using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Xml;
@@ -16,10 +17,12 @@ namespace Schaad.Accounting.Repositories
new(StringComparer.OrdinalIgnoreCase);
protected readonly ISettingsService settingsService;
+ protected readonly RepositoryCache cache;
- protected BaseRepository(ISettingsService settingsService)
+ protected BaseRepository(ISettingsService settingsService, RepositoryCache cache)
{
this.settingsService = settingsService;
+ this.cache = cache;
}
protected void EnsureFileExists(string fileName)
@@ -38,9 +41,20 @@ namespace Schaad.Accounting.Repositories
}
}
+ ///
+ /// Load a list, cached per scope. First call reads the XML file; subsequent calls in
+ /// the same scope return the cached list. invalidates the entry.
+ ///
+ protected List LoadList(string fileName)
+ {
+ var filePath = Path.Combine(settingsService.GetDbPath(), fileName);
+ return cache.GetOrLoad(filePath, () => Load>(fileName) ?? new List());
+ }
+
///
/// Save an object to an xml file. Writes to a .tmp sibling and then atomically
- /// renames it, so a crash mid-write leaves the previous file intact.
+ /// renames it, so a crash mid-write leaves the previous file intact. Invalidates
+ /// the corresponding cache entry.
///
protected void Save(T obj, string fileName)
{
@@ -74,10 +88,13 @@ namespace Schaad.Accounting.Repositories
throw;
}
}
+
+ cache.Invalidate(filePath);
}
///
- /// Load an object from an xml file
+ /// Load an object from an xml file (uncached; used for one-off reads such as file
+ /// existence bootstrapping). Prefer for repository reads.
///
protected T Load(string fileName)
{
diff --git a/Schaad.Accounting.Db/Repositories/BookingRuleRepository.cs b/Schaad.Accounting.Db/Repositories/BookingRuleRepository.cs
index 619fce4..34c04b1 100644
--- a/Schaad.Accounting.Db/Repositories/BookingRuleRepository.cs
+++ b/Schaad.Accounting.Db/Repositories/BookingRuleRepository.cs
@@ -10,7 +10,8 @@ namespace Schaad.Accounting.Repositories
{
private readonly string BOOKING_RULES = "BookingRules.xml";
- public BookingRuleRepository(ISettingsService settingsService) : base(settingsService)
+ public BookingRuleRepository(ISettingsService settingsService, RepositoryCache cache)
+ : base(settingsService, cache)
{
EnsureFileExists(BOOKING_RULES);
}
@@ -18,11 +19,7 @@ namespace Schaad.Accounting.Repositories
///
/// Load booking rules
///
- public List GetBookingRuleList()
- {
- var bookingTexts = Load>(BOOKING_RULES);
- return bookingTexts ?? new List();
- }
+ public List GetBookingRuleList() => LoadList(BOOKING_RULES);
///
/// Save a booking rule (insert/update)
diff --git a/Schaad.Accounting.Db/Repositories/BookingTextRepository.cs b/Schaad.Accounting.Db/Repositories/BookingTextRepository.cs
index d3fa0c7..06c66ec 100644
--- a/Schaad.Accounting.Db/Repositories/BookingTextRepository.cs
+++ b/Schaad.Accounting.Db/Repositories/BookingTextRepository.cs
@@ -10,19 +10,16 @@ namespace Schaad.Accounting.Repositories
{
private readonly string BOOKING_TEXTS = "BookingTexts.xml";
- public BookingTextRepository(ISettingsService settingsService) : base(settingsService)
+ public BookingTextRepository(ISettingsService settingsService, RepositoryCache cache)
+ : base(settingsService, cache)
{
EnsureFileExists(BOOKING_TEXTS);
}
///
- /// Load bokking texts
+ /// Load booking texts
///
- public List GetBookingTextList()
- {
- var bookingTexts = Load>(BOOKING_TEXTS);
- return bookingTexts ?? new List();
- }
+ public List GetBookingTextList() => LoadList(BOOKING_TEXTS);
///
/// Save a booking text (insert/update)
diff --git a/Schaad.Accounting.Db/Repositories/SplitPredefinitionRepository.cs b/Schaad.Accounting.Db/Repositories/SplitPredefinitionRepository.cs
index ac80d29..a73dae8 100644
--- a/Schaad.Accounting.Db/Repositories/SplitPredefinitionRepository.cs
+++ b/Schaad.Accounting.Db/Repositories/SplitPredefinitionRepository.cs
@@ -10,7 +10,8 @@ namespace Schaad.Accounting.Repositories
{
private readonly string SPLIT_PREDEFINITION = "SplitPredefinitions.xml";
- public SplitPredefinitionRepository(ISettingsService settingsService) : base(settingsService)
+ public SplitPredefinitionRepository(ISettingsService settingsService, RepositoryCache cache)
+ : base(settingsService, cache)
{
EnsureFileExists(SPLIT_PREDEFINITION);
}
@@ -18,11 +19,7 @@ namespace Schaad.Accounting.Repositories
///
/// Load split predefinitions
///
- public List GetSplitPredefinitionList()
- {
- var definitions = Load>(SPLIT_PREDEFINITION);
- return definitions ?? new List();
- }
+ public List GetSplitPredefinitionList() => LoadList(SPLIT_PREDEFINITION);
///
/// Save a split predefinition (insert/update)
diff --git a/Schaad.Accounting.Db/Repositories/SubclassRepository.cs b/Schaad.Accounting.Db/Repositories/SubclassRepository.cs
index f2ecb7c..1f4f54f 100644
--- a/Schaad.Accounting.Db/Repositories/SubclassRepository.cs
+++ b/Schaad.Accounting.Db/Repositories/SubclassRepository.cs
@@ -11,7 +11,8 @@ namespace Schaad.Accounting.Repositories
private readonly Dictionary classes = new Dictionary();
private readonly string SUBCLASSES = "SubClasses.xml";
- public SubclassRepository(ISettingsService settingsService) : base(settingsService)
+ public SubclassRepository(ISettingsService settingsService, RepositoryCache cache)
+ : base(settingsService, cache)
{
EnsureFileExists(SUBCLASSES);
@@ -22,13 +23,9 @@ namespace Schaad.Accounting.Repositories
}
///
- /// Load bokking texts
+ /// Load subclasses
///
- public List GetSubClassList()
- {
- var subclasses = Load>(SUBCLASSES);
- return subclasses ?? new List();
- }
+ public List GetSubClassList() => LoadList(SUBCLASSES);
///
/// Save a booking text (insert/update)
diff --git a/Schaad.Accounting.Db/Repositories/TransactionRepository.cs b/Schaad.Accounting.Db/Repositories/TransactionRepository.cs
index ddf294a..0e55408 100644
--- a/Schaad.Accounting.Db/Repositories/TransactionRepository.cs
+++ b/Schaad.Accounting.Db/Repositories/TransactionRepository.cs
@@ -11,7 +11,8 @@ namespace Schaad.Accounting.Repositories
private readonly IAccountRepository accountRepository;
private readonly string TRANSACTIONS = "Transactions.xml";
- public TransactionRepository(ISettingsService settingsService, IAccountRepository accountRepository) : base(settingsService)
+ public TransactionRepository(ISettingsService settingsService, RepositoryCache cache, IAccountRepository accountRepository)
+ : base(settingsService, cache)
{
this.accountRepository = accountRepository;
}
@@ -19,11 +20,7 @@ namespace Schaad.Accounting.Repositories
///
/// Get transaction list
///
- public List GetTransactionList()
- {
- var transactionList = Load>(TRANSACTIONS);
- return transactionList ?? new List();
- }
+ public List GetTransactionList() => LoadList(TRANSACTIONS);
///
/// Save a transaction (insert/update)
diff --git a/Schaad.Accounting.Db/RepositoryCache.cs b/Schaad.Accounting.Db/RepositoryCache.cs
new file mode 100644
index 0000000..e1834af
--- /dev/null
+++ b/Schaad.Accounting.Db/RepositoryCache.cs
@@ -0,0 +1,38 @@
+using System;
+using System.Collections.Generic;
+
+namespace Schaad.Accounting.Repositories
+{
+ ///
+ /// Per-scope cache of deserialized XML lists, keyed by absolute file path.
+ ///
+ /// Registered as Scoped so it lives for the SignalR circuit; each XML file is
+ /// then loaded at most once per circuit. Writes go through ,
+ /// which invalidates the corresponding entry so the next read reloads from disk.
+ ///
+ /// Not thread-safe — callers within a single Blazor Server circuit run serialized on the
+ /// circuit's synchronization context, so no locking is needed.
+ ///
+ public class RepositoryCache
+ {
+ private readonly Dictionary entries =
+ new(StringComparer.OrdinalIgnoreCase);
+
+ public List GetOrLoad(string cacheKey, Func> loader)
+ {
+ if (entries.TryGetValue(cacheKey, out var cached))
+ {
+ return (List)cached;
+ }
+
+ var list = loader();
+ entries[cacheKey] = list;
+ return list;
+ }
+
+ public void Invalidate(string cacheKey)
+ {
+ entries.Remove(cacheKey);
+ }
+ }
+}
diff --git a/Schaad.Accounting.UI/Extensions.cs b/Schaad.Accounting.UI/Extensions.cs
index b564a45..384173b 100644
--- a/Schaad.Accounting.UI/Extensions.cs
+++ b/Schaad.Accounting.UI/Extensions.cs
@@ -16,6 +16,7 @@ namespace Schaad.Accounting.UI
// can safely become Scoped (see IMPROVEMENT_PLAN.md).
services.AddSingleton();
+ services.AddScoped();
services.AddScoped();
services.AddScoped();
services.AddScoped();