diff --git a/PARR.DAL/DomainServices/UnitFilterService/IUnitFilterService.cs b/PARR.DAL/DomainServices/UnitFilterService/IUnitFilterService.cs
index b3a50076..f7619047 100644
--- a/PARR.DAL/DomainServices/UnitFilterService/IUnitFilterService.cs
+++ b/PARR.DAL/DomainServices/UnitFilterService/IUnitFilterService.cs
@@ -23,7 +23,8 @@ namespace PARR.DAL.DomainServices.UnitFilterService
/// Список ID Unit'ов
Task?> GetUnitsIdByJobFilterAsync(
Job job,
- int? takeCount = null);
+ int? takeCount = null,
+ CancellationToken cancellationToken = default);
///
/// Получает отфильтрованные Unit'ы с полной информацией о значениях полей и связях.
@@ -34,7 +35,8 @@ namespace PARR.DAL.DomainServices.UnitFilterService
/// Список UnitFilterResultDto или null, если Job не найден
Task?> GetUnitsByJobFilterAsync(
Guid jobId,
- int? takeCount = null);
+ int? takeCount = null,
+ CancellationToken cancellationToken = default);
///
/// Получает отфильтрованные Unit'ы с полной информацией о значениях полей и связях.
@@ -45,7 +47,9 @@ namespace PARR.DAL.DomainServices.UnitFilterService
/// Список UnitFilterResultDto
Task?> GetUnitsByJobFilterAsync(
Job job,
- int? takeCount = null);
+ int? takeCount = null,
+ CancellationToken cancellationToken = default
+ );
///
/// Получает имена связанных Unit'ов для заданного Unit, отфильтрованных по RelationshipFilters Job.
@@ -56,6 +60,8 @@ namespace PARR.DAL.DomainServices.UnitFilterService
/// Список имён связанных Unit'ов (уникальные, без дублей)
Task> GetRelatedUnitNamesAsync(
Guid jobId,
- Guid unitId);
+ Guid unitId,
+ CancellationToken cancellationToken = default
+ );
}
}
diff --git a/PARR.DAL/DomainServices/UnitFilterService/UnitFilterService.cs b/PARR.DAL/DomainServices/UnitFilterService/UnitFilterService.cs
index e29ebca9..bab9577c 100644
--- a/PARR.DAL/DomainServices/UnitFilterService/UnitFilterService.cs
+++ b/PARR.DAL/DomainServices/UnitFilterService/UnitFilterService.cs
@@ -49,21 +49,33 @@ internal class UnitFilterService : IUnitFilterService
}
- public async Task?> GetUnitsByJobFilterAsync(Guid jobId, int? takeCount = null)
+ public async Task?> GetUnitsByJobFilterAsync(
+ Guid jobId,
+ int? takeCount = null,
+ CancellationToken cancellationToken = default
+ )
{
var job = await LoadJobWithFiltersAsync(jobId);
- return job == null ? null : await GetUnitsByJobFilterAsync(job, takeCount);
+ return job == null ? null : await GetUnitsByJobFilterAsync(job, takeCount, cancellationToken);
}
- public async Task?> GetUnitsIdByJobFilterAsync(Job job, int? takeCount = null)
+ public async Task?> GetUnitsIdByJobFilterAsync(
+ Job job,
+ int? takeCount = null,
+ CancellationToken cancellationToken = default
+ )
{
- var filteredUnits = await GetUnitsByJobFilterAsync(job, takeCount);
+ var filteredUnits = await GetUnitsByJobFilterAsync(job, takeCount, cancellationToken);
return filteredUnits?.Select(u => u.Id).ToList();
}
- public async Task?> GetUnitsByJobFilterAsync(Job job, int? takeCount = null)
+ public async Task?> GetUnitsByJobFilterAsync(
+ Job job,
+ int? takeCount = null,
+ CancellationToken cancellationToken = default
+ )
{
if (job.Group == null)
throw new ArgumentNullException(nameof(job.Group), $"Job {job.Id} не содержит Group");
@@ -92,7 +104,7 @@ internal class UnitFilterService : IUnitFilterService
logger.LogDebug("Применение фильтра #{Index} (Id={FilterId})", i + 1, filter.Id);
// 1. Найти ID юнитов по UnitFilter (Name LIKE)
- var initialUnitIds = await GetUnitIdsFromCacheOrDbAsync(filter);
+ var initialUnitIds = await GetUnitIdsFromCacheOrDbAsync(filter, cancellationToken);
if (!initialUnitIds.Any())
{
logger.LogDebug("Фильтр #{Index}: пропущен (0 юнитов)", i + 1);
@@ -102,13 +114,13 @@ internal class UnitFilterService : IUnitFilterService
#if DEBUG
if (initialUnitIds.Contains(targetUnitId))
{
- logger.LogDebug("🎯 DEBUG: Юнит {TargetUnitId} найден в initialUnitIds для фильтра #{Index}", targetUnitId, i + 1);
+ logger.LogDebug("DEBUG: Юнит {TargetUnitId} найден в initialUnitIds для фильтра #{Index}", targetUnitId, i + 1);
}
#endif
// 2. Применить FieldFilters на уровне SQL
var fieldStopwatch = Stopwatch.StartNew();
- var fieldFilteredIds = await ApplyFieldFiltersOnDbAsync(initialUnitIds, filter.FieldFilters);
+ var fieldFilteredIds = await ApplyFieldFiltersOnDbAsync(initialUnitIds, filter.FieldFilters, cancellationToken);
fieldStopwatch.Stop();
if (!fieldFilteredIds.Any())
@@ -120,13 +132,13 @@ internal class UnitFilterService : IUnitFilterService
#if DEBUG
if (initialUnitIds.Contains(targetUnitId) && !fieldFilteredIds.Contains(targetUnitId))
{
- logger.LogDebug("🎯 DEBUG: Юнит {TargetUnitId} ОТФИЛЬТРОВАН на этапе FieldFilters (Фильтр #{Index})", targetUnitId, i + 1);
+ logger.LogDebug("DEBUG: Юнит {TargetUnitId} ОТФИЛЬТРОВАН на этапе FieldFilters (Фильтр #{Index})", targetUnitId, i + 1);
}
#endif
// 3. Применить RelationshipFilters на уровне SQL -> ВОЗВРАЩАЕТ FilteredUnitContext
var relStopwatch = Stopwatch.StartNew();
- var relationshipFilteredContexts = await ApplyRelationshipFiltersOnDbAsync(fieldFilteredIds, filter.RelationshipFilters);
+ var relationshipFilteredContexts = await ApplyRelationshipFiltersOnDbAsync(fieldFilteredIds, filter.RelationshipFilters, cancellationToken);
relStopwatch.Stop();
if (!relationshipFilteredContexts.Any())
@@ -139,7 +151,7 @@ internal class UnitFilterService : IUnitFilterService
var targetContext = relationshipFilteredContexts.FirstOrDefault(c => c.UnitId == targetUnitId);
if (targetContext != null)
{
- logger.LogDebug("🎯 DEBUG: Юнит {TargetUnitId} прошёл фильтр #{Index}. Родителей: {ParentCount}, Детей: {ChildCount}",
+ logger.LogDebug("DEBUG: Юнит {TargetUnitId} прошёл фильтр #{Index}. Родителей: {ParentCount}, Детей: {ChildCount}",
targetUnitId, i + 1, targetContext.ValidParentIds.Count, targetContext.ValidChildIds.Count);
}
#endif
@@ -194,7 +206,7 @@ internal class UnitFilterService : IUnitFilterService
: targetBeforeUmbrella.ValidChildIds.Count;
var passes = count >= (job.MinValueRelationships ?? 0) && count <= (job.MaxValueRelationships ?? int.MaxValue);
- logger.LogDebug("🎯 DEBUG: Юнит {TargetUnitId} перед Umbrella: Count={Count}, Min={Min}, Max={Max}, Passes={Passes}",
+ logger.LogDebug("DEBUG: Юнит {TargetUnitId} перед Umbrella: Count={Count}, Min={Min}, Max={Max}, Passes={Passes}",
targetUnitId, count, job.MinValueRelationships, job.MaxValueRelationships, passes);
}
#endif
@@ -207,7 +219,7 @@ internal class UnitFilterService : IUnitFilterService
var targetAfterUmbrella = umbrellaFilteredContexts.FirstOrDefault(c => c.UnitId == targetUnitId);
if (targetBeforeUmbrella != null && targetAfterUmbrella == null)
{
- logger.LogDebug("🎯 DEBUG: Юнит {TargetUnitId} ОТФИЛЬТРОВАН на этапе Umbrella", targetUnitId);
+ logger.LogDebug("DEBUG: Юнит {TargetUnitId} ОТФИЛЬТРОВАН на этапе Umbrella", targetUnitId);
}
#endif
}
@@ -252,7 +264,8 @@ internal class UnitFilterService : IUnitFilterService
///
private async Task> ApplyFieldFiltersOnDbAsync(
List unitIds,
- IEnumerable fieldFilters)
+ IEnumerable fieldFilters,
+ CancellationToken cancellationToken = default)
{
if (!fieldFilters.Any())
return unitIds;
@@ -303,7 +316,7 @@ internal class UnitFilterService : IUnitFilterService
}
// Выполняем промежуточный запрос для логирования
- var intermediateResult = await query.Select(u => u.Id).ToListAsync();
+ var intermediateResult = await query.Select(u => u.Id).ToListAsync(cancellationToken);
logger.LogDebug(" После фильтра #{Index}: осталось {Count} юнитов", filterIndex, intermediateResult.Count);
// Обновляем query для следующей итерации
@@ -311,7 +324,7 @@ internal class UnitFilterService : IUnitFilterService
.Where(u => intermediateResult.Contains(u.Id));
}
- var result = await query.Select(u => u.Id).ToListAsync();
+ var result = await query.Select(u => u.Id).ToListAsync(cancellationToken);
logger.LogDebug("ApplyFieldFiltersOnDbAsync: выход {UnitCount} юнитов", result.Count);
return result;
@@ -326,7 +339,8 @@ internal class UnitFilterService : IUnitFilterService
///
private async Task> ApplyRelationshipFiltersOnDbAsync(
List unitIds,
- IEnumerable relationshipFilters
+ IEnumerable relationshipFilters,
+ CancellationToken cancellationToken = default
)
{
if (!relationshipFilters.Any() || !unitIds.Any())
@@ -342,7 +356,7 @@ internal class UnitFilterService : IUnitFilterService
#if DEBUG
if (unitIds.Contains(targetUnitId))
{
- logger.LogDebug("🎯 DEBUG: Юнит {TargetUnitId} присутствует во входных данных ApplyRelationshipFiltersOnDbAsync", targetUnitId);
+ logger.LogDebug("DEBUG: Юнит {TargetUnitId} присутствует во входных данных ApplyRelationshipFiltersOnDbAsync", targetUnitId);
}
#endif
@@ -360,7 +374,7 @@ internal class UnitFilterService : IUnitFilterService
.AsNoTracking()
.Where(link => unitIds.Contains(link.ChildUnitId))
.Select(link => new { ChildId = link.ChildUnitId, ParentId = link.ParentUnitId })
- .ToListAsync();
+ .ToListAsync(cancellationToken);
var allParentIds = allParentLinks.Select(l => l.ParentId).Distinct().ToList();
logger.LogDebug(" Найдено {ParentCount} уникальных родителей для {LinkCount} связей",
@@ -398,7 +412,7 @@ internal class UnitFilterService : IUnitFilterService
v.FieldId == relFilter.FieldId &&
EF.Functions.Like(v.Value.Value, dbValueMask)))
.Select(u => u.Id)
- .ToListAsync();
+ .ToListAsync(cancellationToken);
logger.LogDebug(" Найдено {MatchCount} родителей по маске", matchingParents.Count);
@@ -439,7 +453,7 @@ internal class UnitFilterService : IUnitFilterService
.AsNoTracking()
.Where(link => unitIds.Contains(link.ParentUnitId))
.Select(link => new { ParentId = link.ParentUnitId, ChildId = link.ChildUnitId })
- .ToListAsync();
+ .ToListAsync(cancellationToken);
var allChildIds = allChildLinks.Select(l => l.ChildId).Distinct().ToList();
logger.LogDebug(" Найдено {ChildCount} уникальных детей для {LinkCount} связей",
@@ -477,7 +491,7 @@ internal class UnitFilterService : IUnitFilterService
v.FieldId == relFilter.FieldId &&
EF.Functions.Like(v.Value.Value, dbValueMask)))
.Select(u => u.Id)
- .ToListAsync();
+ .ToListAsync(cancellationToken);
logger.LogDebug(" Найдено {MatchCount} детей по маске", matchingChildren.Count);
@@ -540,7 +554,7 @@ internal class UnitFilterService : IUnitFilterService
#if DEBUG
if (context.UnitId == targetUnitId)
{
- logger.LogDebug("🎯 DEBUG Umbrella: Юнит {TargetUnitId}, Count={Count}, Min={Min}, Max={Max}, Passes={Passes}",
+ logger.LogDebug("DEBUG Umbrella: Юнит {TargetUnitId}, Count={Count}, Min={Min}, Max={Max}, Passes={Passes}",
targetUnitId, count, min, max, passes);
}
#endif
@@ -559,7 +573,7 @@ internal class UnitFilterService : IUnitFilterService
///
///
///
- private async Task> LoadFinalResultAsync(List contexts)
+ private async Task> LoadFinalResultAsync(List contexts, CancellationToken cancellationToken = default)
{
var allResults = new List();
@@ -567,19 +581,19 @@ internal class UnitFilterService : IUnitFilterService
var targetInContexts = contexts.FirstOrDefault(c => c.UnitId == targetUnitId);
if (targetInContexts != null)
{
- logger.LogDebug("🎯 DEBUG: Юнит {TargetUnitId} передан в LoadFinalResultAsync. Родителей: {ParentCount}, Детей: {ChildCount}",
+ logger.LogDebug("DEBUG: Юнит {TargetUnitId} передан в LoadFinalResultAsync. Родителей: {ParentCount}, Детей: {ChildCount}",
targetUnitId, targetInContexts.ValidParentIds.Count, targetInContexts.ValidChildIds.Count);
}
else
{
- logger.LogDebug("🎯 DEBUG: Юнит {TargetUnitId} НЕ передан в LoadFinalResultAsync", targetUnitId);
+ logger.LogDebug("DEBUG: Юнит {TargetUnitId} НЕ передан в LoadFinalResultAsync", targetUnitId);
}
#endif
for (int i = 0; i < contexts.Count; i += batchSize)
{
var batch = contexts.Skip(i).Take(batchSize).ToList();
- var batchResults = await LoadBatchAsync(batch);
+ var batchResults = await LoadBatchAsync(batch, cancellationToken);
allResults.AddRange(batchResults);
}
@@ -592,7 +606,7 @@ internal class UnitFilterService : IUnitFilterService
///
///
///
- private async Task> LoadBatchAsync(List batch)
+ private async Task> LoadBatchAsync(List batch, CancellationToken cancellationToken = default)
{
var unitIds = batch.Select(c => c.UnitId).ToList();
var allParentIds = batch.SelectMany(c => c.ValidParentIds).Distinct().ToList();
@@ -603,7 +617,7 @@ internal class UnitFilterService : IUnitFilterService
.AsSingleQuery()
.Include(u => u.UnitValues).ThenInclude(v => v.Value)
.Where(u => unitIds.Contains(u.Id))
- .ToDictionaryAsync(u => u.Id);
+ .ToDictionaryAsync(u => u.Id, cancellationToken);
// Загружаем родителей
Dictionary parentsMap;
@@ -613,7 +627,7 @@ internal class UnitFilterService : IUnitFilterService
.AsSingleQuery()
.Include(u => u.UnitValues).ThenInclude(v => v.Value)
.Where(u => allParentIds.Contains(u.Id))
- .ToDictionaryAsync(u => u.Id);
+ .ToDictionaryAsync(u => u.Id, cancellationToken);
}
else
{
@@ -628,7 +642,7 @@ internal class UnitFilterService : IUnitFilterService
.AsSingleQuery()
.Include(u => u.UnitValues).ThenInclude(v => v.Value)
.Where(u => allChildIds.Contains(u.Id))
- .ToDictionaryAsync(u => u.Id);
+ .ToDictionaryAsync(u => u.Id, cancellationToken);
}
else
{
@@ -681,14 +695,14 @@ internal class UnitFilterService : IUnitFilterService
- public async Task> GetRelatedUnitNamesAsync(Guid jobId, Guid unitId)
+ public async Task> GetRelatedUnitNamesAsync(Guid jobId, Guid unitId, CancellationToken cancellationToken = default)
{
logger.LogDebug("Начало GetRelatedUnitNamesAsync. JobId: {JobId}, UnitId: {UnitId}", jobId, unitId);
var job = await jobService
.Get().AsNoTracking()
.Include(j => j.UnitFilters).ThenInclude(uf => uf.RelationshipFilters)
- .FirstOrDefaultAsync(j => j.Id == jobId);
+ .FirstOrDefaultAsync(j => j.Id == jobId, cancellationToken);
if (job == null)
{
@@ -762,7 +776,7 @@ internal class UnitFilterService : IUnitFilterService
var names = await unitService.Get().AsNoTracking()
.Where(u => matchingUnitIds.Contains(u.Id))
.Select(u => u.Name)
- .ToListAsync();
+ .ToListAsync(cancellationToken);
result.UnionWith(names);
}
}
@@ -772,7 +786,7 @@ internal class UnitFilterService : IUnitFilterService
#region вспомогательные методы
- private async Task> GetUnitIdsFromCacheOrDbAsync(JobUnitFilter filter)
+ private async Task> GetUnitIdsFromCacheOrDbAsync(JobUnitFilter filter, CancellationToken cancellationToken = default)
{
var cacheKey = cacheService.GetKey(new[] { "uf_ids", filter.UnitFilter }, isUseHash: true);
@@ -785,7 +799,7 @@ internal class UnitFilterService : IUnitFilterService
var initialUnitIds = await unitService.Get().AsNoTracking()
.Where(unit => EF.Functions.Like(unit.Name, filter.UnitFilter))
.Select(u => u.Id)
- .ToListAsync();
+ .ToListAsync(cancellationToken);
var toCache = new UnitFilterIds
{