NTiProgramParameter

NTiProgramParameter décrit un paramètre d'appel de programme ou de procédure de programme de service : un buffer d'octets envoyé au serveur (InputData), un buffer rendu par le programme (OutputData), une direction et un CCSID. La classe est commune à CallProgram/CallProgramAsync et CallServiceProgram/CallServiceProgramAsync de NTiConnection.

Constructeurs

Tous acceptent une ParameterDirection optionnelle en dernier argument (défaut InputOutput).

Constructeur Type IBM i
() récepteur VIDE à taille variable (voir plus bas)
(string value, int length) CHAR(length), complété de blancs
(string value, int length, int ccsid) CHAR(length) dans le CCSID donné
(string[] values, int length) et variantes IEnumerable, avec ou sans ccsid tableau de CHAR(length)
(int value) / (int[] values) / (IEnumerable<int> values) BINARY(4) / tableau
(short value) / (short[] values) / (IEnumerable<short> values) BINARY(2) / tableau
(decimal value, int precision, int scale, bool packed = true) décimal packé (packed: true, DECIMAL) ou zoné (packed: false, NUMERIC)
(byte[] value) octets bruts, envoyés tels quels

Composition par Append

Les surcharges Append(...) prolongent le buffer du paramètre d'un champ supplémentaire, pour composer une structure de données dans un seul buffer ; chacune rend le paramètre, donc s'enchaîne.

Surcharge Champ ajouté
Append(string, int) / Append(string, int, int ccsid) CHAR(length)
Append(string[]/IEnumerable<string>, int) / idem avec ccsid tableau de CHAR(length)
Append(int) / Append(int[]/IEnumerable<int>) BINARY(4) / tableau
Append(short) / Append(short[]/IEnumerable<short>) BINARY(2) / tableau
Append(decimal, int precision, int scale, bool packed = true) décimal packé ou zoné
Append(byte[]) octets bruts

Direction

Direction (défaut InputOutput) pilote ce qui circule ; les extensions fluides .AsInput(), .AsOutput(), .AsInputOutput() la posent en rendant le paramètre.

Direction Sémantique
Input les octets sont envoyés, rien n'est rendu
Output (et ReturnValue) seule la longueur est déclarée, le buffer revient dans OutputData
InputOutput (défaut) les octets sont envoyés ET le buffer revient

Extensions Add

La famille parms.Add(...) sur IList reflète chaque constructeur : elle crée le paramètre, l'ajoute à la liste et le rend, pour enchaîner une direction : parms.Add("ABC", 10).AsInput(). Surcharges : Add() (récepteur vide), 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) (packé ; pour un zoné, passer par le constructeur), Add(byte[]) ; toutes avec la ParameterDirection optionnelle en dernier argument.

Lecture des résultats

Après l'appel, les accesseurs lisent le buffer de sortie (OutputData). Les entiers sont gros-boutistes (big endian), comme sur l'IBM i.

Accesseur Lit
GetString() / GetString(int ccsid) tout le buffer en texte, dans le CCSID du paramètre ou un CCSID explicite
GetString(int offset, int length) / GetString(int offset, int length, int ccsid) un segment en texte
GetInt() / GetInt(int offset) BINARY(4)
GetShort() / GetShort(int offset) BINARY(2), tel le préfixe de longueur d'un VARCHAR
GetPackedDecimal(int precision, int scale[, int offset]) décimal packé
GetZonedDecimal(int precision, int scale[, int offset]) décimal zoné
GetBytes() / GetBytes(int offset, int length) octets bruts
GetDTSTimestamp([int offset]) horodatage *DTS de 8 octets, rendu en DateTime

InputData et OutputData sont des byte[] publics : le contrôle manuel intégral reste possible.

CCSID

Membre Rôle
Ccsid CCSID du paramètre pour les conversions texte ; null = CCSID du job de l'appel
NTiProgramParameter.DefaultCcsid (statique) défaut process-wide : 37 tant qu'aucune connexion n'est ouverte, puis CCSID de job de la DERNIÈRE connexion ouverte (compat v4)
EffectiveCcsid CCSID effectif : l'explicite, sinon le défaut process-wide

Avec plusieurs connexions sur des CCSID différents, passer un ccsid explicite (constructeur, Append ou GetString). Les caractères !, [, ], ^ varient d'une page EBCDIC à l'autre : expliciter le ccsid dès qu'ils comptent.

Récepteur vide

new NTiProgramParameter() (sans valeur) est un récepteur à taille variable : il est déclaré au serveur avec la longueur conventionnelle 0xFFFF sans émettre un octet, et revient dimensionné sur la donnée que le programme a réellement écrite (parité v4.4.14). C'est le pattern des sorties VARCHAR et des structures de réponse : receiver.GetString(2, receiver.GetShort()) lit un VARCHAR (préfixe de longueur BINARY(2), puis la donnée).

ServiceProgramParameterFormat

Propriété utilisée uniquement par CallServiceProgram/CallServiceProgramAsync (ignorée par CallProgram) : ByReference (défaut) passe l'adresse du stockage du paramètre ; ByValue passe un BINARY(4) par valeur, la donnée doit faire exactement 4 octets et la sortie n'a alors pas de sens. Valeurs dans NTiServiceProgramParameterFormat.

Contrat hexadécimal

Partout dans NTi, la représentation chaîne d'une donnée binaire est l'hexadécimal MAJUSCULE sans séparateur (parité v4) : c'est ce que rend le GetString du data reader sur une colonne BINARY, VARBINARY, ROWID ou taguée FOR BIT DATA (CCSID 65535). Sur un NTiProgramParameter, le binaire se lit avec GetBytes ou OutputData ; GetString exige un CCSID texte réel : demander 65535 (« pas de conversion ») lève une erreur explicite.

Exemple complet

L'async est la voie normale ; CallProgram existe aussi en synchrone.

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();

        // Structure d'entrée composée dans UN buffer par Append chaînés
        var order = new NTiProgramParameter("CUST01", 10)      // CHAR(10)
            .Append(1042)                                      // BINARY(4)
            .Append(149.90m, 9, 2)                             // DECIMAL(9,2) packé
            .Append("EUR", 3)                                  // CHAR(3)
            .AsInput();

        // Récepteur VIDE : déclaré à taille variable, dimensionné sur la réponse
        var reply = new NTiProgramParameter().AsInputOutput();

        // Structure de sortie fixe de 30 octets
        var ds = new NTiProgramParameter("", 30, ParameterDirection.Output);

        var parms = new List { order, reply, ds };
        parms.Add("*CURRENT", 10).AsInput();                   // extension Add : crée, ajoute et rend

        await connection.CallProgramAsync("MYLIB", "ORDERPGM", parms);

        // Le récepteur vide contient un VARCHAR : préfixe de longueur, puis la donnée
        string message = reply.GetString(2, reply.GetShort());

        // Lecture par offsets dans la structure fixe
        string code = ds.GetString(0, 10);                     // CHAR(10) à l'offset 0
        int quantity = ds.GetInt(10);                          // BINARY(4) à l'offset 10
        decimal total = ds.GetPackedDecimal(9, 2, 14);         // DECIMAL(9,2) packé à l'offset 14
        DateTime stamp = ds.GetDTSTimestamp(19);               // horodatage *DTS à l'offset 19

        Console.WriteLine($"{code} x{quantity} = {total} le {stamp:O} : {message}");
    }
}

Reconnexion au serveur...

La connexion au serveur a été perdue. La page va se recharger.