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 variants, with or without ccsid 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 IList mirrors every constructor: it creates the parameter, appends it to the list and returns it, so a direction can be chained: 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 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}");
    }
}

Reconnecting to the server...

The connection to the server was lost. The page will reload.