Execução de Procedures
Esta página padroniza a chamada de procedures via uma abstração única, garantindo baixo acoplamento, testabilidade e observabilidade na Application durante a migração de legado.
Regra
- Procedures devem ser executadas via a abstração
IDbProcedure<TResult>. - Implementações e execução pertencem à
Infra. AApplicationapenas orquestra a chamada. - Logging permanece em
Application.
Abstração (contrato)
using System.Data.Common;
/// <summary>
/// Represents a database procedure that can be executed with a specified result type.
/// </summary>
/// <typeparam name="TResult">The type of the result returned by the procedure.</typeparam>
public interface IDbProcedure<TResult>
{
/// <summary>
/// Gets the name of the stored procedure that the implementing class represents.
/// This property is used to specify the procedure to be executed
/// when interacting with the database.
/// </summary>
string ProcedureName { get; }
/// <summary>
/// Executes the database procedure asynchronously and optionally processes
/// rows read from the data reader using the provided callback function.
/// </summary>
/// <param name="connection">
/// The database connection used to execute the procedure.
/// </param>
/// <param name="onRowRead">
/// An optional callback action to process each row read from the data reader.
/// If null, rows are not processed.
/// </param>
/// <returns>
/// A <c>ValidationResult</c> object containing the validation results after the procedure execution.
/// </returns>
Task<TResult> ExecuteAsync(DbConnection conn, Action<DbDataReader, TResult>? onRowRead = null, DbTransaction? transaction = null);
}
Exemplo de implementação
using System.Data.Common;
using System.ComponentModel.DataAnnotations.Schema;
public class StpWmsOperProcessaPedidoAcerto : IDbProcedure<ValidationResult>
{
public string ProcedureName => "stp_wmsoper_processa_pedido_acerto";
[ProcedureParam("cod_pedido")] public int? CodPedido { get; set; }
[ProcedureParam("cod_operacao")] public int? CodOperacao { get; set; }
[ProcedureParam("cod_operacao_logistica")]
public int? CodOperacaoLogistica { get; set; }
public StpWmsOperProcessaPedidoAcerto() { }
public StpWmsOperProcessaPedidoAcerto(int? codPedido, int? codOperacao)
{
CodPedido = codPedido;
CodOperacao = codOperacao;
CodOperacaoLogistica = null;
}
public async Task<ValidationResult> ExecuteAsync(DbConnection conn,
Action<DbDataReader, ValidationResult>? onRowRead = null, DbTransaction? transaction = null)
{
return await ProcedureExecutor.ExecuteWithValidationAsync(
conn,
this,
message: "Retorno validação Processa Pedido Acerto: ",
key: (CodPedido ?? 0).ToString(),
onRowRead,
transaction);
}
}
Uso na Application (orquestração)
public sealed class ProcessaPedidoAcertoHandler
{
private readonly IDbConnectionFactory connectionFactory; // definido em Infra
private readonly ILogger<ProcessaPedidoAcertoHandler> logger;
public ProcessaPedidoAcertoHandler(IDbConnectionFactory connectionFactory, ILogger<ProcessaPedidoAcertoHandler> logger)
{
this.connectionFactory = connectionFactory;
this.logger = logger;
}
public async Task<ValidationResult> HandleAsync(int pedidoId, int operacaoId, CancellationToken ct)
{
logger.LogInformation("Iniciando procedure de acerto para {PedidoId}", pedidoId);
await using var conn = await connectionFactory.OpenAsync(ct);
var proc = new StpWmsOperProcessaPedidoAcerto(pedidoId, operacaoId);
var result = await proc.ExecuteAsync(conn,
onRowRead: (reader, acc) => { /* opcional: tratar linhas */ },
transaction: null);
logger.LogInformation("Procedure finalizada para {PedidoId}", pedidoId);
return result;
}
}
Boas práticas
Domainnão executa procedures; mantém-se livre deDbConnection.- Parâmetros via
[ProcedureParam]garantem acoplamento baixo e clareza. - Prefira transações fornecidas pela unidade de trabalho/connection factory da
Infra. - Validações de retorno (ex.:
ValidationResult) devem ser padronizadas para consistência naApplication.