Files
Libation/Source/LibationWinForms/Form1.BackupCounts.cs
T
Cursor Agentandrmcrackan 813b5eeb6e Guard BackgroundWorker e.Result behind e.Error/e.Cancelled to prevent startup crashes
Reading RunWorkerCompletedEventArgs.Result rethrows any exception from the
count DoWork. In the Avalonia async void handler this became an unhandled
exception that killed the app during library load (issue #1931). Check
e.Cancelled/e.Error first, log once, and degrade to empty stats in both the
Avalonia and WinForms backup-count completion handlers.

Co-authored-by: rmcrackan <rmcrackan@gmail.com>
2026-08-12 18:05:36 +00:00

111 lines
4.0 KiB
C#

using ApplicationServices;
using DataLayer;
using Dinah.Core.Threading;
using System.Collections.Generic;
namespace LibationWinForms;
public partial class Form1
{
private readonly System.ComponentModel.BackgroundWorker updateCountsBw = new();
protected void Configure_BackupCounts()
{
// init formattable
beginBookBackupsToolStripMenuItem.Format(0);
beginPdfBackupsToolStripMenuItem.Format(0);
LibraryCommands.LibrarySizeChanged += setBackupCounts;
//Pass null to the runner to get the whole library.
LibraryCommands.BookUserDefinedItemCommitted += (_, _)
=> setBackupCounts(null, null);
updateCountsBw.DoWork += UpdateCountsBw_DoWork;
// Register the error logger first so a failed count is logged exactly once, before the
// display handlers below run and (safely) treat the stats as unavailable.
updateCountsBw.RunWorkerCompleted += logBackupCountsError;
updateCountsBw.RunWorkerCompleted += exportMenuEnable;
updateCountsBw.RunWorkerCompleted += updateBottomStats;
updateCountsBw.RunWorkerCompleted += update_BeginBookBackups_menuItem;
updateCountsBw.RunWorkerCompleted += udpate_BeginPdfOnlyBackups_menuItem;
}
/// <summary>
/// Safely extract the completed <see cref="LibraryCommands.LibraryStats"/>. Returns null when the
/// background count was cancelled or faulted. Reading <see cref="System.ComponentModel.RunWorkerCompletedEventArgs.Result"/>
/// directly rethrows any <see cref="System.ComponentModel.RunWorkerCompletedEventArgs.Error"/>, which would otherwise
/// crash the app; callers must degrade gracefully on null.
/// </summary>
private static LibraryCommands.LibraryStats? getLibraryStats(System.ComponentModel.RunWorkerCompletedEventArgs e)
=> e.Cancelled || e.Error is not null ? null : e.Result as LibraryCommands.LibraryStats;
private static void logBackupCountsError(object? _, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
if (e.Error is not null)
Serilog.Log.Logger.Error(e.Error, "Failed to update backup counts");
}
private bool runBackupCountsAgain;
private void setBackupCounts(object? _, List<LibraryBook>? libraryBooks)
{
runBackupCountsAgain = true;
if (!updateCountsBw.IsBusy)
updateCountsBw.RunWorkerAsync(libraryBooks);
}
private void UpdateCountsBw_DoWork(object? sender, System.ComponentModel.DoWorkEventArgs e)
{
while (runBackupCountsAgain)
{
runBackupCountsAgain = false;
e.Result = LibraryCommands.GetCounts(e.Argument as IEnumerable<LibraryBook>);
}
}
private void exportMenuEnable(object? _, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
var libraryStats = getLibraryStats(e);
Invoke(() => exportLibraryToolStripMenuItem.Enabled = libraryStats?.HasBookResults is true);
}
private void updateBottomStats(object? _, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
var libraryStats = getLibraryStats(e);
statusStrip1.UIThreadAsync(() => backupsCountsLbl.Text = libraryStats?.StatusString ?? "ERROR GETTING STATUS");
}
// update 'begin book and pdf backups' menu item
private void update_BeginBookBackups_menuItem(object? _, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
var libraryStats = getLibraryStats(e);
var menuItemText
= libraryStats?.HasPendingBooks is true
? $"{libraryStats.PendingBooks} remaining"
: "All books have been liberated";
menuStrip1.UIThreadAsync(() =>
{
beginBookBackupsToolStripMenuItem.Format(menuItemText);
beginBookBackupsToolStripMenuItem.Enabled = libraryStats?.HasPendingBooks is true;
});
}
// update 'begin pdf only backups' menu item
private void udpate_BeginPdfOnlyBackups_menuItem(object? _, System.ComponentModel.RunWorkerCompletedEventArgs e)
{
var libraryStats = getLibraryStats(e);
var menuItemText
= libraryStats?.pdfsNotDownloaded > 0
? $"{libraryStats.pdfsNotDownloaded} remaining"
: "All PDFs have been downloaded";
menuStrip1.UIThreadAsync(() =>
{
beginPdfBackupsToolStripMenuItem.Format(menuItemText);
beginPdfBackupsToolStripMenuItem.Enabled = libraryStats?.pdfsNotDownloaded > 0;
});
}
}