Enhance query performance by applying AsSplitQuery to multiple handlers and loaders for Channels and ScheduleTemplates, improving efficiency in data retrieval and reducing unnecessary data duplication in queries.
This commit is contained in:
+1
@@ -16,6 +16,7 @@ public sealed class AddBumperTextVariantCommandHandler(IAppDbContext dbContext)
|
||||
var channel = await dbContext
|
||||
.Channels.Include(c => c.BumperTemplates)
|
||||
.ThenInclude(t => t.Variants)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(c => c.Id == command.ChannelId, cancellationToken);
|
||||
if (channel is null)
|
||||
return Result.Failure<Guid>(ChannelErrors.NotFound);
|
||||
|
||||
@@ -42,6 +42,7 @@ public sealed class BumperSpecLoader(
|
||||
.Channels.AsNoTracking()
|
||||
.Include(c => c.BumperTemplates)
|
||||
.ThenInclude(t => t.Variants)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(c => c.Id == cache.ChannelId, cancellationToken);
|
||||
var template = channel?.BumperTemplates.FirstOrDefault(t => t.Id == cache.TemplateId);
|
||||
var variant = template?.Variants.FirstOrDefault(v => v.Id == cache.VariantId);
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ public sealed class RemoveBumperTextVariantCommandHandler(IAppDbContext dbContex
|
||||
var channel = await dbContext
|
||||
.Channels.Include(c => c.BumperTemplates)
|
||||
.ThenInclude(t => t.Variants)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(c => c.Id == command.ChannelId, cancellationToken);
|
||||
if (channel is null)
|
||||
return Result.Failure(ChannelErrors.NotFound);
|
||||
|
||||
+1
@@ -33,6 +33,7 @@ public sealed class RenderBumperPreviewCommandHandler(
|
||||
.Channels.AsNoTracking()
|
||||
.Include(c => c.BumperTemplates)
|
||||
.ThenInclude(t => t.Variants)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(c => c.Id == query.ChannelId, cancellationToken);
|
||||
if (channel is null)
|
||||
return Result.Failure(ChannelErrors.NotFound);
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ public sealed class UpdateBumperTextVariantCommandHandler(IAppDbContext dbContex
|
||||
var channel = await dbContext
|
||||
.Channels.Include(c => c.BumperTemplates)
|
||||
.ThenInclude(t => t.Variants)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(c => c.Id == command.ChannelId, cancellationToken);
|
||||
if (channel is null)
|
||||
return Result.Failure(ChannelErrors.NotFound);
|
||||
|
||||
@@ -19,6 +19,7 @@ public sealed class GetChannelQueryHandler(IAppDbContext dbContext)
|
||||
.Channels.AsNoTracking()
|
||||
.Include(c => c.BumperTemplates)
|
||||
.ThenInclude(t => t.Variants)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(c => c.Id == query.Id, cancellationToken);
|
||||
if (channel is null)
|
||||
return Result.Failure<ChannelDto>(ChannelErrors.NotFound);
|
||||
|
||||
@@ -18,6 +18,8 @@ public sealed class GetShowQueryHandler(IAppDbContext dbContext)
|
||||
.Shows.AsNoTracking()
|
||||
.Include(s => s.Episodes)
|
||||
.Include(s => s.Genres)
|
||||
// Сиблинги: серии × жанры перемножились бы в одном запросе (см. ListShowsQueryHandler).
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(s => s.Id == query.Id, cancellationToken);
|
||||
if (show is null)
|
||||
return Result.Failure<ShowDto>(ShowErrors.NotFound);
|
||||
|
||||
@@ -17,6 +17,9 @@ public sealed class ListShowsQueryHandler(IAppDbContext dbContext)
|
||||
.Shows.AsNoTracking()
|
||||
.Include(s => s.Episodes)
|
||||
.Include(s => s.Genres)
|
||||
// Серии и жанры — сиблинги: одним запросом это честное перемножение (сериал на 200 серий
|
||||
// с тремя жанрами даёт 600 строк вместо 203). Разделяем.
|
||||
.AsSplitQuery()
|
||||
.Where(s =>
|
||||
query.Interstitials
|
||||
? s.Kind == ShowKind.Interstitial
|
||||
|
||||
@@ -60,6 +60,10 @@ public sealed class GridScheduleGenerator(
|
||||
var channel = await dbContext
|
||||
.Channels.Include(c => c.BumperTemplates)
|
||||
.ThenInclude(t => t.Variants)
|
||||
// Вложенные коллекции тянем отдельными запросами: иначе колонки родителя (у шаблона —
|
||||
// jsonb с правилами, у слоя — jsonb применимости) приезжают по копии на каждую строку
|
||||
// листа. Тик планировщика повторяет эти два запроса на каждый канал.
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(c => c.Id == channelId, cancellationToken);
|
||||
if (channel is null || !channel.IsEnabled || channel.TemplateId is null)
|
||||
return new GenerationReport(0, [], ChannelSkipped: true);
|
||||
@@ -70,6 +74,7 @@ public sealed class GridScheduleGenerator(
|
||||
var template = await dbContext
|
||||
.ScheduleTemplates.Include(t => t.Layers)
|
||||
.ThenInclude(l => l.Slots)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(t => t.Id == channel.TemplateId, cancellationToken);
|
||||
if (template is null)
|
||||
return new GenerationReport(0, [], ChannelSkipped: true);
|
||||
@@ -174,6 +179,7 @@ public sealed class GridScheduleGenerator(
|
||||
.Channels.AsNoTracking()
|
||||
.Include(c => c.BumperTemplates)
|
||||
.ThenInclude(t => t.Variants)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(c => c.Id == channelId, cancellationToken);
|
||||
if (channel is null || channel.TemplateId is null)
|
||||
return null;
|
||||
@@ -182,6 +188,7 @@ public sealed class GridScheduleGenerator(
|
||||
.ScheduleTemplates.AsNoTracking()
|
||||
.Include(t => t.Layers)
|
||||
.ThenInclude(l => l.Slots)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(t => t.Id == channel.TemplateId, cancellationToken);
|
||||
if (template is null)
|
||||
return null;
|
||||
|
||||
+1
@@ -25,6 +25,7 @@ public sealed class CopyTemplateCommandHandler(IAppDbContext dbContext)
|
||||
.ScheduleTemplates.AsNoTracking()
|
||||
.Include(t => t.Layers)
|
||||
.ThenInclude(l => l.Slots)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(t => t.ChannelId == command.SourceChannelId, cancellationToken);
|
||||
if (source is null)
|
||||
return Result.Failure<CopyTemplateResultDto>(ChannelErrors.TemplateNotFound);
|
||||
|
||||
+1
@@ -24,6 +24,7 @@ public sealed class GetChannelTemplateQueryHandler(IAppDbContext dbContext)
|
||||
.ScheduleTemplates.AsNoTracking()
|
||||
.Include(t => t.Layers)
|
||||
.ThenInclude(l => l.Slots)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(t => t.ChannelId == channel.Id, cancellationToken);
|
||||
if (template is null)
|
||||
return Result.Failure<ScheduleTemplateDto>(ChannelErrors.TemplateNotFound);
|
||||
|
||||
@@ -109,6 +109,7 @@ internal static class LayerLoader
|
||||
dbContext
|
||||
.ScheduleTemplates.Include(t => t.Layers)
|
||||
.ThenInclude(l => l.Slots)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(t => t.Id == templateId, cancellationToken);
|
||||
|
||||
public static Task<ScheduleTemplate?> ByLayerAsync(
|
||||
@@ -119,5 +120,6 @@ internal static class LayerLoader
|
||||
dbContext
|
||||
.ScheduleTemplates.Include(t => t.Layers)
|
||||
.ThenInclude(l => l.Slots)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(t => t.Layers.Any(l => l.Id == layerId), cancellationToken);
|
||||
}
|
||||
|
||||
@@ -89,6 +89,7 @@ public sealed class SlotWriter(IAppDbContext dbContext)
|
||||
dbContext
|
||||
.ScheduleTemplates.Include(t => t.Layers)
|
||||
.ThenInclude(l => l.Slots)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(t => t.Layers.Any(l => l.Id == layerId), cancellationToken);
|
||||
|
||||
/// <summary>Загружает шаблон по слоту.</summary>
|
||||
@@ -99,6 +100,7 @@ public sealed class SlotWriter(IAppDbContext dbContext)
|
||||
dbContext
|
||||
.ScheduleTemplates.Include(t => t.Layers)
|
||||
.ThenInclude(l => l.Slots)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(
|
||||
t => t.Layers.Any(l => l.Slots.Any(s => s.Id == slotId)),
|
||||
cancellationToken
|
||||
|
||||
+1
@@ -28,6 +28,7 @@ public sealed class ValidateTemplateQueryHandler(IAppDbContext dbContext)
|
||||
.ScheduleTemplates.AsNoTracking()
|
||||
.Include(t => t.Layers)
|
||||
.ThenInclude(l => l.Slots)
|
||||
.AsSplitQuery()
|
||||
.FirstOrDefaultAsync(t => t.ChannelId == channel.Id, cancellationToken);
|
||||
if (template is null)
|
||||
return Result.Failure<IReadOnlyList<TemplateIssueDto>>(ChannelErrors.TemplateNotFound);
|
||||
|
||||
Reference in New Issue
Block a user