Handle cancelled history reads gracefully

Handles cancellation separately in analytics and scan history reads, and centralizes scan history DTO mapping to reduce duplicated code.
main
SYED MUSTUFA AHMED NAQVI 2026-05-23 11:41:22 +05:00
parent c60bfec891
commit bd4fd0d4b5
5 changed files with 98 additions and 41 deletions

View File

@ -27,6 +27,7 @@ builder.Services
.AddOptions<DesktopHostOptions>()
.Bind(builder.Configuration.GetSection(DesktopHostOptions.SectionName));
builder.Services.AddSingleton<DesktopShutdownService>();
builder.Services.AddHostedService<LaunchBrowserHostedService>();
builder.Services.AddControllersWithViews();
@ -76,7 +77,10 @@ if (!app.Environment.IsDevelopment())
app.UseHsts();
}
if (KestrelConfiguration.HasHttpsEndpoint(app.Configuration))
{
app.UseHttpsRedirection();
}
app.UseRouting();
app.UseAuthorization();
app.UseStaticFiles(new StaticFileOptions

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<DeleteExistingFiles>false</DeleteExistingFiles>
<ExcludeApp_Data>false</ExcludeApp_Data>
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>\\FileServer\Edata\Deployments-by-DotNet-Team\Deployment-By-Mustafa\AVSSHIPPINGMARK</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
<SiteUrlToLaunchAfterPublish />
<TargetFramework>net10.0</TargetFramework>
<RuntimeIdentifier>win-x86</RuntimeIdentifier>
<ProjectGuid>fcd78a4e-cf57-4247-9698-bb63ba85b062</ProjectGuid>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>

View File

@ -53,6 +53,11 @@ public sealed class MovementAnalyticsService(
.Where(x => x.ProcessedAtUtc >= rangeStartUtc && x.ProcessedAtUtc <= rangeEndUtc)
.ToListAsync(cancellationToken);
}
catch (OperationCanceledException exception) when (cancellationToken.IsCancellationRequested)
{
_logger.LogDebug(exception, "Movement analytics read was cancelled.");
throw;
}
catch (Exception exception)
{
_logger.LogError(exception, "Failed to load movement analytics.");

View File

@ -70,16 +70,11 @@ public sealed class SqliteScanHistoryStore(
.Take(5)
.ToArrayAsync(cancellationToken);
return entries
.Select(x => new ScannedRecordDto
return MapSuccessful(entries);
}
catch (Exception exception) when (TryHandleReadCancellation(exception, cancellationToken, "Successful scan history (last five)"))
{
ModelNumber = x.ModelNumber,
UniqueNumber = x.UniqueNumber,
ExistsInSystem = x.ExistsInSystem,
ShipmentMarked = x.ShipmentMarked,
ProcessedAtUtc = DateTime.SpecifyKind(x.ProcessedAtUtc, DateTimeKind.Utc)
})
.ToArray();
return Array.Empty<ScannedRecordDto>();
}
catch (Exception exception)
{
@ -98,16 +93,11 @@ public sealed class SqliteScanHistoryStore(
.Take(5)
.ToArrayAsync(cancellationToken);
return entries
.Select(x => new RejectedScanRecordDto
return MapRejected(entries);
}
catch (Exception exception) when (TryHandleReadCancellation(exception, cancellationToken, "Rejected scan history (last five)"))
{
RawInputValue = x.RawInputValue,
ModelNumber = x.ModelNumber,
UniqueNumber = x.UniqueNumber,
RejectionReason = x.RejectionReason,
ProcessedAtUtc = DateTime.SpecifyKind(x.ProcessedAtUtc, DateTimeKind.Utc)
})
.ToArray();
return Array.Empty<RejectedScanRecordDto>();
}
catch (Exception exception)
{
@ -126,16 +116,11 @@ public sealed class SqliteScanHistoryStore(
.OrderByDescending(x => x.ProcessedAtUtc)
.ToArrayAsync(cancellationToken);
return entries
.Select(x => new ScannedRecordDto
return MapSuccessful(entries);
}
catch (Exception exception) when (TryHandleReadCancellation(exception, cancellationToken, "Successful scan history (all)"))
{
ModelNumber = x.ModelNumber,
UniqueNumber = x.UniqueNumber,
ExistsInSystem = x.ExistsInSystem,
ShipmentMarked = x.ShipmentMarked,
ProcessedAtUtc = DateTime.SpecifyKind(x.ProcessedAtUtc, DateTimeKind.Utc)
})
.ToArray();
return Array.Empty<ScannedRecordDto>();
}
catch (Exception exception)
{
@ -153,7 +138,44 @@ public sealed class SqliteScanHistoryStore(
.OrderByDescending(x => x.ProcessedAtUtc)
.ToArrayAsync(cancellationToken);
return entries
return MapRejected(entries);
}
catch (Exception exception) when (TryHandleReadCancellation(exception, cancellationToken, "Rejected scan history (all)"))
{
return Array.Empty<RejectedScanRecordDto>();
}
catch (Exception exception)
{
_logger.LogError(exception, "Failed to read full rejected scan history.");
return Array.Empty<RejectedScanRecordDto>();
}
}
private bool TryHandleReadCancellation(Exception exception, CancellationToken cancellationToken, string operation)
{
if (exception is not OperationCanceledException || !cancellationToken.IsCancellationRequested)
{
return false;
}
_logger.LogDebug(exception, "{Operation} read was cancelled.", operation);
return true;
}
private static ScannedRecordDto[] MapSuccessful(IEnumerable<ScanHistoryEntry> entries) =>
entries
.Select(x => new ScannedRecordDto
{
ModelNumber = x.ModelNumber,
UniqueNumber = x.UniqueNumber,
ExistsInSystem = x.ExistsInSystem,
ShipmentMarked = x.ShipmentMarked,
ProcessedAtUtc = DateTime.SpecifyKind(x.ProcessedAtUtc, DateTimeKind.Utc)
})
.ToArray();
private static RejectedScanRecordDto[] MapRejected(IEnumerable<RejectedScanEntry> entries) =>
entries
.Select(x => new RejectedScanRecordDto
{
RawInputValue = x.RawInputValue,
@ -163,13 +185,6 @@ public sealed class SqliteScanHistoryStore(
ProcessedAtUtc = DateTime.SpecifyKind(x.ProcessedAtUtc, DateTimeKind.Utc)
})
.ToArray();
}
catch (Exception exception)
{
_logger.LogError(exception, "Failed to read full rejected scan history.");
return Array.Empty<RejectedScanRecordDto>();
}
}
private async Task TrimSuccessfulAsync(CancellationToken cancellationToken)
{

13
dotnet-tools.json Normal file
View File

@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-ef": {
"version": "10.0.8",
"commands": [
"dotnet-ef"
],
"rollForward": false
}
}
}