Every repository, service, Blazor page/dialog, and test now uses
async/await. Single atomic diff; the codebase does not compile in
intermediate states.
- BaseRepository: LoadListAsync/LoadAsync/SaveAsync return Task<T>;
per-file locks use SemaphoreSlim so waiters can await; Save
serialises to a MemoryStream sync (XmlSerializer has no async
form), then File.WriteAllBytesAsync + sync File.Move.
- RepositoryCache.GetOrLoadAsync takes a Func<Task<List<T>>>.
- All 7 repository interfaces + implementations async.
- All service interfaces + implementations async (except vendor
IFxService and stateless IFxConverter / SettingsService).
- Every Blazor OnInitializedAsync switches to await base.
- Test suite fully async, 42 tests pass.
AccountRepository.EnsureAccountsFile keeps two .GetAwaiter().GetResult()
bridges because it runs from the constructor.
Null-render guard follow-up (folded in):
Blazor now renders the component once with fields at their initial
values while OnInitializedAsync awaits — so fields declared `= null!`
are actually null on that first render and things like
`accounts.GroupBy(...)` throw ArgumentNullException. Fixed across
Transactions, BalanceReport, BalanceSheetReport, ProfitLossReport,
DetailReport, Assets, Spendings, SpendingsOverTime, TransactionDialog,
TransactionSplitDialog, and BookingRuleDialog:
- Collection fields initialise to [] so first-render loops are empty.
- Single-object data fields become nullable; the razor wraps
consumption in `@if (field is null) { <p>Lädt…</p> return; }`.
- <PlotlyChart> guarded behind a null check on config/layout/data so
Plotly.Blazor's @bind doesn't see nulls.
- Header/footer strings initialise to "" instead of null!.
Architectural hygiene on a single-user local Blazor Server app: the
observed win is one File.ReadAllBytesAsync and one
File.WriteAllBytesAsync per Load/Save, and after PR D each file is
loaded at most once per SignalR circuit.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Replace the hand-rolled model.Copy(target) pattern in every SaveXxx
with FindIndex -> in-place replace (or Add on new). Move the two
hidden defaults out of the deleted Copy bodies: Currency = "CHF"
default now lives in AccountRepository.SaveAccount; BookingDate =
ValueDate default now lives in TransactionRepository.SaveTransaction.
Delete Copy from Account, BookingRule, BookingText, SubClass, and
SplitPredefinition. Keep Transaction.Clone() (renamed from Copy, and
now includes RelatedParty) for the defensive copy in GetTransaction
and ViewService.WithDisplaySign.
Fixes a pre-existing bug where Transaction.Copy silently dropped
RelatedParty on every update save.
42 tests total, all passing.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
BaseRepository takes an ILogger via its constructor and logs the
failing file path and exception in Save's catch block before
rethrowing. Every concrete repository takes ILogger<ThisRepo> and
passes it up so failures are categorised by repository type.
Pulls Microsoft.Extensions.Logging.Abstractions into the Db project.
TransactionRepositoryTestShould and AccountRepositoryTestShould pass
NullLogger<T>.Instance.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Add a Scoped RepositoryCache that memoizes deserialized XML lists,
keyed by absolute file path. Each XML file is loaded at most once per
SignalR circuit; Save invalidates the corresponding entry so
subsequent reads reload from disk.
BaseRepository gains a LoadList<T>(fileName) helper; each of the seven
repositories now defines Get*List() as a one-liner over that helper
and takes RepositoryCache via the constructor.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Consolidate decimal.ToFormattedString into Schaad.Accounting.Formatting
- Rename EnsureFileExisits -> EnsureFileExists and
SplitPredefiniton* -> SplitPredefinition* (class, interface, files, DI reg)
- AccountRepository: single-save year-rollover start-balance seed
- Drop legacy .NET Standard packages redundant on net9.0 and the stale
Interfaces\Extensions\ folder entry
- Set culture via RequestLocalizationOptions middleware
- Plan: item 9 (unit of work) split off into a dedicated PR D
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-07-02 20:46:55 +02:00
Renamed from Schaad.Accounting.Db/Repositories/SplitPredefinitonRepository.cs (Browse further)