NTiConnection
NTiConnection is the ADO.NET connection of the NTi provider for IBM i (DB2 for i). It derives from DbConnection and fully honors the standard contract (Open/OpenAsync, CreateCommand, BeginTransaction, ChangeDatabase, GetSchema, Close/DisposeAsync...), which is not redocumented here (see the DbConnection reference).
This page covers the surface specific to NTi: configuration, CL commands, program and service program calls, MFA, diagnostics, and pooling.
Async is the normal path. Every method also has a synchronous counterpart.
Configuration properties
Each typed property maps to a connection string keyword. Defaults, synonyms, and detailed semantics are in Connection string.
Configuration is locked in as soon as the connection is open. Everything is set before OpenAsync (or Open). Only the current schema can be changed on the fly, with ChangeDatabase/ChangeDatabaseAsync.
| Properties | Role |
|---|---|
Server, Username, Password |
server and IBM i identity |
AdditionalFactor |
static MFA factor (see the MFA family below) |
LicenseLibrary |
library holding the NTi license |
UseSSL, Untrusted |
TLS to the host servers. Untrusted (test only) accepts any certificate |
UsePortMapper, SignonPort, DatabasePort, CommandPort, MapperPort |
fixed ports or ports resolved through the port mapper |
Compress |
RLE compression of large responses |
Pooling, PoolSize, LimitPoolSize |
connection pool, off by default (pooling=true strongly recommended on the web) |
DefaultCcsid, ForceTranslate, TrimCharFields, LegacyNullSemantics |
text, CCSID, and NULL semantics |
DecfloatRoundMode |
DECFLOAT rounding, see DecfloatRoundOption |
LOBMaxSize |
threshold (bytes) above which LOBs switch to locators |
BlockingFactor, FetchBlockSize, FetchAhead |
fetch blocks (adaptive factor by default) |
DefaultDatabase, NamingConvention |
current schema and naming, see NamingConvention |
ApplicationName, ClientAccounting, ClientUserIdentifier, ClientProgramIdentifier |
client identity, part of the pool key |
ConnectionTimeout (read-only) |
connection establishment delay in seconds. 0 = infinite (the default, like every NTi timeout) |
By default (persist security info set to false), ConnectionString is stripped of the password as soon as the connection opens.
CL commands and program calls
Every connection opens two distinct IBM i jobs: SQL runs in the database job (QZDASOINIT), while CL commands and program calls run in the command job (QZRCSRVS). Each job has its own QTEMP, its own CURLIB, and its own overrides. An object created in QTEMP by a CL command is not visible from SQL, and vice versa. To run a CL command inside the SQL job, go through CALL QSYS2.QCMDEXC('...'), at the cost of the SQL round trip.
| Method | Role |
|---|---|
ExecuteClCommand(command) and ExecuteClCommandAsync(command, ct) |
runs a CL command. On failure: NTiCommandException with the message stack |
CallProgram(library, program[, parameters]) and CallProgramAsync(..., ct) |
calls a program or a system API. Parameters: List<NTiProgramParameter> or IList<NTiProgramParameter> (see NTiProgramParameter) |
CallServiceProgram(library, serviceProgram, procedureName, parameters[, returnFormat, procedureNameCcsid]) and CallServiceProgramAsync(..., ct) |
calls an exported procedure of a service program through the QZRUCLSP API |
A few constraints apply to CallServiceProgram:
- The export name is case sensitive, matched byte for byte in the
procedureNameCcsidCCSID (37 by default). - Each parameter passes by reference by default, or by value depending on its
ServiceProgramParameterFormat. By value, it must be a BINARY(4) of exactly 4 bytes. - 7 parameters maximum.
- The method returns the receptacle carrying the return value, or
nullwhenreturnFormatisNone. Readrc.GetInt()for the integer, andrc.GetInt(4)for the errno withIntegerAndErrno. - Pointer return values are not supported, since a server space pointer has no meaning on the client. A procedure that returns text fills a caller supplied buffer instead.
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() is still available synchronously
// CL command: command job (QZRCSRVS)
await connection.ExecuteClCommandAsync("OVRDBF FILE(ORDERS) TOFILE(MYLIB/ORDERS)");
// System API = ordinary program: QWCRSVAL (system values)
var parms = new List
{
new NTiProgramParameter().AsInputOutput(), // empty receiver
new NTiProgramParameter(32, ParameterDirection.Input), // receiver length
new NTiProgramParameter(1, ParameterDirection.Input), // number of values
new NTiProgramParameter("QSRLNBR", 10, ParameterDirection.Input),
new NTiProgramParameter("", 8, ParameterDirection.Output), // error code
};
await connection.CallProgramAsync("QSYS", "QWCRSVAL", parms);
Console.WriteLine(parms[0].GetString(24, 8, 37).Trim()); // system serial number
// Exported procedure of a service program (QZRUCLSP)
var buffer = new NTiProgramParameter(new byte[64], ParameterDirection.InputOutput);
var length = new NTiProgramParameter(64, ParameterDirection.Input)
{
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')); // server host name
// Two distinct IBM i jobs per connection
Console.WriteLine($"SQL job : {connection.DatabaseJob} (CCSID {connection.DatabaseJobCcsid})");
Console.WriteLine($"Command job : {connection.CommandJob} (CCSID {connection.CommandJobCcsid})");
Console.WriteLine($"License: {connection.RemainingDays} days remaining");
// Purging the pool
NTiConnection.ClearPool(connection); // the pool matching these options
NTiConnection.ClearAllPools(); // every pool in the process
}
} MFA family
| Member | Role |
|---|---|
AdditionalFactor |
static factor (TOTP code), takes precedence over both callbacks |
AdditionalFactorProvider |
synchronous callback Func<NTiAuthenticationFactorContext, string?>, invoked at signon of every physical session, takes precedence over the async variant |
AdditionalFactorAsyncProvider |
asynchronous callback Func<NTiAuthenticationFactorContext, CancellationToken, ValueTask<string?>> (vaults, HSMs, prompts), awaited natively on OpenAsync with the caller's token |
AdditionalFactorCallback |
obsolete (v4 compatibility, no context). Prefer AdditionalFactorProvider |
SupportsAdditionalFactor |
true when the connected server supports multi-factor authentication |
The context (NTiAuthenticationFactorContext) identifies the host (Host) and the profile (User) being authenticated. The callback is invoked once per physical session (the factor is reused for the session's sockets) and can possibly run concurrently while the pool grows. Returning null continues the signon without a factor. The server makes the final call.
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");
// Async variant: called at signon of every physical session, with
// the caller's token on OpenAsync. A static factor
// (connection.AdditionalFactor = "123456") or the synchronous callback
// (AdditionalFactorProvider) take precedence if defined.
connection.AdditionalFactorAsyncProvider = async (ctx, ct) =>
{
await Task.Yield(); // vault, HSM, or prompt: cancellable through ct
return Environment.GetEnvironmentVariable($"TOTP_{ctx.User}");
};
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
await connection.OpenAsync(cts.Token);
Console.WriteLine(connection.SupportsAdditionalFactor
? "The server supports MFA"
: "Signed on without an additional factor");
}
}Diagnostics and state
| Member | Role |
|---|---|
Result |
NTiMessage: the result of the last operation (SQL, CL, or program), success or failure alike. Carries SqlCode, SqlState, MessageID, MessageText, SecondLevelText, and Messages (a detailed stack for CL/program calls) |
Messages |
v4 compatibility: use Result |
DatabaseJob / CommandJob |
name of the database job (QZDASOINIT) / command job (QZRCSRVS), for server side inspection |
DatabaseJobCcsid / CommandJobCcsid |
CCSID of each of the two jobs |
RemainingDays |
days remaining on the NTi license (0 if unknown or unlicensed) |
Pool
Two purge statics, mirroring SqlConnection: ClearPool(connection) clears the pool matching the passed connection's options, ClearAllPools() clears every pool in the process (see the end of the first example).
The pool is off by default (v4 compatibility). Client identity (ApplicationName, ClientAccounting, ClientUserIdentifier, ClientProgramIdentifier) is part of the pool key, so two distinct identities never share a pool.