diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/ExceptionExtensions.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/ExceptionExtensions.cs new file mode 100644 index 000000000..5c6f937d4 --- /dev/null +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/ExceptionExtensions.cs @@ -0,0 +1,21 @@ +using System.ServiceModel; +using Microsoft.Xrm.Sdk; + +namespace TeachingRecordSystem.Core.Dqt; + +public static class ExceptionExtensions +{ + public static bool IsCrmRateLimitException(this Exception exception, out TimeSpan retryAfter) + { + if (exception is FaultException fault && + fault.Detail.ErrorDetails.TryGetValue("Retry-After", out var retryAfterObj) && + retryAfterObj is TimeSpan retryAfterTs) + { + retryAfter = retryAfterTs; + return true; + } + + retryAfter = default; + return false; + } +} diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Services/CrmEntityChanges/CrmEntityChangesService.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Services/CrmEntityChanges/CrmEntityChangesService.cs index b2d97f29a..a61a1c651 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Services/CrmEntityChanges/CrmEntityChangesService.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Services/CrmEntityChanges/CrmEntityChangesService.cs @@ -96,6 +96,11 @@ public async IAsyncEnumerable GetEntityChanges( request.PageInfo.PagingCookie = null; continue; } + catch (Exception ex) when (ex.IsCrmRateLimitException(out var retryAfter)) + { + await Task.Delay(retryAfter, cancellationToken); + continue; + } gotData |= response.EntityChanges.Changes.Count > 0; diff --git a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Services/DqtReporting/DqtReportingService.cs b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Services/DqtReporting/DqtReportingService.cs index a7343520a..bb83cea48 100644 --- a/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Services/DqtReporting/DqtReportingService.cs +++ b/TeachingRecordSystem/src/TeachingRecordSystem.Core/Dqt/Services/DqtReporting/DqtReportingService.cs @@ -1,6 +1,4 @@ -#nullable enable using System.Data; -using System.ServiceModel; using System.Text; using Microsoft.ApplicationInsights; using Microsoft.ApplicationInsights.DataContracts; @@ -72,13 +70,6 @@ protected override async Task ExecuteAsync(CancellationToken stoppingToken) _logger.LogWarning(ex, "Transient SQL exception thrown."); continue; } - catch (ProcessChangesException ex) when (ex.InnerException!.IsCrmRateLimitException(out var retryAfter)) - { - _logger.LogWarning(ex, "Hit CRM rate limit error."); - - await Task.Delay(retryAfter, stoppingToken); - continue; - } catch (ProcessChangesException ex) { _logger.LogError(ex.InnerException, ex.Message); @@ -153,7 +144,7 @@ await Parallel.ForEachAsync( } catch (Exception ex) { - throw new ProcessChangesException($"Failed processing changes for '{entityType}' entity.", ex); + throw new ProcessChangesException(entityType, ex); } }); } @@ -384,20 +375,3 @@ public ProcessChangesException(string entityType, Exception innerException) private static string GetMessage(string entityType) => $"Failed processing changes for '{entityType}' entity."; } - -file static class ExceptionExtensions -{ - public static bool IsCrmRateLimitException(this Exception exception, out TimeSpan retryAfter) - { - if (exception is FaultException fault && - fault.Detail.ErrorDetails.TryGetValue("Retry-After", out var retryAfterObj) && - retryAfterObj is TimeSpan retryAfterTs) - { - retryAfter = retryAfterTs; - return true; - } - - retryAfter = default; - return false; - } -}