Pular para o conteúdo principal

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. A Application apenas 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

  • Domain não executa procedures; mantém-se livre de DbConnection.
  • 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 na Application.