diff --git a/backend/Tnsu.ApprovalRoute.Application/RequestManage/RequestManageService.cs b/backend/Tnsu.ApprovalRoute.Application/RequestManage/RequestManageService.cs index 50e18aa..4cc7af0 100644 --- a/backend/Tnsu.ApprovalRoute.Application/RequestManage/RequestManageService.cs +++ b/backend/Tnsu.ApprovalRoute.Application/RequestManage/RequestManageService.cs @@ -152,13 +152,21 @@ public sealed class RequestManageService( var statusFilter = string.Join(" or ", activeStatuses.Select(s => $"{StatusField} eq '{Escape(s)}'")); var email = approverEmail.Trim(); + // DbContext не потокобезопасен: архивные партиции читаем из БД ПОСЛЕДОВАТЕЛЬНО, + // и только дальнейшее чтение SharePoint (без БД) идёт параллельно. Раньше + // GetArchivedAsync вызывался из параллельных задач — «вторая операция на + // контексте» падала, а пустой catch превращал это в пустой список. + var archivedByType = new Dictionary>(); + foreach (var rt in requestTypes) + archivedByType[rt.Id] = await partitionRegistry.GetArchivedAsync(rt, cancellationToken); + using var gate = new SemaphoreSlim(MaxParallelTypes); var results = await Task.WhenAll(requestTypes.Select(async rt => { await gate.WaitAsync(cancellationToken); try { - return await FindForTypeSafeAsync(rt, statusFilter, email, cancellationToken); + return await FindForTypeSafeAsync(rt, archivedByType[rt.Id], statusFilter, email, cancellationToken); } finally { @@ -170,11 +178,11 @@ public sealed class RequestManageService( } private async Task> FindForTypeSafeAsync( - RequestType requestType, string statusFilter, string approverEmail, CancellationToken cancellationToken) + RequestType requestType, IReadOnlyList archived, string statusFilter, string approverEmail, CancellationToken cancellationToken) { try { - return await FindForTypeAsync(requestType, statusFilter, approverEmail, cancellationToken); + return await FindForTypeAsync(requestType, archived, statusFilter, approverEmail, cancellationToken); } catch (Exception ex) when (ex is not OperationCanceledException) { @@ -186,10 +194,9 @@ public sealed class RequestManageService( } private async Task> FindForTypeAsync( - RequestType requestType, string statusFilter, string approverEmail, CancellationToken cancellationToken) + RequestType requestType, IReadOnlyList archived, string statusFilter, string approverEmail, CancellationToken cancellationToken) { var siteUrl = requestType.SiteUrl!; - var archived = await partitionRegistry.GetArchivedAsync(requestType, cancellationToken); var current = await partitionRegistry.BuildCurrentAsync(requestType, archived, cancellationToken); var partitions = archived