public Enumerator(RemovableEntitiesCollectHandlerCollection collection) { _delegateInfo = new[] { RemovableEntitiesCollectHandlerInfo.Create(nameof(Failure), collection.CollectAutobookFailures), RemovableEntitiesCollectHandlerInfo.Create(nameof(Recommendation), collection.CollectRecommendationProcessors), RemovableEntitiesCollectHandlerInfo.Create(nameof(ResultsFile), collection.CollectResultFiles), RemovableEntitiesCollectHandlerInfo.Create(nameof(SmoothFailure), collection.CollectSmoothFailures), RemovableEntitiesCollectHandlerInfo.Create(nameof(Pass), collection.CollectScenarioPasses), RemovableEntitiesCollectHandlerInfo.Create(nameof(ScenarioCampaignResult), collection.CollectScenarioCampaignResult), RemovableEntitiesCollectHandlerInfo.Create(nameof(ScenarioCampaignFailure), collection.CollectScenarioCampaignFailures), RemovableEntitiesCollectHandlerInfo.Create(nameof(ScenarioCampaignMetric), collection.CollectScenarioCampaignMetrics), RemovableEntitiesCollectHandlerInfo.Create(nameof(AutoBookTask), collection.CollectAutobookTasks), RemovableEntitiesCollectHandlerInfo.Create(nameof(PipelineAuditEvent), collection.CollectPipelineAuditEvents), RemovableEntitiesCollectHandlerInfo.Create(nameof(AutoBookTaskReport), collection.CollectAutobookTaskReports), RemovableEntitiesCollectHandlerInfo.Create(nameof(ScenarioResult), collection.CollectScenarioResults), RemovableEntitiesCollectHandlerInfo.Create(nameof(Scenario), collection.CollectScenarios), RemovableEntitiesCollectHandlerInfo.Create(nameof(Run), collection.CollectRuns) }; }
protected override async Task DeleteRunDataAsync(IReadOnlyCollection <RunDeletionInfo> runDeletionInfos, CancellationToken cancellationToken) { var allRunIds = runDeletionInfos.Select(r => r.RunId).ToArray(); var allScenarioIds = runDeletionInfos.SelectMany(r => r.ScenarioIds).Distinct().ToArray(); var actionBlock = new ActionBlock <RemovableEntitiesCollectHandlerInfo>(RemoveEntitiesAsync, new ExecutionDataflowBlockOptions { CancellationToken = cancellationToken, MaxDegreeOfParallelism = DataflowBlockOptions.Unbounded }); var collectHandlerCollection = new RemovableEntitiesCollectHandlerCollection(allRunIds, allScenarioIds, cancellationToken); foreach (var itemInfo in collectHandlerCollection.Where(x => x.EntityName != nameof(Run))) { _ = await actionBlock.SendAsync(itemInfo, cancellationToken).ConfigureAwait(false); } actionBlock.Complete(); await actionBlock.Completion.AggregateExceptions().ConfigureAwait(false); // removes Run entities only when all dependent entities have been removed successfully var runCollectHandlerInfo = collectHandlerCollection.FirstOrDefault(x => x.EntityName == nameof(Run)); if (!(runCollectHandlerInfo is null)) { await RemoveEntitiesAsync(runCollectHandlerInfo).ConfigureAwait(false); } #region Local functions async Task RemoveEntitiesAsync(RemovableEntitiesCollectHandlerInfo handlerInfo) { try { using (var dbContext = _dbContextFactory.Create()) { var entities = await handlerInfo.CollectFunc(dbContext).ConfigureAwait(false); foreach (var entity in entities) { dbContext.Specific.Attach(entity).State = EntityState.Deleted; } while (true) { try { await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false); break; } catch (DbUpdateConcurrencyException ex) { if (ex.Entries.Count == 0) { throw; } foreach (var entry in ex.Entries) { entry.State = EntityState.Detached; } } } } } catch (OperationCanceledException) { throw; } catch (Exception ex) { var formatterRunInfo = allRunIds.Length == 1 ? $"run with id '{allRunIds.First()}'" : $"{allRunIds.Length} runs ({string.Join(", ", allRunIds.Select(x => $"'{x}'"))})"; throw new RunCleaningException( $"Deletion of {formatterRunInfo} has been failed while removing '{handlerInfo.EntityName}' entities. See inner exception for more details.", ex); } } #endregion Local functions }