NTiConnection
NTiConnection est la connexion ADO.NET du provider NTi pour IBM i (DB2 for i). Elle dérive de DbConnection et en honore intégralement le contrat standard (Open/OpenAsync, CreateCommand, BeginTransaction, ChangeDatabase, GetSchema, Close/DisposeAsync...), qui n'est pas redocumenté ici : voir la référence DbConnection. Cette page couvre la surface spécifique NTi : configuration, commandes CL, appels de programmes et de programmes de service, MFA, diagnostic et pool. L'async est la voie normale ; chaque méthode existe aussi en synchrone.
Propriétés de configuration
Chaque propriété typée correspond à un mot-clé de la chaîne de connexion ; défauts, synonymes et sémantique détaillée sont dans Propriétés de connexion. La configuration est figée dès que la connexion est ouverte : tout se règle avant OpenAsync (ou Open) ; seul le schéma courant se change à chaud, avec ChangeDatabase/ChangeDatabaseAsync. Par défaut (« persist security info » à faux), ConnectionString est expurgée du mot de passe dès l'ouverture.
| Propriétés | Rôle |
|---|---|
Server, Username, Password |
serveur et identité IBM i |
AdditionalFactor |
facteur MFA statique (voir la famille MFA plus bas) |
LicenseLibrary |
bibliothèque de la licence NTi |
UseSSL, Untrusted |
TLS vers les serveurs hôtes ; Untrusted (tests uniquement) accepte tout certificat |
UsePortMapper, SignonPort, DatabasePort, CommandPort, MapperPort |
ports fixes ou résolus par le port mapper |
Compress |
compression RLE des grandes réponses |
Pooling, PoolSize, LimitPoolSize |
pool de connexions, INACTIF par défaut (pooling=true fortement recommandé en web) |
DefaultCcsid, ForceTranslate, TrimCharFields, LegacyNullSemantics |
texte, CCSID et sémantique des NULL |
DecfloatRoundMode |
arrondi DECFLOAT, voir DecfloatRoundOption |
LOBMaxSize |
seuil (octets) de bascule des LOB en locators |
BlockingFactor, FetchBlockSize, FetchAhead |
blocs de fetch (facteur adaptatif par défaut) |
DefaultDatabase, NamingConvention |
schéma courant et nommage, voir NamingConvention |
ApplicationName, ClientAccounting, ClientUserIdentifier, ClientProgramIdentifier |
identité client, intégrée à la clé de pool |
ConnectionTimeout (lecture seule) |
délai d'établissement en secondes ; 0 = infini (défaut, comme tous les timeouts NTi) |
Commandes CL et appels de programmes
Chaque connexion ouvre deux jobs IBM i distincts : le SQL s'exécute dans le job database (QZDASOINIT), les commandes CL et les appels de programmes dans le job commande (QZRCSRVS). Chaque job a son propre QTEMP, sa propre CURLIB et ses propres overrides : un objet créé en QTEMP par une commande CL n'est pas visible du SQL, et réciproquement. Pour exécuter une commande CL DANS le job SQL, passer par CALL QSYS2.QCMDEXC('...'), au prix de l'aller-retour SQL.
| Méthode | Rôle |
|---|---|
ExecuteClCommand(command) et ExecuteClCommandAsync(command, ct) |
exécute une commande CL ; échec = NTiCommandException avec la pile de messages |
CallProgram(library, program[, parameters]) et CallProgramAsync(..., ct) |
appelle un programme ou une API système ; paramètres : List |
CallServiceProgram(library, serviceProgram, procedureName, parameters[, returnFormat, procedureNameCcsid]) et CallServiceProgramAsync(..., ct) |
appelle une procédure exportée d'un programme de service via l'API QZRUCLSP |
CallServiceProgram : le nom d'export est SENSIBLE À LA CASSE (comparé octet à octet dans le CCSID procedureNameCcsid, 37 par défaut) ; chaque paramètre passe par référence (défaut) ou par valeur selon son ServiceProgramParameterFormat (par valeur : BINARY(4) de 4 octets exactement) ; 7 paramètres au maximum. La méthode rend le porteur de la valeur de retour, ou null quand returnFormat vaut None : lire rc.GetInt() pour l'entier, et rc.GetInt(4) pour l'errno en IntegerAndErrno. Les retours pointeur ne sont pas supportés (un pointeur d'espace serveur n'a pas de sens côté client) : une procédure qui rend du texte remplit un buffer fourni par l'appelant.
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using Aumerial.Data.Nti;
class NTiConnectionDemo
{
static async Task Main()
{
await using var connection = new NTiConnection(
"server=MYIBMI;user=MYUSER;password=MYPASSWORD;pooling=true");
await connection.OpenAsync(); // Open() reste disponible en synchrone
// Commande CL : job commande (QZRCSRVS)
await connection.ExecuteClCommandAsync("OVRDBF FILE(ORDERS) TOFILE(MYLIB/ORDERS)");
// API système = programme ordinaire : QWCRSVAL (valeurs système)
var parms = new List
{
new NTiProgramParameter().AsInputOutput(), // récepteur vide
new NTiProgramParameter(32, ParameterDirection.Input), // longueur du récepteur
new NTiProgramParameter(1, ParameterDirection.Input), // nombre de valeurs
new NTiProgramParameter("QSRLNBR", 10, ParameterDirection.Input),
new NTiProgramParameter("", 8, ParameterDirection.Output), // code erreur
};
await connection.CallProgramAsync("QSYS", "QWCRSVAL", parms);
Console.WriteLine(parms[0].GetString(24, 8, 37).Trim()); // numéro de série
// Procédure exportée d'un programme de service (QZRUCLSP)
var buffer = new NTiProgramParameter(new byte[64], ParameterDirection.InputOutput);
var length = new NTiProgramParameter(64)
{
ServiceProgramParameterFormat = NTiServiceProgramParameterFormat.ByValue,
};
var rc = await connection.CallServiceProgramAsync("QSYS", "QSOSRV1", "gethostname",
new List { buffer, length },
NTiServiceProgramReturnFormat.Integer);
if (rc is not null && rc.GetInt() == 0)
Console.WriteLine(buffer.GetString().TrimEnd('\0')); // nom d'hôte du serveur
// Deux jobs IBM i distincts par connexion
Console.WriteLine($"Job SQL : {connection.DatabaseJob} (CCSID {connection.DatabaseJobCcsid})");
Console.WriteLine($"Job commande : {connection.CommandJob} (CCSID {connection.CommandJobCcsid})");
Console.WriteLine($"Licence : {connection.RemainingDays} jours restants");
// Purge du pool
NTiConnection.ClearPool(connection); // le pool correspondant à ces options
NTiConnection.ClearAllPools(); // tous les pools du processus
}
} Famille MFA
| Membre | Rôle |
|---|---|
AdditionalFactor |
facteur statique (code TOTP), prioritaire sur les deux rappels |
AdditionalFactorProvider |
rappel synchrone Func<NTiAuthenticationFactorContext, string?>, invoqué au signon de chaque session physique ; prime sur la variante asynchrone |
AdditionalFactorAsyncProvider |
rappel asynchrone Func<NTiAuthenticationFactorContext, CancellationToken, ValueTask<string?>> (coffres, HSM, prompts) ; sur OpenAsync, attendu nativement avec le jeton de l'APPELANT |
AdditionalFactorCallback |
obsolète (compat v4, sans contexte) : préférer AdditionalFactorProvider |
SupportsAdditionalFactor |
vrai quand le serveur connecté supporte l'authentification multifacteur |
Le contexte (NTiAuthenticationFactorContext) identifie l'hôte (Host) et le profil (User) en cours d'authentification. Le rappel est invoqué une fois par session physique (le facteur est réutilisé pour les sockets de la session) et possiblement en concurrence pendant la croissance du pool. Rendre null poursuit le signon sans facteur : le serveur tranche.
using System;
using System.Threading;
using System.Threading.Tasks;
using Aumerial.Data.Nti;
class MfaDemo
{
static async Task Main()
{
await using var connection = new NTiConnection("server=MYIBMI;user=MYUSER;password=MYPASSWORD");
// Variante asynchrone : appelée au signon de chaque session physique, avec
// le jeton de l'appelant sur OpenAsync. Un facteur statique
// (connection.AdditionalFactor = "123456") ou le rappel synchrone
// (AdditionalFactorProvider) ont priorité s'ils sont définis.
connection.AdditionalFactorAsyncProvider = async (ctx, ct) =>
{
await Task.Yield(); // coffre, HSM ou prompt : annulable par ct
return Environment.GetEnvironmentVariable($"TOTP_{ctx.User}");
};
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await connection.OpenAsync(cts.Token);
Console.WriteLine(connection.SupportsAdditionalFactor
? "Le serveur supporte la MFA"
: "Signon sans facteur supplémentaire");
}
}Diagnostic et état
| Membre | Rôle |
|---|---|
Result |
NTiMessage : résultat de la dernière opération (SQL, CL ou programme), succès comme échec ; porte SqlCode, SqlState, MessageID, MessageText, SecondLevelText et Messages (pile détaillée pour CL/programmes) |
Messages |
compatibilité v4 : utiliser Result |
DatabaseJob / CommandJob |
nom du job database (QZDASOINIT) / du job commande (QZRCSRVS), pour inspection côté serveur |
DatabaseJobCcsid / CommandJobCcsid |
CCSID de chacun des deux jobs |
RemainingDays |
jours restants de la licence NTi (0 si inconnu ou non licencié) |
Pool
Deux statiques de purge, symétriques de SqlConnection : ClearPool(connection) vide le pool correspondant aux options de la connexion passée, ClearAllPools() vide tous les pools du processus (voir la fin du premier exemple). Le pool est inactif par défaut (compat v4) ; l'identité client (ApplicationName, ClientAccounting, ClientUserIdentifier, ClientProgramIdentifier) fait partie de la clé de pool : deux identités distinctes ne partagent jamais un pool.