How to call PostgreSQL 13 stored procedure (no function) with cursor INOUT parameter from Npgsql 4.1.5.0 in C#

2.8k views Asked by At

I have this Stored Procedure with IN char parameter and INOUT cursor parameter:

CREATE OR REPLACE PROCEDURE SP_ObtenerFacturaPorNombreCliente(IN p_nombreCliente CHAR, INOUT p_cursorFacturas REFCURSOR)
LANGUAGE PLPGSQL 
AS 
$$
BEGIN
    OPEN p_cursorFacturas FOR
    SELECT "CodigoFactura", "NombreCliente", "DireccionCliente", "TelefonoCliente", "Fecha", "SubTotal", "Iva", "ValorIva", "Total", "Geografico", "Geometrico" FROM "Factura"
    WHERE "NombreCliente" = p_nombreCliente
    ORDER BY "CodigoFactura";
END
$$

The stored procedure calling with Npgsql 4.1.5.0 in C#:

NpgsqlConnection npgsqlConnection = new NpgsqlConnection("Server=127.0.0.1;Port=5432;Database=mybase;User Id=user;Password=password;");
npgsqlConnection.Open();
npgsqlConnection.TypeMapper.UseNetTopologySuite();
string sentencialSQL = "CALL SP_ObtenerFacturaPorNombreCliente(:p_nombreCliente, :p_cursorFacturas);";
NpgsqlCommand npgsqlCommand = new NpgsqlCommand(sentencialSQL, npgsqlConnection);
// ===============================
NpgsqlParameter npgsqlParameter1 = new NpgsqlParameter();
npgsqlParameter1.ParameterName = ":p_nombreCliente";
npgsqlParameter1.Value = "Perico de los palotes";
npgsqlParameter1.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Text;
npgsqlParameter1.Direction = ParameterDirection.Input;
npgsqlCommand.Parameters.Add(npgsqlParameter1);
// -------------------
NpgsqlParameter npgsqlParameter2 = new NpgsqlParameter();
npgsqlParameter2.ParameterName = ":p_cursorFacturas";
npgsqlParameter2.Value = string.Empty;
npgsqlParameter2.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Refcursor;
npgsqlParameter2.Direction = ParameterDirection.InputOutput;
npgsqlCommand.Parameters.Add(npgsqlParameter2);
// ===============================
npgsqlCommand.CommandType = CommandType.Text; // CommandType.StoredProcedure is with Function
NpgsqlDataReader npgsqlDataReader = npgsqlCommand.ExecuteReader();

With: npgsqlParameter2.Value = string.Empty; I have this error:

42601: syntax error at or near <<:>>

With: npgsqlParameter2.Value = null; I have this error:

Parameter :p_cursorFacturas must be set

UPDATE:

With @madreflection suggestion, I changed null by DBNull.Value but the calling changed npgsqlParameter2 with "<unnamed portal 1>"

NpgsqlConnection npgsqlConnection = new NpgsqlConnection("Server=127.0.0.1;Port=5432;Database=mybase;User Id=user;Password=password;");
npgsqlConnection.Open();
npgsqlConnection.TypeMapper.UseNetTopologySuite();
string sentencialSQL = "CALL SP_ObtenerFacturaPorNombreCliente(:p_nombreCliente, :p_cursorFacturas);";
NpgsqlCommand npgsqlCommand = new NpgsqlCommand(sentencialSQL, npgsqlConnection);
// ===============================
NpgsqlParameter npgsqlParameter1 = new NpgsqlParameter();
npgsqlParameter1.ParameterName = ":p_nombreCliente";
npgsqlParameter1.Value = "Perico de los palotes";
npgsqlParameter1.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Text;
npgsqlParameter1.Direction = ParameterDirection.Input;
npgsqlCommand.Parameters.Add(npgsqlParameter1);
// -------------------
NpgsqlParameter npgsqlParameter2 = new NpgsqlParameter();
npgsqlParameter2.ParameterName = ":p_cursorFacturas";
npgsqlParameter2.Value = DBNull.Value;
npgsqlParameter2.NpgsqlDbType = NpgsqlTypes.NpgsqlDbType.Refcursor;
npgsqlParameter2.Direction = ParameterDirection.InputOutput;
npgsqlCommand.Parameters.Add(npgsqlParameter2);
// ===============================
npgsqlCommand.CommandType = CommandType.Text; // CommandType.StoredProcedure is with Function
NpgsqlDataReader npgsqlDataReader = npgsqlCommand.ExecuteReader();
1

There are 1 answers

0
Luis Lavieri On

Ok I figured it out. This post helped me a lot. Here's the answer for others.

//transaction is needed to keep unnamed portal 1
var transaction = npgsqlConnection.BeginTransaction();

//create the command and add all the parameters, command type, etc 

...

npgsqlCommand.CommandType = CommandType.Text; 

npgsqlCommand.CommandText = "call your_procedure(:parameters)";

//execute the original procedure with ExecuteNonQuery
npgsqlCommand.ExecuteNonQuery();

//change the command text and execute the reader
npgsqlCommand.CommandText = "fetch all in \"<unnamed portal 1>\"";

var npgsqlDataReader = npgsqlCommand.ExecuteReader();

while (reader.Read())
{  
    // do whatever is needed to extract the data
}

//clean up
transaction.Commit();
npgsqlConnection.Close();