NTiProgramParameter
NTiProgramParameter describes a parameter of a program call or 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 accept an optional trailing ParameterDirection (default: InputOutput):
| Constructor | IBM i type |
|---|---|
() |
empty variable size 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) and IEnumerable<string> variants, with or without ccsid |
array of CHAR(length) |
(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 | Field added |
|---|---|
Append(string, int) / Append(string, int, int ccsid) |
CHAR(length) |
Append(string[]/IEnumerable<string>, int) / same with ccsid |
array of CHAR(length) |
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 over the wire. The fluent extensions .AsInput(), .AsOutput(), .AsInputOutput() set it and return the parameter.
| Direction | Semantics |
|---|---|
Input |
bytes are sent, nothing comes back |
Output (and ReturnValue) |
only the length is declared, the buffer comes back in OutputData |
InputOutput (default) |
bytes are sent AND the buffer comes back |
Add extensions
The parms.Add(...) family on IList<NTiProgramParameter> mirrors every constructor: it creates the parameter, appends it to the list, and returns it, so a direction can be chained, as in parms.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 accept the optional trailing ParameterDirection.
Reading results
After the call, the accessors read the output buffer (OutputData). Integers are big-endian, like on the IBM i.
| Accessor | Reads |
|---|---|
GetString() / GetString(int ccsid) |
the whole buffer as text, in the parameter's CCSID or an explicit one |
GetString(int offset, int length) / GetString(int offset, int length, int ccsid) |
a text segment |
GetInt() / GetInt(int offset) |
BINARY(4) |
GetShort() / GetShort(int offset) |
BINARY(2), such as a VARCHAR length prefix |
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[], for full manual control.
CCSID
| Member | Role |
|---|---|
Ccsid |
the parameter's CCSID for text conversions. null means the CCSID of the call's job |
NTiProgramParameter.DefaultCcsid (static) |
process-wide default: 37 until a connection has opened, then the job CCSID of the last connection opened (v4 compatibility) |
EffectiveCcsid |
the effective CCSID: the explicit one, or the process-wide default otherwise |
With several connections on different CCSIDs, or as soon as the !, [, ], and ^ characters come into play (they vary from one EBCDIC code page to another), pass an explicit ccsid to the constructor, to Append, or to GetString.
Empty receiver
new NTiProgramParameter() (no value) is a variable size receiver. It is declared to the server with the conventional 0xFFFF length without emitting a single byte, and comes back sized to the data the program actually wrote (v4.4.14 parity).
This is the pattern for VARCHAR output and response structures. receiver.GetString(2, receiver.GetShort()) reads a VARCHAR (a BINARY(2) length prefix, then the data).
ServiceProgramParameterFormat
A property used only by CallServiceProgram/CallServiceProgramAsync (ignored by CallProgram):
ByReference(the default) passes the address of the parameter's storage.ByValuepasses a BINARY(4) by value. The data must be exactly 4 bytes, and output no longer makes sense in that case.
Values are in NTiServiceProgramParameterFormat.
The hexadecimal contract
Throughout NTi, the string representation of binary data is uppercase hexadecimal with no separators (v4 parity), and that is what the data reader's GetString returns on a BINARY, VARBINARY, ROWID, or FOR BIT DATA tagged column (CCSID 65535).
On an NTiProgramParameter, binary data is read with GetBytes or OutputData, since GetString requires a real text CCSID. Requesting 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 a single buffer through chained Append calls
var order = new NTiProgramParameter("CUST01", 10) // CHAR(10)
.Append(1042) // BINARY(4)
.Append(149.90m, 9, 2) // DECIMAL(9,2) packed
.Append("EUR", 3) // CHAR(3)
.AsInput();
// Empty receiver: declared with a variable size, sized to the response
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());
// Reading by offset inside 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); // DECIMAL(9,2) packed at offset 14
DateTime stamp = ds.GetDTSTimestamp(19); // *DTS timestamp at offset 19
Console.WriteLine($"{code} x{quantity} = {total} on {stamp:O}: {message}");
}
}