NTiProgramParameter
NTiProgramParameter describes one parameter of a program call or of a service program procedure call: a byte buffer sent to the server (InputData), a buffer returned by the program (OutputData), a direction and a CCSID. The class is shared by CallProgram/CallProgramAsync and CallServiceProgram/CallServiceProgramAsync on NTiConnection.
Constructors
All take an optional trailing ParameterDirection (default InputOutput).
| Constructor | IBM i type |
|---|---|
() |
EMPTY variable length receiver (see below) |
(string value, int length) |
CHAR(length), blank padded |
(string value, int length, int ccsid) |
CHAR(length) in the given CCSID |
(string[] values, int length) plus IEnumerable |
CHAR(length) array |
(int value) / (int[] values) / (IEnumerable<int> values) |
BINARY(4) / array |
(short value) / (short[] values) / (IEnumerable<short> values) |
BINARY(2) / array |
(decimal value, int precision, int scale, bool packed = true) |
packed decimal (packed: true, DECIMAL) or zoned (packed: false, NUMERIC) |
(byte[] value) |
raw bytes, sent as is |
Composing with Append
The Append(...) overloads extend the parameter's buffer with an additional field, to compose a data structure in a single buffer; each returns the parameter, so calls chain.
| Overload | Appended field |
|---|---|
Append(string, int) / Append(string, int, int ccsid) |
CHAR(length) |
Append(string[]/IEnumerable<string>, int) / same with ccsid |
CHAR(length) array |
Append(int) / Append(int[]/IEnumerable<int>) |
BINARY(4) / array |
Append(short) / Append(short[]/IEnumerable<short>) |
BINARY(2) / array |
Append(decimal, int precision, int scale, bool packed = true) |
packed or zoned decimal |
Append(byte[]) |
raw bytes |
Direction
Direction (default InputOutput) controls what travels; the fluent extensions .AsInput(), .AsOutput(), .AsInputOutput() set it and return the parameter.
| Direction | Semantics |
|---|---|
Input |
the bytes are sent, nothing comes back |
Output (and ReturnValue) |
only the length is declared, the buffer comes back in OutputData |
InputOutput (default) |
the bytes are sent AND the buffer comes back |
Add extensions
The parms.Add(...) family on IListparms.Add("ABC", 10).AsInput(). Overloads: Add() (empty receiver), Add(string, int[, int ccsid]), Add(string[]/IEnumerable<string>, int[, int ccsid]), Add(int), Add(int[]/IEnumerable<int>), Add(short), Add(short[]/IEnumerable<short>), Add(decimal, int precision, int scale) (packed; use the constructor for a zoned decimal), Add(byte[]); all with the optional trailing ParameterDirection.
Reading results
After the call, the accessors read the output buffer (OutputData). Integers are big endian, as on the IBM i.
| Accessor | Reads |
|---|---|
GetString() / GetString(int ccsid) |
the whole buffer as text, in the parameter CCSID or an explicit one |
GetString(int offset, int length) / GetString(int offset, int length, int ccsid) |
a segment as text |
GetInt() / GetInt(int offset) |
BINARY(4) |
GetShort() / GetShort(int offset) |
BINARY(2), such as the length prefix of a VARCHAR |
GetPackedDecimal(int precision, int scale[, int offset]) |
packed decimal |
GetZonedDecimal(int precision, int scale[, int offset]) |
zoned decimal |
GetBytes() / GetBytes(int offset, int length) |
raw bytes |
GetDTSTimestamp([int offset]) |
8 byte *DTS timestamp, returned as a DateTime |
InputData and OutputData are public byte[]: full manual control remains available.
CCSID
| Member | Purpose |
|---|---|
Ccsid |
parameter CCSID for text conversions; null = job CCSID of the call |
NTiProgramParameter.DefaultCcsid (static) |
process-wide default: 37 until a connection opens, then the job CCSID of the LAST opened connection (v4 compat) |
EffectiveCcsid |
effective CCSID: the explicit one, else the process-wide default |
With several connections on different CCSIDs, pass an explicit ccsid (constructor, Append or GetString). The !, [, ], ^ characters vary across EBCDIC code pages: make the ccsid explicit whenever they matter.
Empty receiver
new NTiProgramParameter() (no value) is a variable length receiver: it is declared to the server with the conventional length 0xFFFF without sending a single byte, and comes back sized to the data the program actually wrote (v4.4.14 parity). This is the pattern for VARCHAR outputs and reply structures: receiver.GetString(2, receiver.GetShort()) reads a VARCHAR (BINARY(2) length prefix, then the data).
ServiceProgramParameterFormat
Property used only by CallServiceProgram/CallServiceProgramAsync (ignored by CallProgram): ByReference (default) passes the address of the parameter storage; ByValue passes a BINARY(4) by value, the data must be exactly 4 bytes and the output data is then meaningless. Values in NTiServiceProgramParameterFormat.
The hex contract
Everywhere in NTi, the string representation of binary data is UPPERCASE hex without separators (v4 parity): this is what the data reader's GetString returns on a BINARY, VARBINARY, ROWID or FOR BIT DATA (CCSID 65535) column. On an NTiProgramParameter, binary data is read with GetBytes or OutputData; GetString requires a real text CCSID: asking for 65535 ("no conversion") throws an explicit error.
Complete example
Async is the normal path; CallProgram also exists synchronously.
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using Aumerial.Data.Nti;
class ProgramParameterDemo
{
static async Task Main()
{
await using var connection = new NTiConnection("server=MYIBMI;user=MYUSER;password=MYPASSWORD");
await connection.OpenAsync();
// Input structure composed in ONE buffer with chained Append calls
var order = new NTiProgramParameter("CUST01", 10) // CHAR(10)
.Append(1042) // BINARY(4)
.Append(149.90m, 9, 2) // packed DECIMAL(9,2)
.Append("EUR", 3) // CHAR(3)
.AsInput();
// EMPTY receiver: declared variable length, sized on the reply
var reply = new NTiProgramParameter().AsInputOutput();
// Fixed 30 byte output structure
var ds = new NTiProgramParameter("", 30, ParameterDirection.Output);
var parms = new List { order, reply, ds };
parms.Add("*CURRENT", 10).AsInput(); // Add extension: creates, appends, returns
await connection.CallProgramAsync("MYLIB", "ORDERPGM", parms);
// The empty receiver holds a VARCHAR: length prefix, then the data
string message = reply.GetString(2, reply.GetShort());
// Offset-based reads in the fixed structure
string code = ds.GetString(0, 10); // CHAR(10) at offset 0
int quantity = ds.GetInt(10); // BINARY(4) at offset 10
decimal total = ds.GetPackedDecimal(9, 2, 14); // packed DECIMAL(9,2) at offset 14
DateTime stamp = ds.GetDTSTimestamp(19); // *DTS timestamp at offset 19
Console.WriteLine($"{code} x{quantity} = {total} on {stamp:O}: {message}");
}
}