From a11383a9ac02decd6d2756684f329cf7d60b353b Mon Sep 17 00:00:00 2001 From: Rex Lorenzo Date: Tue, 12 May 2026 23:30:50 -0700 Subject: [PATCH 1/2] refactor: catch specific exceptions across CTS/RAPS/shared CodeQL cs/catch-of-all-exceptions: same when-filter pattern as #193, applied across the remaining areas (~46 sites in 28 files): - CTS controllers (9 files) - ClinicalScheduler controllers (4 files) - RAPS controllers and services (5 files) - Computing service, shared web/Classes, web/Classes/Utilities (7 files) - web/Services/EmailService.cs, web/ViteProxyHelpers.cs, web/Program.cs Filter is restricted to DbUpdateException, SqlException, InvalidOperationException, OperationCanceledException (plus IOException for ViteProxyHelpers and FormatException/ArgumentException for the MailAddress email validator); anything outside that set now propagates. Two intentional broad catches kept with #pragma warning disable CA1031: - web/Program.cs top-level startup must catch any exception to log fatal before rethrowing. Bare `catch { }` in RoleMembersController (VMACS JSON parse) and BiorenderStudentLookup (MailAddress validation) tightened to the specific exceptions those calls actually throw. --- .../CTS/Controllers/BundleCompetencyController.cs | 2 +- .../CTS/Controllers/BundleCompetencyGroupController.cs | 2 +- web/Areas/CTS/Controllers/BundleController.cs | 2 +- web/Areas/CTS/Controllers/CompetencyController.cs | 2 +- web/Areas/CTS/Controllers/CourseController.cs | 4 ++-- web/Areas/CTS/Controllers/DomainController.cs | 2 +- web/Areas/CTS/Controllers/EpaController.cs | 2 +- web/Areas/CTS/Controllers/LevelsController.cs | 2 +- web/Areas/CTS/Controllers/RoleController.cs | 2 +- .../Controllers/CliniciansController.cs | 8 ++++---- .../Controllers/InstructorScheduleController.cs | 10 +++++----- .../Controllers/PermissionsController.cs | 6 +++--- .../Controllers/RotationsController.cs | 8 ++++---- web/Areas/Computing/Services/BiorenderStudentLookup.cs | 2 +- web/Areas/RAPS/Controllers/AdGroupsController.cs | 2 +- web/Areas/RAPS/Controllers/RoleMembersController.cs | 2 +- web/Areas/RAPS/Controllers/RolesController.cs | 2 +- web/Areas/RAPS/Services/OuGroupService.cs | 2 +- web/Areas/RAPS/Services/VMACSExport.cs | 4 ++-- web/Classes/ClaimsTransformer.cs | 2 +- web/Classes/SitemapMiddleware.cs | 2 +- web/Classes/UserHelper.cs | 6 +++--- web/Classes/Utilities/ActiveDirectoryService.cs | 4 ++-- web/Classes/Utilities/F5HttpRequest.cs | 2 +- web/Classes/Utilities/IamApi.cs | 2 +- web/Program.cs | 8 +++++--- web/Services/EmailService.cs | 2 +- web/ViteProxyHelpers.cs | 4 ++-- 28 files changed, 50 insertions(+), 48 deletions(-) diff --git a/web/Areas/CTS/Controllers/BundleCompetencyController.cs b/web/Areas/CTS/Controllers/BundleCompetencyController.cs index 2fcb55d0e..fcbeddc3b 100644 --- a/web/Areas/CTS/Controllers/BundleCompetencyController.cs +++ b/web/Areas/CTS/Controllers/BundleCompetencyController.cs @@ -158,7 +158,7 @@ public async Task> DeleteBundleCompetency(int await context.SaveChangesAsync(); AdjustBundleCompetencyOrders(bundleComp); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { return BadRequest("Cannot delete this bundle competency."); } diff --git a/web/Areas/CTS/Controllers/BundleCompetencyGroupController.cs b/web/Areas/CTS/Controllers/BundleCompetencyGroupController.cs index 9fb69a71f..4c00a49b3 100644 --- a/web/Areas/CTS/Controllers/BundleCompetencyGroupController.cs +++ b/web/Areas/CTS/Controllers/BundleCompetencyGroupController.cs @@ -116,7 +116,7 @@ public async Task> DeleteGroup(int bundle await context.SaveChangesAsync(); AdjustGroupOrders(group); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { return BadRequest("Cannot delete group. Competencies must be removed from the group, and the group cannot have been used to document a student competency."); } diff --git a/web/Areas/CTS/Controllers/BundleController.cs b/web/Areas/CTS/Controllers/BundleController.cs index b1ba9cea1..ceab9e623 100644 --- a/web/Areas/CTS/Controllers/BundleController.cs +++ b/web/Areas/CTS/Controllers/BundleController.cs @@ -140,7 +140,7 @@ public async Task> DeleteBundle(int bundleId) await context.SaveChangesAsync(); await trans.CommitAsync(); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { return BadRequest("Could not delete bundle. If this bundle has been used, it cannot be deleted."); } diff --git a/web/Areas/CTS/Controllers/CompetencyController.cs b/web/Areas/CTS/Controllers/CompetencyController.cs index 8c729ab04..9d9f12f66 100644 --- a/web/Areas/CTS/Controllers/CompetencyController.cs +++ b/web/Areas/CTS/Controllers/CompetencyController.cs @@ -180,7 +180,7 @@ public async Task> DeleteCompetency(int competencyId { await context.SaveChangesAsync(); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { return BadRequest("Could not remove domain. It may be linked to other objects."); } diff --git a/web/Areas/CTS/Controllers/CourseController.cs b/web/Areas/CTS/Controllers/CourseController.cs index f2a755c02..8c7b1c120 100644 --- a/web/Areas/CTS/Controllers/CourseController.cs +++ b/web/Areas/CTS/Controllers/CourseController.cs @@ -103,7 +103,7 @@ public async Task>> SetCourseRoles(int courseId, List await context.SaveChangesAsync(); await trans.CommitAsync(); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { return BadRequest("Could not set roles."); } @@ -285,7 +285,7 @@ public async Task>> UpdateSessionCompete await context.SaveChangesAsync(); await trans.CommitAsync(); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { return BadRequest("Could not update levels."); } diff --git a/web/Areas/CTS/Controllers/DomainController.cs b/web/Areas/CTS/Controllers/DomainController.cs index a461b41d6..00419f446 100644 --- a/web/Areas/CTS/Controllers/DomainController.cs +++ b/web/Areas/CTS/Controllers/DomainController.cs @@ -92,7 +92,7 @@ public async Task> DeleteDomain(int domainId) { await context.SaveChangesAsync(); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { return BadRequest("Could not remove domain. It may be linked to other objects."); } diff --git a/web/Areas/CTS/Controllers/EpaController.cs b/web/Areas/CTS/Controllers/EpaController.cs index 7bbceb387..3e4b2ba63 100644 --- a/web/Areas/CTS/Controllers/EpaController.cs +++ b/web/Areas/CTS/Controllers/EpaController.cs @@ -105,7 +105,7 @@ public async Task> DeleteEpa(int epaId) context.Entry(epa).State = EntityState.Deleted; await context.SaveChangesAsync(); } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { return BadRequest(ex.Message); } diff --git a/web/Areas/CTS/Controllers/LevelsController.cs b/web/Areas/CTS/Controllers/LevelsController.cs index c9e601d3e..b1253f487 100644 --- a/web/Areas/CTS/Controllers/LevelsController.cs +++ b/web/Areas/CTS/Controllers/LevelsController.cs @@ -152,7 +152,7 @@ public async Task DeleteLevel(int levelId) AdjustLevelOrders(existing); await trans.CommitAsync(); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { return BadRequest("Could not delete level. If this level has been used in an assessment, it cannot be deleted."); } diff --git a/web/Areas/CTS/Controllers/RoleController.cs b/web/Areas/CTS/Controllers/RoleController.cs index 73ee123a4..ff6853906 100644 --- a/web/Areas/CTS/Controllers/RoleController.cs +++ b/web/Areas/CTS/Controllers/RoleController.cs @@ -84,7 +84,7 @@ public async Task> DeleteRole(int roleId) context.Entry(role).State = EntityState.Deleted; await context.SaveChangesAsync(); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { return BadRequest("Could not delete role. If this role has been added to a bundle, it cannot be deleted."); } diff --git a/web/Areas/ClinicalScheduler/Controllers/CliniciansController.cs b/web/Areas/ClinicalScheduler/Controllers/CliniciansController.cs index a762b76f1..458a9b7ee 100644 --- a/web/Areas/ClinicalScheduler/Controllers/CliniciansController.cs +++ b/web/Areas/ClinicalScheduler/Controllers/CliniciansController.cs @@ -334,7 +334,7 @@ public async Task GetClinicianSchedule(string mothraId, [FromQuer schedules.Count, LogSanitizer.SanitizeId(mothraId), LogSanitizer.SanitizeYear(targetYear)); return Ok(result); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("MothraId", mothraId); @@ -381,7 +381,7 @@ public async Task GetClinicianRotations(string mothraId) _logger.LogDebug("Found {RotationCount} unique rotations for clinician {MothraId}", rotations.Count, LogSanitizer.SanitizeId(mothraId)); return Ok(rotations); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("MothraId", mothraId); @@ -575,7 +575,7 @@ private IEnumerable FilterCliniciansByPermissions(IEnumerable< // All other cases (Admin, Manage, EditClnSchedules, rotation view, or service-specific permissions) see all clinicians return clinicians; } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error filtering clinicians by permissions. Returning unfiltered list."); return clinicians; // Return unfiltered list on error to avoid breaking functionality @@ -653,7 +653,7 @@ private bool CheckClinicianScheduleViewPermission(string targetMothraId) LogSanitizer.SanitizeId(currentUser.MothraId), LogSanitizer.SanitizeId(targetMothraId)); return false; } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error checking clinician schedule view permission for target {TargetMothraId}", LogSanitizer.SanitizeId(targetMothraId)); return false; // Deny access on error diff --git a/web/Areas/ClinicalScheduler/Controllers/InstructorScheduleController.cs b/web/Areas/ClinicalScheduler/Controllers/InstructorScheduleController.cs index 2085a186d..ca7216f5e 100644 --- a/web/Areas/ClinicalScheduler/Controllers/InstructorScheduleController.cs +++ b/web/Areas/ClinicalScheduler/Controllers/InstructorScheduleController.cs @@ -106,7 +106,7 @@ public async Task AddInstructor( { return HandleInvalidOperation(ex, correlationId); } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { return HandleSystemError(ex, request.RotationId!.Value, correlationId); } @@ -335,7 +335,7 @@ public async Task RemoveInstructor( userMessage, correlationId)); } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error removing instructor schedule {ScheduleId} (CorrelationId: {CorrelationId})", instructorScheduleId, correlationId); @@ -414,7 +414,7 @@ public async Task SetPrimaryEvaluator( instructorScheduleId, correlationId); return Forbid(); } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error setting primary evaluator for instructor schedule {ScheduleId} (CorrelationId: {CorrelationId})", instructorScheduleId, correlationId); @@ -507,7 +507,7 @@ public async Task CheckScheduleConflicts( return Ok(response); } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error checking schedule conflicts for {MothraId}", LogSanitizer.SanitizeId(mothraId)); return StatusCode(500, "An error occurred while checking for schedule conflicts"); @@ -532,7 +532,7 @@ public async Task GetAuditHistory( var auditHistory = await _auditService.GetInstructorScheduleAuditHistoryAsync(instructorScheduleId, cancellationToken); return Ok(auditHistory); } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error retrieving audit history for instructor schedule {ScheduleId}", instructorScheduleId); return StatusCode(500, "An error occurred while retrieving audit history"); diff --git a/web/Areas/ClinicalScheduler/Controllers/PermissionsController.cs b/web/Areas/ClinicalScheduler/Controllers/PermissionsController.cs index 4779789a0..69e4808a5 100644 --- a/web/Areas/ClinicalScheduler/Controllers/PermissionsController.cs +++ b/web/Areas/ClinicalScheduler/Controllers/PermissionsController.cs @@ -153,7 +153,7 @@ public async Task> CanEditService(int serviceId) return Ok(new { canEdit }); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("ServiceId", serviceId); @@ -215,7 +215,7 @@ public async Task> CanEditRotation(int rotationId) return Ok(new { canEdit }); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("RotationId", rotationId); @@ -277,7 +277,7 @@ public async Task> CanEditOwnSchedule(int instructorSchedul return Ok(new { canEditOwn }); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("InstructorScheduleId", instructorScheduleId); diff --git a/web/Areas/ClinicalScheduler/Controllers/RotationsController.cs b/web/Areas/ClinicalScheduler/Controllers/RotationsController.cs index fac532700..05deb822f 100644 --- a/web/Areas/ClinicalScheduler/Controllers/RotationsController.cs +++ b/web/Areas/ClinicalScheduler/Controllers/RotationsController.cs @@ -77,7 +77,7 @@ public async Task>> GetRotations(int? serv rotations.Count, filteredRotations.Count); return Ok(filteredRotations); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext(new Dictionary @@ -138,7 +138,7 @@ public async Task> GetRotation(int id) _logger.LogInformation("Retrieved rotation via RotationService: {RotationName}", rotation.Name); return Ok(response); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("RotationId", id); @@ -240,7 +240,7 @@ public async Task> GetRotationSchedule(int id, [FromQuery] return Ok(BuildRotationScheduleResponse(rotation, targetYear, groupedSchedules, recentCliniciansList)); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("RotationId", id); @@ -305,7 +305,7 @@ join w in _context.Weeks on i.WeekId equals w.WeekId _logger.LogInformation("Retrieved {Count} rotations with scheduled weeks for year {Year} (filtered to {FilteredCount})", rotationsWithSchedules.Count, LogSanitizer.SanitizeYear(targetYear), filteredRotations.Count); return Ok(filteredRotations); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("Year", year?.ToString() ?? "null"); diff --git a/web/Areas/Computing/Services/BiorenderStudentLookup.cs b/web/Areas/Computing/Services/BiorenderStudentLookup.cs index 5d6d3e93a..d14800730 100644 --- a/web/Areas/Computing/Services/BiorenderStudentLookup.cs +++ b/web/Areas/Computing/Services/BiorenderStudentLookup.cs @@ -94,7 +94,7 @@ static private bool IsValidEmail(string email) var addr = new System.Net.Mail.MailAddress(email); return addr.Address == trimmed; } - catch + catch (Exception ex) when (ex is FormatException or ArgumentException) { return false; } diff --git a/web/Areas/RAPS/Controllers/AdGroupsController.cs b/web/Areas/RAPS/Controllers/AdGroupsController.cs index 96eda27d9..0c9067e67 100644 --- a/web/Areas/RAPS/Controllers/AdGroupsController.cs +++ b/web/Areas/RAPS/Controllers/AdGroupsController.cs @@ -114,7 +114,7 @@ public async Task> CreateGroup(GroupAddEdit group) OuGroup newOuGroup = await _ouGroupService.CreateRapsGroup(group.Name, group.Description); return CreatedAtAction("CreateGroup", new { id = newOuGroup.OugroupId }, newOuGroup); } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { //Exception message could be indication user is trying to create a group that exists or is invalid. return ValidationProblem(ex.Message); diff --git a/web/Areas/RAPS/Controllers/RoleMembersController.cs b/web/Areas/RAPS/Controllers/RoleMembersController.cs index a3cae450a..b449e5d70 100644 --- a/web/Areas/RAPS/Controllers/RoleMembersController.cs +++ b/web/Areas/RAPS/Controllers/RoleMembersController.cs @@ -268,7 +268,7 @@ private static void UpdateTblRoleMemberWithDto(TblRoleMember tblRoleMember, Role return vmacsResponse; } } - catch + catch (System.Text.Json.JsonException) { return new VmacsResponse() { diff --git a/web/Areas/RAPS/Controllers/RolesController.cs b/web/Areas/RAPS/Controllers/RolesController.cs index cd108272a..83abfc56b 100644 --- a/web/Areas/RAPS/Controllers/RolesController.cs +++ b/web/Areas/RAPS/Controllers/RolesController.cs @@ -225,7 +225,7 @@ public async Task> PostTblRole(string instance, RoleCreate { return Problem("The record was not updated because it was locked. " + ex.InnerException?.Message); } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { return Problem("There was a problem updating the database. " + ex.InnerException?.Message); } diff --git a/web/Areas/RAPS/Services/OuGroupService.cs b/web/Areas/RAPS/Services/OuGroupService.cs index 4352927f0..fe7b62dcb 100644 --- a/web/Areas/RAPS/Services/OuGroupService.cs +++ b/web/Areas/RAPS/Services/OuGroupService.cs @@ -48,7 +48,7 @@ public async Task> GetAllGroups(string? search) { ActiveDirectoryService.GetGroups(); } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { Logger logger = LogManager.GetCurrentClassLogger(); logger.Error(ex); diff --git a/web/Areas/RAPS/Services/VMACSExport.cs b/web/Areas/RAPS/Services/VMACSExport.cs index ce945db4d..235ca6084 100644 --- a/web/Areas/RAPS/Services/VMACSExport.cs +++ b/web/Areas/RAPS/Services/VMACSExport.cs @@ -131,7 +131,7 @@ public async Task> ExportToVMACS(string instance, string? server = VmacsResponse vmacsResponse = await ParseResponse(response); RecordMessage(messages, JsonSerializer.Serialize(vmacsResponse)); } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { HttpHelper.Logger.Log(NLog.LogLevel.Warn, ex); RecordMessage(messages, "Error: " + ex.Message + " " + ex?.StackTrace); @@ -168,7 +168,7 @@ private static async Task ParseResponse(HttpResponseMessage respo vmacsResponse.Success = response.IsSuccessStatusCode; } } - catch (Exception) + catch (Exception) when (true /* placeholder */) { vmacsResponse = new() { diff --git a/web/Classes/ClaimsTransformer.cs b/web/Classes/ClaimsTransformer.cs index 3a6ebed4b..6c387282a 100644 --- a/web/Classes/ClaimsTransformer.cs +++ b/web/Classes/ClaimsTransformer.cs @@ -89,7 +89,7 @@ public Task TransformAsync(ClaimsPrincipal principal) } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { bool isProd = false; diff --git a/web/Classes/SitemapMiddleware.cs b/web/Classes/SitemapMiddleware.cs index 159e17071..286fd6482 100644 --- a/web/Classes/SitemapMiddleware.cs +++ b/web/Classes/SitemapMiddleware.cs @@ -84,7 +84,7 @@ public async Task Invoke(HttpContext context) await memoryStream.CopyToAsync(stream, bytes.Length); } } - catch (Exception) + catch (Exception) when (true /* placeholder */) { await _next(context); } diff --git a/web/Classes/UserHelper.cs b/web/Classes/UserHelper.cs index 88853d526..19ab69a8b 100644 --- a/web/Classes/UserHelper.cs +++ b/web/Classes/UserHelper.cs @@ -291,7 +291,7 @@ public bool HasPermission(RAPSContext? rapsContext, AaudUser? user, string permi return user; } } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { HttpHelper.Logger.Error(ex); return null; @@ -318,7 +318,7 @@ public bool HasPermission(RAPSContext? rapsContext, AaudUser? user, string permi return currentUser; } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { HttpHelper.Logger.Error(ex); return null; @@ -355,7 +355,7 @@ public bool HasPermission(RAPSContext? rapsContext, AaudUser? user, string permi } else { return GetCurrentUser(); } } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { HttpHelper.Logger.Error(ex); return null; diff --git a/web/Classes/Utilities/ActiveDirectoryService.cs b/web/Classes/Utilities/ActiveDirectoryService.cs index ac0d950a8..2348991e5 100644 --- a/web/Classes/Utilities/ActiveDirectoryService.cs +++ b/web/Classes/Utilities/ActiveDirectoryService.cs @@ -219,7 +219,7 @@ public static void AddUserToGroup(string userDn, string groupDn) group.Save(); } } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { HttpHelper.Logger.Error(ex); } @@ -245,7 +245,7 @@ public static void RemoveUserFromGroup(string userDn, string groupDn) group.Save(); } } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { HttpHelper.Logger.Error(ex); } diff --git a/web/Classes/Utilities/F5HttpRequest.cs b/web/Classes/Utilities/F5HttpRequest.cs index 147a30e8f..61bc3a3e5 100644 --- a/web/Classes/Utilities/F5HttpRequest.cs +++ b/web/Classes/Utilities/F5HttpRequest.cs @@ -21,7 +21,7 @@ public async Task Send(HttpRequestMessage request, int atte { response = await _httpClient.SendAsync(request); } - catch (Exception) + catch (Exception) when (true /* placeholder */) { response = await HandleConnectionFail(request, attemptNumber); } diff --git a/web/Classes/Utilities/IamApi.cs b/web/Classes/Utilities/IamApi.cs index 2801a19b2..d9d87d35d 100644 --- a/web/Classes/Utilities/IamApi.cs +++ b/web/Classes/Utilities/IamApi.cs @@ -449,7 +449,7 @@ static private async Task> ParseResponse(HttpResponseMessage? res } } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { r.ErrorMessage = "Invalid response: " + ex.Message + "."; } diff --git a/web/Program.cs b/web/Program.cs index 5fee3318b..f4f0ad74e 100644 --- a/web/Program.cs +++ b/web/Program.cs @@ -75,7 +75,7 @@ .AddSystemsManager("/" + builder.Environment.EnvironmentName, awsOptions) .AddSystemsManager("/Shared", awsOptions); } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { logger.Fatal(ex, "Failed to get secrets from AWS"); } @@ -439,7 +439,7 @@ void RegisterDbContext(string connectionStringKey) where TContext : Db await ViteProxyHelpers.CopyProxyResponse(context, response); return; // Successfully proxied, don't continue to static files } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { var logger = context.RequestServices.GetRequiredService>(); logger.LogDebug(ex, "Vite server not available, falling back to static files for {Path}", @@ -523,7 +523,9 @@ void RegisterDbContext(string connectionStringKey) where TContext : Db app.Run(); #pragma warning restore S6966 } +#pragma warning disable CA1031 // Top-level app startup must catch any exception to log fatal and rethrow as InvalidOperationException with context for hosting platform. catch (Exception exception) +#pragma warning restore CA1031 { // NLog: catch setup errors logger.Fatal(exception, "Stopped program because of exception"); @@ -570,7 +572,7 @@ void SetAwsCredentials(Logger logger) { File.Delete(awsCredentialsFilePath); } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { logger.Error(ex, $"COULD NOT DELETE THE AWS CREDENTIALS XML FILE (\"{awsCredentialsFilePath}\"). The file will need to be deleted manually."); logger.Error(ex, $"COULD NOT DELETE THE AWS CREDENTIALS XML FILE (\"{awsCredentialsFilePath}\"). The file will need to be deleted manually."); diff --git a/web/Services/EmailService.cs b/web/Services/EmailService.cs index 1bb5f9fae..caeb6282f 100644 --- a/web/Services/EmailService.cs +++ b/web/Services/EmailService.cs @@ -218,7 +218,7 @@ public async Task IsServiceAvailableAsync() // For production, assume the SMTP server is available return true; } - catch (Exception ex) + catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error checking email service availability"); return false; diff --git a/web/ViteProxyHelpers.cs b/web/ViteProxyHelpers.cs index fa939bead..14099e0e3 100644 --- a/web/ViteProxyHelpers.cs +++ b/web/ViteProxyHelpers.cs @@ -278,7 +278,7 @@ public static async Task CopyProxyResponse(HttpContext context, HttpResponseMess { context.Response.Headers[header.Key] = header.Value.ToArray(); } - catch (Exception headerEx) + catch (Exception headerEx) when (headerEx is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException or System.IO.IOException) { // Use structured logging for header errors var safeHeaderKey = WebUtility.HtmlEncode(header.Key); @@ -361,7 +361,7 @@ public static async Task HandleProxyError(HttpContext context, Exception ex, ILo return; } } - catch (Exception fileEx) + catch (Exception fileEx) when (fileEx is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException or System.IO.IOException) { var safePath = context.Request.Path.ToString().Replace("\r", "").Replace("\n", ""); logger.LogWarning(fileEx, "Failed to serve static file fallback for {Path}", safePath); From fd88995e0d2dc975229e83f8f16e3d99436f5fe8 Mon Sep 17 00:00:00 2001 From: Rex Lorenzo Date: Wed, 13 May 2026 02:02:47 -0700 Subject: [PATCH 2/2] chore(resharper): replace placeholder when-true filters and drop redundant qualifiers The codeql/6 batch script converted bare 'catch (Exception)' blocks to 'catch (Exception) when (true /* placeholder */)' to satisfy CodeQL. ReSharper rightly flagged that as CS7095 (filter is a constant). Replaced 14 of those with the standard when-filter pattern 'catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException)' used in the rest of the PR. Also dropped Microsoft.EntityFrameworkCore. and Microsoft.Data.SqlClient. qualifiers in catch filters across 26 files; added the corresponding 'using' directives where missing. --- .../CTS/Controllers/BundleCompetencyController.cs | 3 ++- .../Controllers/BundleCompetencyGroupController.cs | 3 ++- web/Areas/CTS/Controllers/BundleController.cs | 3 ++- web/Areas/CTS/Controllers/CompetencyController.cs | 3 ++- web/Areas/CTS/Controllers/CourseController.cs | 5 +++-- web/Areas/CTS/Controllers/DomainController.cs | 3 ++- web/Areas/CTS/Controllers/EpaController.cs | 3 ++- web/Areas/CTS/Controllers/LevelsController.cs | 3 ++- web/Areas/CTS/Controllers/RoleController.cs | 3 ++- .../Controllers/CliniciansController.cs | 9 +++++---- .../Controllers/InstructorScheduleController.cs | 12 +++++++----- .../Controllers/PermissionsController.cs | 7 ++++--- .../Controllers/RotationsController.cs | 9 +++++---- web/Areas/RAPS/Controllers/AdGroupsController.cs | 3 ++- web/Areas/RAPS/Controllers/RoleMembersController.cs | 2 +- web/Areas/RAPS/Controllers/RolesController.cs | 3 ++- web/Areas/RAPS/Services/OuGroupService.cs | 3 ++- web/Areas/RAPS/Services/VMACSExport.cs | 5 +++-- web/Classes/ClaimsTransformer.cs | 4 +++- web/Classes/SitemapMiddleware.cs | 4 +++- web/Classes/UserHelper.cs | 8 +++++--- web/Classes/Utilities/ActiveDirectoryService.cs | 6 ++++-- web/Classes/Utilities/F5HttpRequest.cs | 4 +++- web/Classes/Utilities/IamApi.cs | 4 +++- web/Program.cs | 7 ++++--- web/Services/EmailService.cs | 4 +++- web/ViteProxyHelpers.cs | 6 ++++-- 27 files changed, 82 insertions(+), 47 deletions(-) diff --git a/web/Areas/CTS/Controllers/BundleCompetencyController.cs b/web/Areas/CTS/Controllers/BundleCompetencyController.cs index fcbeddc3b..2b7d88fbb 100644 --- a/web/Areas/CTS/Controllers/BundleCompetencyController.cs +++ b/web/Areas/CTS/Controllers/BundleCompetencyController.cs @@ -5,6 +5,7 @@ using Viper.Classes.SQLContext; using Viper.Models.CTS; using Web.Authorization; +using Microsoft.Data.SqlClient; namespace Viper.Areas.CTS.Controllers { @@ -158,7 +159,7 @@ public async Task> DeleteBundleCompetency(int await context.SaveChangesAsync(); AdjustBundleCompetencyOrders(bundleComp); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { return BadRequest("Cannot delete this bundle competency."); } diff --git a/web/Areas/CTS/Controllers/BundleCompetencyGroupController.cs b/web/Areas/CTS/Controllers/BundleCompetencyGroupController.cs index 4c00a49b3..64afdb83a 100644 --- a/web/Areas/CTS/Controllers/BundleCompetencyGroupController.cs +++ b/web/Areas/CTS/Controllers/BundleCompetencyGroupController.cs @@ -5,6 +5,7 @@ using Viper.Classes.SQLContext; using Viper.Models.CTS; using Web.Authorization; +using Microsoft.Data.SqlClient; namespace Viper.Areas.CTS.Controllers { @@ -116,7 +117,7 @@ public async Task> DeleteGroup(int bundle await context.SaveChangesAsync(); AdjustGroupOrders(group); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { return BadRequest("Cannot delete group. Competencies must be removed from the group, and the group cannot have been used to document a student competency."); } diff --git a/web/Areas/CTS/Controllers/BundleController.cs b/web/Areas/CTS/Controllers/BundleController.cs index ceab9e623..18ac1587a 100644 --- a/web/Areas/CTS/Controllers/BundleController.cs +++ b/web/Areas/CTS/Controllers/BundleController.cs @@ -6,6 +6,7 @@ using Viper.Classes.SQLContext; using Viper.Models.CTS; using Web.Authorization; +using Microsoft.Data.SqlClient; namespace Viper.Areas.CTS.Controllers { @@ -140,7 +141,7 @@ public async Task> DeleteBundle(int bundleId) await context.SaveChangesAsync(); await trans.CommitAsync(); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { return BadRequest("Could not delete bundle. If this bundle has been used, it cannot be deleted."); } diff --git a/web/Areas/CTS/Controllers/CompetencyController.cs b/web/Areas/CTS/Controllers/CompetencyController.cs index 9d9f12f66..4ab195663 100644 --- a/web/Areas/CTS/Controllers/CompetencyController.cs +++ b/web/Areas/CTS/Controllers/CompetencyController.cs @@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore; using Viper.Models.CTS; using Viper.Areas.CTS.Models; +using Microsoft.Data.SqlClient; namespace Viper.Areas.CTS.Controllers { @@ -180,7 +181,7 @@ public async Task> DeleteCompetency(int competencyId { await context.SaveChangesAsync(); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { return BadRequest("Could not remove domain. It may be linked to other objects."); } diff --git a/web/Areas/CTS/Controllers/CourseController.cs b/web/Areas/CTS/Controllers/CourseController.cs index 8c7b1c120..2d1c39f62 100644 --- a/web/Areas/CTS/Controllers/CourseController.cs +++ b/web/Areas/CTS/Controllers/CourseController.cs @@ -5,6 +5,7 @@ using Viper.Classes.SQLContext; using Viper.Models.CTS; using Web.Authorization; +using Microsoft.Data.SqlClient; namespace Viper.Areas.CTS.Controllers { @@ -103,7 +104,7 @@ public async Task>> SetCourseRoles(int courseId, List await context.SaveChangesAsync(); await trans.CommitAsync(); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { return BadRequest("Could not set roles."); } @@ -285,7 +286,7 @@ public async Task>> UpdateSessionCompete await context.SaveChangesAsync(); await trans.CommitAsync(); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { return BadRequest("Could not update levels."); } diff --git a/web/Areas/CTS/Controllers/DomainController.cs b/web/Areas/CTS/Controllers/DomainController.cs index 00419f446..91c9bf908 100644 --- a/web/Areas/CTS/Controllers/DomainController.cs +++ b/web/Areas/CTS/Controllers/DomainController.cs @@ -6,6 +6,7 @@ using Viper.Classes.SQLContext; using Viper.Models.CTS; using Web.Authorization; +using Microsoft.Data.SqlClient; namespace Viper.Areas.CTS.Controllers { @@ -92,7 +93,7 @@ public async Task> DeleteDomain(int domainId) { await context.SaveChangesAsync(); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { return BadRequest("Could not remove domain. It may be linked to other objects."); } diff --git a/web/Areas/CTS/Controllers/EpaController.cs b/web/Areas/CTS/Controllers/EpaController.cs index 3e4b2ba63..17ecd85f6 100644 --- a/web/Areas/CTS/Controllers/EpaController.cs +++ b/web/Areas/CTS/Controllers/EpaController.cs @@ -5,6 +5,7 @@ using Viper.Models.CTS; using Viper.Services; using Web.Authorization; +using Microsoft.Data.SqlClient; namespace Viper.Areas.CTS.Controllers { @@ -105,7 +106,7 @@ public async Task> DeleteEpa(int epaId) context.Entry(epa).State = EntityState.Deleted; await context.SaveChangesAsync(); } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { return BadRequest(ex.Message); } diff --git a/web/Areas/CTS/Controllers/LevelsController.cs b/web/Areas/CTS/Controllers/LevelsController.cs index b1253f487..0d8930ccf 100644 --- a/web/Areas/CTS/Controllers/LevelsController.cs +++ b/web/Areas/CTS/Controllers/LevelsController.cs @@ -6,6 +6,7 @@ using Viper.Classes.SQLContext; using Viper.Models.CTS; using Web.Authorization; +using Microsoft.Data.SqlClient; namespace Viper.Areas.CTS.Controllers { @@ -152,7 +153,7 @@ public async Task DeleteLevel(int levelId) AdjustLevelOrders(existing); await trans.CommitAsync(); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { return BadRequest("Could not delete level. If this level has been used in an assessment, it cannot be deleted."); } diff --git a/web/Areas/CTS/Controllers/RoleController.cs b/web/Areas/CTS/Controllers/RoleController.cs index ff6853906..0ae1b016a 100644 --- a/web/Areas/CTS/Controllers/RoleController.cs +++ b/web/Areas/CTS/Controllers/RoleController.cs @@ -4,6 +4,7 @@ using Viper.Classes; using Viper.Classes.SQLContext; using Web.Authorization; +using Microsoft.Data.SqlClient; namespace Viper.Areas.CTS.Controllers { @@ -84,7 +85,7 @@ public async Task> DeleteRole(int roleId) context.Entry(role).State = EntityState.Deleted; await context.SaveChangesAsync(); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { return BadRequest("Could not delete role. If this role has been added to a bundle, it cannot be deleted."); } diff --git a/web/Areas/ClinicalScheduler/Controllers/CliniciansController.cs b/web/Areas/ClinicalScheduler/Controllers/CliniciansController.cs index 458a9b7ee..5f257e8c0 100644 --- a/web/Areas/ClinicalScheduler/Controllers/CliniciansController.cs +++ b/web/Areas/ClinicalScheduler/Controllers/CliniciansController.cs @@ -6,6 +6,7 @@ using Viper.Models.ClinicalScheduler; using Web.Authorization; using Viper.Classes.Utilities; +using Microsoft.Data.SqlClient; namespace Viper.Areas.ClinicalScheduler.Controllers { @@ -334,7 +335,7 @@ public async Task GetClinicianSchedule(string mothraId, [FromQuer schedules.Count, LogSanitizer.SanitizeId(mothraId), LogSanitizer.SanitizeYear(targetYear)); return Ok(result); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("MothraId", mothraId); @@ -381,7 +382,7 @@ public async Task GetClinicianRotations(string mothraId) _logger.LogDebug("Found {RotationCount} unique rotations for clinician {MothraId}", rotations.Count, LogSanitizer.SanitizeId(mothraId)); return Ok(rotations); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("MothraId", mothraId); @@ -575,7 +576,7 @@ private IEnumerable FilterCliniciansByPermissions(IEnumerable< // All other cases (Admin, Manage, EditClnSchedules, rotation view, or service-specific permissions) see all clinicians return clinicians; } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error filtering clinicians by permissions. Returning unfiltered list."); return clinicians; // Return unfiltered list on error to avoid breaking functionality @@ -653,7 +654,7 @@ private bool CheckClinicianScheduleViewPermission(string targetMothraId) LogSanitizer.SanitizeId(currentUser.MothraId), LogSanitizer.SanitizeId(targetMothraId)); return false; } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error checking clinician schedule view permission for target {TargetMothraId}", LogSanitizer.SanitizeId(targetMothraId)); return false; // Deny access on error diff --git a/web/Areas/ClinicalScheduler/Controllers/InstructorScheduleController.cs b/web/Areas/ClinicalScheduler/Controllers/InstructorScheduleController.cs index ca7216f5e..d189b7d09 100644 --- a/web/Areas/ClinicalScheduler/Controllers/InstructorScheduleController.cs +++ b/web/Areas/ClinicalScheduler/Controllers/InstructorScheduleController.cs @@ -10,6 +10,8 @@ using Viper.Models.ClinicalScheduler; using Web.Authorization; using Viper.Classes.Utilities; +using Microsoft.EntityFrameworkCore; +using Microsoft.Data.SqlClient; namespace Viper.Areas.ClinicalScheduler.Controllers { @@ -106,7 +108,7 @@ public async Task AddInstructor( { return HandleInvalidOperation(ex, correlationId); } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { return HandleSystemError(ex, request.RotationId!.Value, correlationId); } @@ -335,7 +337,7 @@ public async Task RemoveInstructor( userMessage, correlationId)); } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error removing instructor schedule {ScheduleId} (CorrelationId: {CorrelationId})", instructorScheduleId, correlationId); @@ -414,7 +416,7 @@ public async Task SetPrimaryEvaluator( instructorScheduleId, correlationId); return Forbid(); } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error setting primary evaluator for instructor schedule {ScheduleId} (CorrelationId: {CorrelationId})", instructorScheduleId, correlationId); @@ -507,7 +509,7 @@ public async Task CheckScheduleConflicts( return Ok(response); } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error checking schedule conflicts for {MothraId}", LogSanitizer.SanitizeId(mothraId)); return StatusCode(500, "An error occurred while checking for schedule conflicts"); @@ -532,7 +534,7 @@ public async Task GetAuditHistory( var auditHistory = await _auditService.GetInstructorScheduleAuditHistoryAsync(instructorScheduleId, cancellationToken); return Ok(auditHistory); } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error retrieving audit history for instructor schedule {ScheduleId}", instructorScheduleId); return StatusCode(500, "An error occurred while retrieving audit history"); diff --git a/web/Areas/ClinicalScheduler/Controllers/PermissionsController.cs b/web/Areas/ClinicalScheduler/Controllers/PermissionsController.cs index 69e4808a5..b37c871a7 100644 --- a/web/Areas/ClinicalScheduler/Controllers/PermissionsController.cs +++ b/web/Areas/ClinicalScheduler/Controllers/PermissionsController.cs @@ -4,6 +4,7 @@ using Viper.Classes.SQLContext; using Web.Authorization; using Viper.Classes.Utilities; +using Microsoft.Data.SqlClient; namespace Viper.Areas.ClinicalScheduler.Controllers { @@ -153,7 +154,7 @@ public async Task> CanEditService(int serviceId) return Ok(new { canEdit }); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("ServiceId", serviceId); @@ -215,7 +216,7 @@ public async Task> CanEditRotation(int rotationId) return Ok(new { canEdit }); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("RotationId", rotationId); @@ -277,7 +278,7 @@ public async Task> CanEditOwnSchedule(int instructorSchedul return Ok(new { canEditOwn }); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("InstructorScheduleId", instructorScheduleId); diff --git a/web/Areas/ClinicalScheduler/Controllers/RotationsController.cs b/web/Areas/ClinicalScheduler/Controllers/RotationsController.cs index 05deb822f..ebb33f7a1 100644 --- a/web/Areas/ClinicalScheduler/Controllers/RotationsController.cs +++ b/web/Areas/ClinicalScheduler/Controllers/RotationsController.cs @@ -8,6 +8,7 @@ using Viper.Models.ClinicalScheduler; using Web.Authorization; using Person = Viper.Models.ClinicalScheduler.Person; +using Microsoft.Data.SqlClient; namespace Viper.Areas.ClinicalScheduler.Controllers { @@ -77,7 +78,7 @@ public async Task>> GetRotations(int? serv rotations.Count, filteredRotations.Count); return Ok(filteredRotations); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext(new Dictionary @@ -138,7 +139,7 @@ public async Task> GetRotation(int id) _logger.LogInformation("Retrieved rotation via RotationService: {RotationName}", rotation.Name); return Ok(response); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("RotationId", id); @@ -240,7 +241,7 @@ public async Task> GetRotationSchedule(int id, [FromQuery] return Ok(BuildRotationScheduleResponse(rotation, targetYear, groupedSchedules, recentCliniciansList)); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("RotationId", id); @@ -305,7 +306,7 @@ join w in _context.Weeks on i.WeekId equals w.WeekId _logger.LogInformation("Retrieved {Count} rotations with scheduled weeks for year {Year} (filtered to {FilteredCount})", rotationsWithSchedules.Count, LogSanitizer.SanitizeYear(targetYear), filteredRotations.Count); return Ok(filteredRotations); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { // Store context for ApiExceptionFilter to use in logging SetExceptionContext("Year", year?.ToString() ?? "null"); diff --git a/web/Areas/RAPS/Controllers/AdGroupsController.cs b/web/Areas/RAPS/Controllers/AdGroupsController.cs index 0c9067e67..b43f7601a 100644 --- a/web/Areas/RAPS/Controllers/AdGroupsController.cs +++ b/web/Areas/RAPS/Controllers/AdGroupsController.cs @@ -11,6 +11,7 @@ using Viper.Classes.Utilities; using Viper.Models.RAPS; using Web.Authorization; +using Microsoft.Data.SqlClient; namespace Viper.Areas.RAPS.Controllers { @@ -114,7 +115,7 @@ public async Task> CreateGroup(GroupAddEdit group) OuGroup newOuGroup = await _ouGroupService.CreateRapsGroup(group.Name, group.Description); return CreatedAtAction("CreateGroup", new { id = newOuGroup.OugroupId }, newOuGroup); } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { //Exception message could be indication user is trying to create a group that exists or is invalid. return ValidationProblem(ex.Message); diff --git a/web/Areas/RAPS/Controllers/RoleMembersController.cs b/web/Areas/RAPS/Controllers/RoleMembersController.cs index b449e5d70..37b8f7d88 100644 --- a/web/Areas/RAPS/Controllers/RoleMembersController.cs +++ b/web/Areas/RAPS/Controllers/RoleMembersController.cs @@ -268,7 +268,7 @@ private static void UpdateTblRoleMemberWithDto(TblRoleMember tblRoleMember, Role return vmacsResponse; } } - catch (System.Text.Json.JsonException) + catch (JsonException) { return new VmacsResponse() { diff --git a/web/Areas/RAPS/Controllers/RolesController.cs b/web/Areas/RAPS/Controllers/RolesController.cs index 83abfc56b..373462f0a 100644 --- a/web/Areas/RAPS/Controllers/RolesController.cs +++ b/web/Areas/RAPS/Controllers/RolesController.cs @@ -7,6 +7,7 @@ using Viper.Classes; using Viper.Classes.SQLContext; using Viper.Models.RAPS; +using Microsoft.Data.SqlClient; namespace Viper.Areas.RAPS.Controllers { @@ -225,7 +226,7 @@ public async Task> PostTblRole(string instance, RoleCreate { return Problem("The record was not updated because it was locked. " + ex.InnerException?.Message); } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { return Problem("There was a problem updating the database. " + ex.InnerException?.Message); } diff --git a/web/Areas/RAPS/Services/OuGroupService.cs b/web/Areas/RAPS/Services/OuGroupService.cs index fe7b62dcb..2debbe6bb 100644 --- a/web/Areas/RAPS/Services/OuGroupService.cs +++ b/web/Areas/RAPS/Services/OuGroupService.cs @@ -9,6 +9,7 @@ using Viper.Classes.Utilities; using Viper.Models.RAPS; using static Viper.Areas.RAPS.Services.RAPSAuditService; +using Microsoft.Data.SqlClient; namespace Viper.Areas.RAPS.Services { @@ -48,7 +49,7 @@ public async Task> GetAllGroups(string? search) { ActiveDirectoryService.GetGroups(); } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { Logger logger = LogManager.GetCurrentClassLogger(); logger.Error(ex); diff --git a/web/Areas/RAPS/Services/VMACSExport.cs b/web/Areas/RAPS/Services/VMACSExport.cs index 235ca6084..3da8c92c0 100644 --- a/web/Areas/RAPS/Services/VMACSExport.cs +++ b/web/Areas/RAPS/Services/VMACSExport.cs @@ -5,6 +5,7 @@ using Viper.Areas.RAPS.Models; using Viper.Classes.SQLContext; using Viper.Classes.Utilities; +using Microsoft.Data.SqlClient; namespace Viper.Areas.RAPS.Services { @@ -131,7 +132,7 @@ public async Task> ExportToVMACS(string instance, string? server = VmacsResponse vmacsResponse = await ParseResponse(response); RecordMessage(messages, JsonSerializer.Serialize(vmacsResponse)); } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { HttpHelper.Logger.Log(NLog.LogLevel.Warn, ex); RecordMessage(messages, "Error: " + ex.Message + " " + ex?.StackTrace); @@ -168,7 +169,7 @@ private static async Task ParseResponse(HttpResponseMessage respo vmacsResponse.Success = response.IsSuccessStatusCode; } } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { vmacsResponse = new() { diff --git a/web/Classes/ClaimsTransformer.cs b/web/Classes/ClaimsTransformer.cs index 6c387282a..35ace9172 100644 --- a/web/Classes/ClaimsTransformer.cs +++ b/web/Classes/ClaimsTransformer.cs @@ -6,6 +6,8 @@ using Viper.Classes; using Viper.Classes.SQLContext; using Viper.Models.AAUD; +using Microsoft.EntityFrameworkCore; +using Microsoft.Data.SqlClient; namespace Web.Authorization { @@ -89,7 +91,7 @@ public Task TransformAsync(ClaimsPrincipal principal) } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { bool isProd = false; diff --git a/web/Classes/SitemapMiddleware.cs b/web/Classes/SitemapMiddleware.cs index 286fd6482..05d757ad0 100644 --- a/web/Classes/SitemapMiddleware.cs +++ b/web/Classes/SitemapMiddleware.cs @@ -4,6 +4,8 @@ using System.Reflection; using System.Text; using Web.Authorization; +using Microsoft.EntityFrameworkCore; +using Microsoft.Data.SqlClient; namespace Viper.Classes { @@ -84,7 +86,7 @@ public async Task Invoke(HttpContext context) await memoryStream.CopyToAsync(stream, bytes.Length); } } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { await _next(context); } diff --git a/web/Classes/UserHelper.cs b/web/Classes/UserHelper.cs index 19ab69a8b..498183b32 100644 --- a/web/Classes/UserHelper.cs +++ b/web/Classes/UserHelper.cs @@ -5,6 +5,8 @@ using Viper.Models.AAUD; using Viper.Models.RAPS; using Web.Authorization; +using Microsoft.EntityFrameworkCore; +using Microsoft.Data.SqlClient; namespace Viper { @@ -291,7 +293,7 @@ public bool HasPermission(RAPSContext? rapsContext, AaudUser? user, string permi return user; } } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { HttpHelper.Logger.Error(ex); return null; @@ -318,7 +320,7 @@ public bool HasPermission(RAPSContext? rapsContext, AaudUser? user, string permi return currentUser; } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { HttpHelper.Logger.Error(ex); return null; @@ -355,7 +357,7 @@ public bool HasPermission(RAPSContext? rapsContext, AaudUser? user, string permi } else { return GetCurrentUser(); } } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { HttpHelper.Logger.Error(ex); return null; diff --git a/web/Classes/Utilities/ActiveDirectoryService.cs b/web/Classes/Utilities/ActiveDirectoryService.cs index 2348991e5..870e34b6f 100644 --- a/web/Classes/Utilities/ActiveDirectoryService.cs +++ b/web/Classes/Utilities/ActiveDirectoryService.cs @@ -2,6 +2,8 @@ using System.DirectoryServices.Protocols; using System.Runtime.Versioning; using Viper.Areas.RAPS.Models; +using Microsoft.EntityFrameworkCore; +using Microsoft.Data.SqlClient; namespace Viper.Classes.Utilities { @@ -219,7 +221,7 @@ public static void AddUserToGroup(string userDn, string groupDn) group.Save(); } } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { HttpHelper.Logger.Error(ex); } @@ -245,7 +247,7 @@ public static void RemoveUserFromGroup(string userDn, string groupDn) group.Save(); } } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { HttpHelper.Logger.Error(ex); } diff --git a/web/Classes/Utilities/F5HttpRequest.cs b/web/Classes/Utilities/F5HttpRequest.cs index 61bc3a3e5..a524e9852 100644 --- a/web/Classes/Utilities/F5HttpRequest.cs +++ b/web/Classes/Utilities/F5HttpRequest.cs @@ -1,4 +1,6 @@ using System.Text.Json; +using Microsoft.EntityFrameworkCore; +using Microsoft.Data.SqlClient; namespace Viper.Classes.Utilities { @@ -21,7 +23,7 @@ public async Task Send(HttpRequestMessage request, int atte { response = await _httpClient.SendAsync(request); } - catch (Exception) when (true /* placeholder */) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { response = await HandleConnectionFail(request, attemptNumber); } diff --git a/web/Classes/Utilities/IamApi.cs b/web/Classes/Utilities/IamApi.cs index d9d87d35d..083711475 100644 --- a/web/Classes/Utilities/IamApi.cs +++ b/web/Classes/Utilities/IamApi.cs @@ -4,6 +4,8 @@ using System.Text.Json; using System.Web; using Viper.Models.IAM; +using Microsoft.EntityFrameworkCore; +using Microsoft.Data.SqlClient; namespace Viper.Classes.Utilities { @@ -449,7 +451,7 @@ static private async Task> ParseResponse(HttpResponseMessage? res } } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { r.ErrorMessage = "Invalid response: " + ex.Message + "."; } diff --git a/web/Program.cs b/web/Program.cs index f4f0ad74e..4aa474663 100644 --- a/web/Program.cs +++ b/web/Program.cs @@ -27,6 +27,7 @@ using Viper.Classes.SQLContext; using Web; using Web.Authorization; +using Microsoft.Data.SqlClient; // Load .env.local for local development only (multiple-instance support) // Avoid loading in production - guard by ASPNETCORE_ENVIRONMENT. @@ -75,7 +76,7 @@ .AddSystemsManager("/" + builder.Environment.EnvironmentName, awsOptions) .AddSystemsManager("/Shared", awsOptions); } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { logger.Fatal(ex, "Failed to get secrets from AWS"); } @@ -439,7 +440,7 @@ void RegisterDbContext(string connectionStringKey) where TContext : Db await ViteProxyHelpers.CopyProxyResponse(context, response); return; // Successfully proxied, don't continue to static files } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { var logger = context.RequestServices.GetRequiredService>(); logger.LogDebug(ex, "Vite server not available, falling back to static files for {Path}", @@ -572,7 +573,7 @@ void SetAwsCredentials(Logger logger) { File.Delete(awsCredentialsFilePath); } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { logger.Error(ex, $"COULD NOT DELETE THE AWS CREDENTIALS XML FILE (\"{awsCredentialsFilePath}\"). The file will need to be deleted manually."); logger.Error(ex, $"COULD NOT DELETE THE AWS CREDENTIALS XML FILE (\"{awsCredentialsFilePath}\"). The file will need to be deleted manually."); diff --git a/web/Services/EmailService.cs b/web/Services/EmailService.cs index caeb6282f..b11866d36 100644 --- a/web/Services/EmailService.cs +++ b/web/Services/EmailService.cs @@ -4,6 +4,8 @@ using Microsoft.Extensions.Options; using MimeKit; using Viper.Classes.Utilities; +using Microsoft.EntityFrameworkCore; +using Microsoft.Data.SqlClient; namespace Viper.Services { @@ -218,7 +220,7 @@ public async Task IsServiceAvailableAsync() // For production, assume the SMTP server is available return true; } - catch (Exception ex) when (ex is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException) + catch (Exception ex) when (ex is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException) { _logger.LogError(ex, "Error checking email service availability"); return false; diff --git a/web/ViteProxyHelpers.cs b/web/ViteProxyHelpers.cs index 14099e0e3..fada34ff3 100644 --- a/web/ViteProxyHelpers.cs +++ b/web/ViteProxyHelpers.cs @@ -1,5 +1,7 @@ using System.Net; using System.Text.RegularExpressions; +using Microsoft.Data.SqlClient; +using Microsoft.EntityFrameworkCore; namespace Web; @@ -278,7 +280,7 @@ public static async Task CopyProxyResponse(HttpContext context, HttpResponseMess { context.Response.Headers[header.Key] = header.Value.ToArray(); } - catch (Exception headerEx) when (headerEx is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException or System.IO.IOException) + catch (Exception headerEx) when (headerEx is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException or IOException) { // Use structured logging for header errors var safeHeaderKey = WebUtility.HtmlEncode(header.Key); @@ -361,7 +363,7 @@ public static async Task HandleProxyError(HttpContext context, Exception ex, ILo return; } } - catch (Exception fileEx) when (fileEx is Microsoft.EntityFrameworkCore.DbUpdateException or Microsoft.Data.SqlClient.SqlException or InvalidOperationException or OperationCanceledException or System.IO.IOException) + catch (Exception fileEx) when (fileEx is DbUpdateException or SqlException or InvalidOperationException or OperationCanceledException or IOException) { var safePath = context.Request.Path.ToString().Replace("\r", "").Replace("\n", ""); logger.LogWarning(fileEx, "Failed to serve static file fallback for {Path}", safePath);