feat(dal): UnitFilterService добавлена обработка cancellationToken
This commit is contained in:
@@ -23,7 +23,8 @@ namespace PARR.DAL.DomainServices.UnitFilterService
|
||||
/// <returns>Список ID Unit'ов</returns>
|
||||
Task<IEnumerable<Guid>?> GetUnitsIdByJobFilterAsync(
|
||||
Job job,
|
||||
int? takeCount = null);
|
||||
int? takeCount = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Получает отфильтрованные Unit'ы с полной информацией о значениях полей и связях.
|
||||
@@ -34,7 +35,8 @@ namespace PARR.DAL.DomainServices.UnitFilterService
|
||||
/// <returns>Список UnitFilterResultDto или null, если Job не найден</returns>
|
||||
Task<IEnumerable<UnitFilterResultDto>?> GetUnitsByJobFilterAsync(
|
||||
Guid jobId,
|
||||
int? takeCount = null);
|
||||
int? takeCount = null,
|
||||
CancellationToken cancellationToken = default);
|
||||
|
||||
/// <summary>
|
||||
/// Получает отфильтрованные Unit'ы с полной информацией о значениях полей и связях.
|
||||
@@ -45,7 +47,9 @@ namespace PARR.DAL.DomainServices.UnitFilterService
|
||||
/// <returns>Список UnitFilterResultDto</returns>
|
||||
Task<IEnumerable<UnitFilterResultDto>?> GetUnitsByJobFilterAsync(
|
||||
Job job,
|
||||
int? takeCount = null);
|
||||
int? takeCount = null,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
|
||||
/// <summary>
|
||||
/// Получает имена связанных Unit'ов для заданного Unit, отфильтрованных по RelationshipFilters Job.
|
||||
@@ -56,6 +60,8 @@ namespace PARR.DAL.DomainServices.UnitFilterService
|
||||
/// <returns>Список имён связанных Unit'ов (уникальные, без дублей)</returns>
|
||||
Task<List<string>> GetRelatedUnitNamesAsync(
|
||||
Guid jobId,
|
||||
Guid unitId);
|
||||
Guid unitId,
|
||||
CancellationToken cancellationToken = default
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,21 +49,33 @@ internal class UnitFilterService : IUnitFilterService
|
||||
}
|
||||
|
||||
|
||||
public async Task<IEnumerable<UnitFilterResultDto>?> GetUnitsByJobFilterAsync(Guid jobId, int? takeCount = null)
|
||||
public async Task<IEnumerable<UnitFilterResultDto>?> 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<IEnumerable<Guid>?> GetUnitsIdByJobFilterAsync(Job job, int? takeCount = null)
|
||||
public async Task<IEnumerable<Guid>?> 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<IEnumerable<UnitFilterResultDto>?> GetUnitsByJobFilterAsync(Job job, int? takeCount = null)
|
||||
public async Task<IEnumerable<UnitFilterResultDto>?> 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
|
||||
/// <returns></returns>
|
||||
private async Task<List<Guid>> ApplyFieldFiltersOnDbAsync(
|
||||
List<Guid> unitIds,
|
||||
IEnumerable<JobFieldFilter> fieldFilters)
|
||||
IEnumerable<JobFieldFilter> 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
|
||||
/// <returns></returns>
|
||||
private async Task<List<FilteredUnitContext>> ApplyRelationshipFiltersOnDbAsync(
|
||||
List<Guid> unitIds,
|
||||
IEnumerable<JobRelationshipFilter> relationshipFilters
|
||||
IEnumerable<JobRelationshipFilter> 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
|
||||
/// </summary>
|
||||
/// <param name="contexts"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<List<UnitFilterResultDto>> LoadFinalResultAsync(List<FilteredUnitContext> contexts)
|
||||
private async Task<List<UnitFilterResultDto>> LoadFinalResultAsync(List<FilteredUnitContext> contexts, CancellationToken cancellationToken = default)
|
||||
{
|
||||
var allResults = new List<UnitFilterResultDto>();
|
||||
|
||||
@@ -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
|
||||
/// </summary>
|
||||
/// <param name="batch"></param>
|
||||
/// <returns></returns>
|
||||
private async Task<List<UnitFilterResultDto>> LoadBatchAsync(List<FilteredUnitContext> batch)
|
||||
private async Task<List<UnitFilterResultDto>> LoadBatchAsync(List<FilteredUnitContext> 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<Guid, Unit> 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<List<string>> GetRelatedUnitNamesAsync(Guid jobId, Guid unitId)
|
||||
public async Task<List<string>> 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<List<Guid>> GetUnitIdsFromCacheOrDbAsync(JobUnitFilter filter)
|
||||
private async Task<List<Guid>> 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
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user