Refactor query behaviors to process action collections

Refactored BodyQueryBehavior and HeaderQueryBehavior to operate on collections of RecActionViewDto instead of single instances. Moved SQL execution and property assignment logic into private helper methods using ADO.NET commands. Improved null checks and logging, and updated type constraints to reflect the new usage. Behaviors now return the modified collection after processing.
This commit is contained in:
2026-04-17 00:15:52 +02:00
parent 6681e56afc
commit 4bde1d090f
2 changed files with 68 additions and 33 deletions

View File

@@ -1,23 +1,41 @@
using MediatR; using MediatR;
using Microsoft.EntityFrameworkCore;
using ReC.Application.Common.Dto; using ReC.Application.Common.Dto;
using ReC.Application.Common.Interfaces; using ReC.Application.Common.Interfaces;
using Microsoft.EntityFrameworkCore;
namespace ReC.Application.Common.Behaviors.Action; namespace ReC.Application.Common.Behaviors.Action;
public class BodyQueryBehavior<TRequest, TResponse>(IRecDbContext dbContext) : IPipelineBehavior<TRequest, TResponse> public class BodyQueryBehavior<TRequest, TResponse>(IRecDbContext dbContext) : IPipelineBehavior<TRequest, TResponse>
where TRequest : RecActionViewDto where TRequest : notnull
where TResponse : notnull where TResponse : IEnumerable<RecActionViewDto>
{ {
public async Task<TResponse> Handle(TRequest action, RequestHandlerDelegate<TResponse> next, CancellationToken cancel) public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancel)
{ {
if (action.BodyQuery is null) var actions = await next(cancel);
return await next(cancel);
var result = await dbContext.BodyQueryResults.FromSqlRaw(action.BodyQuery).SingleOrDefaultAsync(cancel); foreach (var action in actions)
await SetBody(action, cancel);
action.Body = result?.RawBody; return actions;
}
return await next(cancel); private async Task SetBody(RecActionViewDto action, CancellationToken cancel)
{
if (action.BodyQuery is not string bodyQuery)
return;
await using var command = dbContext.Database.GetDbConnection().CreateCommand();
command.CommandText = bodyQuery;
await dbContext.Database.OpenConnectionAsync(cancel);
try
{
var scalar = await command.ExecuteScalarAsync(cancel);
action.Body = scalar as string;
}
finally
{
await dbContext.Database.CloseConnectionAsync();
}
} }
} }

View File

@@ -1,4 +1,4 @@
using MediatR; using MediatR;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
using ReC.Application.Common.Dto; using ReC.Application.Common.Dto;
@@ -8,36 +8,53 @@ using System.Text.Json;
namespace ReC.Application.Common.Behaviors.Action; namespace ReC.Application.Common.Behaviors.Action;
public class HeaderQueryBehavior<TRequest, TResponse>(IRecDbContext dbContext, ILogger<HeaderQueryBehavior<TRequest, TResponse>>? logger = null) : IPipelineBehavior<TRequest, TResponse> public class HeaderQueryBehavior<TRequest, TResponse>(IRecDbContext dbContext, ILogger<HeaderQueryBehavior<TRequest, TResponse>>? logger = null) : IPipelineBehavior<TRequest, TResponse>
where TRequest : RecActionViewDto where TRequest : notnull
where TResponse : notnull where TResponse : IEnumerable<RecActionViewDto>
{ {
public async Task<TResponse> Handle(TRequest action, RequestHandlerDelegate<TResponse> next, CancellationToken cancel) public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancel)
{ {
if (action.HeaderQuery is null) var actions = await next(cancel);
return await next(cancel);
var result = await dbContext.HeaderQueryResults.FromSqlRaw(action.HeaderQuery).SingleOrDefaultAsync(cancel); foreach (var action in actions)
await SetHeader(action, cancel);
if (result?.RawHeader is null) return actions;
}
private async Task SetHeader(RecActionViewDto action, CancellationToken cancel)
{
if (action.HeaderQuery is not string headerQuery)
return;
await using var command = dbContext.Database.GetDbConnection().CreateCommand();
command.CommandText = headerQuery;
await dbContext.Database.OpenConnectionAsync(cancel);
try
{ {
logger?.LogWarning("Header query did not return a result or returned a null REQUEST_HEADER. Profile ID: {ProfileId}, Action ID: {Id}", action.ProfileId, action.Id); var scalar = await command.ExecuteScalarAsync(cancel);
return await next(cancel); if (scalar is not string rawHeader)
{
logger?.LogWarning("Header query did not return a result or returned a null value. Profile ID: {ProfileId}, Action ID: {Id}", action.ProfileId, action.Id);
return;
}
var headerDict = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(rawHeader);
if (headerDict is null)
{
logger?.LogWarning(
"Header JSON deserialization returned null. RawHeader: {RawHeader}, ProfileId: {ProfileId}, Id: {Id}",
rawHeader, action.ProfileId, action.Id);
return;
}
action.Headers = headerDict.ToDictionary(header => header.Key, kvp => kvp.Value.ToString());
} }
finally
var headerDict = JsonSerializer.Deserialize<Dictionary<string, JsonElement>>(result.RawHeader);
if(headerDict is null)
{ {
logger?.LogWarning( await dbContext.Database.CloseConnectionAsync();
"Header JSON deserialization returned null. RawHeader: {RawHeader}, ProfileId: {ProfileId}, Id: {Id}",
result.RawHeader, action.ProfileId, action.Id);
return await next(cancel);
} }
action.Headers = headerDict.ToDictionary(header => header.Key, kvp => kvp.Value.ToString());
return await next(cancel);
} }
} }