PostgreSQL · Execute
Call a stored procedure
Calling a stored procedure, in three scenarios: named arguments; positional arguments; a parameter left out, so the procedure's default is used. The call is described with ArxProcedureExpression, the way a query is described with ArxQueryExpression; ARX sends CALL with every value as a parameter.
Set up for this page
The scenarios build on the sample application: SampleContext.cs (the dbContext every sample is given); SampleOutput.cs (Step and Show, which print); SampleContext.Application.cs (dbContext.Service, the application's ARX service); SampleCatalog.cs (the tables' ids, keys, lookups and display names).
Before scenario 1 the sample runs this, so the service and the rows the scenarios use are there:
var service = new ArxPgDbService(connectionString);
var account = new ArxEntity("account");
account["name"] = "Old Name Ltd";
account["account_number"] = "ACC-E101";
var accountId = await service.CreateAsync(account);1 Named arguments
sp_rename_account(p_account_id, p_name, p_reason) with every argument named. Nothing comes back.
Already in scope: service (ArxPgDbService), accountId (ArxRecordId)
var procedure = new ArxProcedureExpression("sp_rename_account");
procedure.AddParameter("p_account_id", accountId.AsInt64());
procedure.AddParameter("p_name", "New Name Ltd");
procedure.AddParameter("p_reason", "rebranding");
var result = await service.ExecuteAsync(procedure);
Console.WriteLine($"renamed; outputs: {result.Outputs.Attributes.Count()}, result sets: {result.ResultSets.Count}");Check it in the database
Run it in any SQL tool connected to the database, after the code has run.
SELECT name
FROM account
WHERE account_number = 'ACC-E101';Expect: New Name Ltd (until scenario 2 renames it again).
2 Positional arguments
The same procedure with positional arguments, in the procedure's order.
Already in scope: service (ArxPgDbService), accountId (ArxRecordId)
var procedure = new ArxProcedureExpression("sp_rename_account");
procedure.AddParameter(accountId.AsInt64());
procedure.AddParameter("Positional Ltd");
procedure.AddParameter("a positional call");
await service.ExecuteAsync(procedure);
Console.WriteLine("renamed to Positional Ltd");Check it in the database
Run it in any SQL tool connected to the database, after the code has run.
SELECT name
FROM account
WHERE account_number = 'ACC-E101';Expect: Positional Ltd (until scenario 3).
3 A parameter left out: the default is used
p_reason left out: the procedure's default ('not given') is used.
Already in scope: service (ArxPgDbService), accountId (ArxRecordId)
var procedure = new ArxProcedureExpression("sp_rename_account");
procedure.AddParameter("p_account_id", accountId.AsInt64());
procedure.AddParameter("p_name", "Final Name Ltd");
await service.ExecuteAsync(procedure);
Console.WriteLine("renamed to Final Name Ltd, without a reason");Check it in the database
Run it in any SQL tool connected to the database, after the code has run.
SELECT name, modified_on IS NOT NULL AS modified
FROM account
WHERE account_number = 'ACC-E101';Expect: Final Name Ltd, t.
Run the complete sample
The code above comes from one program, built in a fresh project and run against the database. To run it yourself, here is every file and step.
The program, its files and how to run it
Run it locally
- .NET SDK: .NET SDK 10.0.100 or later 10.0.x (target
net10.0). - Create the project:
Terminal dotnet new console -n ArxSample -f net10.0 cd ArxSample - Install the packages:
Terminal dotnet add package ARX.SDK.PostgreSql --version 1.0.0-rc.1 - Database: a PostgreSQL 16 (tested with 16.15) database with the sample schema.
- Configuration: set
ARX_SAMPLES_POSTGRESQLto its connection string. Nothing is hard-coded. - Source code: add the files of the sample application this sample uses (
SampleContext.cs,SampleOutput.cs,SampleContext.Application.cs,SampleCatalog.cs), then replaceProgram.csand add the other file below, with the file names shown. - Run it:
Terminal dotnet run - Expected output: below.
The files
Runs the sample. Generated for this page; it reads the connection string from the environment.
using ArxSamples.PostgreSql;
using ArxSamples.PostgreSql.Operations.Execute.Basics;
// The connection string comes from the environment, never from the code (see "Configure the connection").
var connectionString = Environment.GetEnvironmentVariable("ARX_SAMPLES_POSTGRESQL")
?? throw new InvalidOperationException("Set the ARX_SAMPLES_POSTGRESQL environment variable to your PostgreSQL connection string.");
await new Basic1_CallAProcedure().RunAsync(new SampleContext(connectionString));
The sample, as it is in the repository.
using ARX.SDK.PostgreSql.Services;
using ARX.SDK.Query;
using ARX.SDK.Services;
using static ArxSamples.PostgreSql.SampleOutput;
namespace ArxSamples.PostgreSql.Operations.Execute.Basics;
/// <summary>
/// Calling a stored procedure, in three scenarios: named arguments; positional arguments; a parameter left out, so the
/// procedure's default is used. The call is described with <see cref="ArxProcedureExpression"/>, the way a query
/// is described with <see cref="ArxQueryExpression"/>; ARX sends <c>CALL</c> with every value as a parameter.
/// </summary>
public sealed class Basic1_CallAProcedure : ISample
{
public string Name => "execute-basics-1-call-a-procedure";
public string Title => "Call a stored procedure";
public async Task RunAsync(SampleContext dbContext)
{
var service = new ArxPgDbService(dbContext.ConnectionString);
var account = new ArxEntity("account");
account["name"] = "Old Name Ltd";
account["account_number"] = "ACC-E101";
var accountId = await dbContext.Service.CreateAsync(account);
await Scenario1_NamedArgumentsAsync(service, accountId);
await Scenario2_PositionalArgumentsAsync(service, accountId);
await Scenario3_ADefaultAsync(service, accountId);
}
/// <summary>1. <c>sp_rename_account(p_account_id, p_name, p_reason)</c> with every argument named. Nothing comes back.</summary>
private static async Task Scenario1_NamedArgumentsAsync(ArxPgDbService service, ArxRecordId accountId)
{
Step("1. Named arguments");
var procedure = new ArxProcedureExpression("sp_rename_account");
procedure.AddParameter("p_account_id", accountId.AsInt64());
procedure.AddParameter("p_name", "New Name Ltd");
procedure.AddParameter("p_reason", "rebranding");
var result = await service.ExecuteAsync(procedure);
Show($"renamed; outputs: {result.Outputs.Attributes.Count()}, result sets: {result.ResultSets.Count}");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT name
* FROM account
* WHERE account_number = 'ACC-E101';
* -- Expect: New Name Ltd (until scenario 2 renames it again).
* ===========================================================
*/
}
/// <summary>2. The same procedure with positional arguments, in the procedure's order.</summary>
private static async Task Scenario2_PositionalArgumentsAsync(ArxPgDbService service, ArxRecordId accountId)
{
Step("2. Positional arguments");
var procedure = new ArxProcedureExpression("sp_rename_account");
procedure.AddParameter(accountId.AsInt64());
procedure.AddParameter("Positional Ltd");
procedure.AddParameter("a positional call");
await service.ExecuteAsync(procedure);
Show("renamed to Positional Ltd");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT name
* FROM account
* WHERE account_number = 'ACC-E101';
* -- Expect: Positional Ltd (until scenario 3).
* ===========================================================
*/
}
/// <summary>3. <c>p_reason</c> left out: the procedure's default (<c>'not given'</c>) is used.</summary>
private static async Task Scenario3_ADefaultAsync(ArxPgDbService service, ArxRecordId accountId)
{
Step("3. A parameter left out: the default is used");
var procedure = new ArxProcedureExpression("sp_rename_account");
procedure.AddParameter("p_account_id", accountId.AsInt64());
procedure.AddParameter("p_name", "Final Name Ltd");
await service.ExecuteAsync(procedure);
Show("renamed to Final Name Ltd, without a reason");
/* ===========================================================
* [DIRECT QUERY CHECK]
* -----------------------------------------------------------
* SELECT name, modified_on IS NOT NULL AS modified
* FROM account
* WHERE account_number = 'ACC-E101';
* -- Expect: Final Name Ltd, t.
* ===========================================================
*/
}
}
Expected output
What dotnet run printed when the sample was verified, on a freshly created sample schema. Ids differ when the tables already hold rows.
> 1. Named arguments
renamed; outputs: 0, result sets: 0
> 2. Positional arguments
renamed to Positional Ltd
> 3. A parameter left out: the default is used
renamed to Final Name Ltd, without a reason