Refactor stored procedure SQL construction and execution

Centralize stored procedure SQL generation in StoredProcedureBuilder,
allowing handlers to specify procedure name and return variable.
Removes manual SQL string building from DeleteObjectProcedure and
UpdateObjectProcedure handlers, reducing boilerplate and improving
maintainability.
This commit is contained in:
2026-03-30 09:30:07 +02:00
parent b66a49f74d
commit 93b5f976d3
3 changed files with 21 additions and 28 deletions

View File

@@ -5,7 +5,6 @@ using Microsoft.Data.SqlClient;
using Microsoft.Extensions.Options; using Microsoft.Extensions.Options;
using ReC.Application.Common.Exceptions; using ReC.Application.Common.Exceptions;
using ReC.Application.Common.Options; using ReC.Application.Common.Options;
using System.Text;
namespace ReC.Application.Common.Procedures.DeleteProcedure; namespace ReC.Application.Common.Procedures.DeleteProcedure;
@@ -36,21 +35,15 @@ public class DeleteObjectProcedureHandler(IRepository repo, IOptionsMonitor<SqlE
{ {
public async Task<int> Handle(DeleteObjectProcedure request, CancellationToken cancel) public async Task<int> Handle(DeleteObjectProcedure request, CancellationToken cancel)
{ {
var sp = new StoredProcedureBuilder("EXEC @RC = [dbo].[PRREC_DELETE_OBJECT]") var sp = new StoredProcedureBuilder("[dbo].[PRREC_DELETE_OBJECT]", "RC")
.Add("pENTITY", request.Entity) .Add("pENTITY", request.Entity)
.Add("pSTART", request.Start.ToString()) .Add("pSTART", request.Start.ToString())
.Add("pEND", request.End.ToString()) .Add("pEND", request.End.ToString())
.Add("pFORCE", request.Force); .Add("pFORCE", request.Force);
var sql = new StringBuilder()
.AppendLine("DECLARE @RC SMALLINT = 0;")
.Append(sp.BuildSql()).AppendLine(";")
.AppendLine("SELECT @RC;")
.ToString();
try try
{ {
var result = await repo.ExecuteQueryRawAsync(sql, sp.BuildParameters(), cancel); var result = await repo.ExecuteQueryRawAsync(sp.BuildSql(), sp.BuildParameters(), cancel);
if (result > 0) if (result > 0)
{ {

View File

@@ -4,22 +4,19 @@ using System.Text;
namespace ReC.Application.Common.Procedures; namespace ReC.Application.Common.Procedures;
internal sealed class StoredProcedureBuilder internal sealed class StoredProcedureBuilder(string procedureName, string? returnVariable = null)
{ {
private readonly StringBuilder _sql; private readonly StringBuilder _execSql = returnVariable is not null
? new StringBuilder($"EXEC @{returnVariable} = {procedureName}")
: new StringBuilder($"EXEC {procedureName}");
private readonly List<SqlParameter> _parameters = []; private readonly List<SqlParameter> _parameters = [];
private char _separator = ' '; private char _separator = ' ';
public StoredProcedureBuilder(string execPrefix)
{
_sql = new StringBuilder(execPrefix);
}
public StoredProcedureBuilder Add(string name, object? value, SqlDbType? dbType = null) public StoredProcedureBuilder Add(string name, object? value, SqlDbType? dbType = null)
{ {
if (value is null) return this; if (value is null) return this;
_sql.AppendLine($"{_separator}@{name} = @{name}"); _execSql.AppendLine($"{_separator}@{name} = @{name}");
_separator = ','; _separator = ',';
if (dbType.HasValue) if (dbType.HasValue)
@@ -32,7 +29,7 @@ internal sealed class StoredProcedureBuilder
public StoredProcedureBuilder AddOutput(string name, SqlDbType dbType) public StoredProcedureBuilder AddOutput(string name, SqlDbType dbType)
{ {
_sql.AppendLine($"{_separator}@{name} = @{name} OUTPUT"); _execSql.AppendLine($"{_separator}@{name} = @{name} OUTPUT");
_separator = ','; _separator = ',';
_parameters.Add(new SqlParameter _parameters.Add(new SqlParameter
@@ -45,7 +42,17 @@ internal sealed class StoredProcedureBuilder
return this; return this;
} }
public string BuildSql() => _sql.ToString(); public string BuildSql()
{
if (returnVariable is null)
return _execSql.ToString();
return new StringBuilder()
.AppendLine($"DECLARE @{returnVariable} SMALLINT = 0;")
.Append(_execSql).AppendLine(";")
.AppendLine($"SELECT @{returnVariable};")
.ToString();
}
public SqlParameter[] BuildParameters() => [.. _parameters]; public SqlParameter[] BuildParameters() => [.. _parameters];

View File

@@ -7,7 +7,6 @@ using ReC.Application.Common.Exceptions;
using ReC.Application.Common.Options; using ReC.Application.Common.Options;
using ReC.Application.Common.Procedures.UpdateProcedure.Dto; using ReC.Application.Common.Procedures.UpdateProcedure.Dto;
using System.Data; using System.Data;
using System.Text;
namespace ReC.Application.Common.Procedures.UpdateProcedure; namespace ReC.Application.Common.Procedures.UpdateProcedure;
@@ -38,7 +37,7 @@ public class UpdateObjectProcedureHandler(IRepository repo, IOptionsMonitor<SqlE
{ {
public async Task<int> Handle(UpdateObjectProcedure request, CancellationToken cancel) public async Task<int> Handle(UpdateObjectProcedure request, CancellationToken cancel)
{ {
var sp = new StoredProcedureBuilder("EXEC @RC = [dbo].[PRREC_UPDATE_OBJECT]") var sp = new StoredProcedureBuilder("[dbo].[PRREC_UPDATE_OBJECT]", "RC")
.Add("pENTITY", request.Entity) .Add("pENTITY", request.Entity)
.Add("pGUID", request.Id) .Add("pGUID", request.Id)
.Add("pCHANGED_WHO", request.ChangedWho) .Add("pCHANGED_WHO", request.ChangedWho)
@@ -91,15 +90,9 @@ public class UpdateObjectProcedureHandler(IRepository repo, IOptionsMonitor<SqlE
.Add("pRESULT_HEADER", request.Result.Header) .Add("pRESULT_HEADER", request.Result.Header)
.Add("pRESULT_BODY", request.Result.Body); .Add("pRESULT_BODY", request.Result.Body);
var sql = new StringBuilder()
.AppendLine("DECLARE @RC SMALLINT = 0;")
.Append(sp.BuildSql()).AppendLine(";")
.AppendLine("SELECT @RC;")
.ToString();
try try
{ {
var result = await repo.ExecuteQueryRawAsync(sql, sp.BuildParameters(), cancel); var result = await repo.ExecuteQueryRawAsync(sp.BuildSql(), sp.BuildParameters(), cancel);
if (result > 0) if (result > 0)
{ {