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.
  • ByValue passes 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}");
    }
}

Reconnecting to the server...

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