Guide de démarrage rapide

NTi est conçu pour répondre à une réalité que peu d'outils adressent : connecter votre IBM i à .NET et être opérationnel en moins de 15 minutes.

Aucun driver, aucun composant natif : NTi est un provider ADO.NET 100 % managé, distribué en simple package NuGet. Vous codez en C# avec une syntaxe familière, et l'API est réellement asynchrone de bout en bout : OpenAsync, exécution, lecture, fermeture, annulation au jeton de l'appelant.

NTi est entièrement multiplateforme : Windows, Linux et macOS, en x86, x64 ou ARM64. Côté serveur, il fonctionne de la V5R4 aux versions les plus récentes d'IBM i.

Simple, rapide et immédiat !

Prérequis

Côté développement

  • Visual Studio 2022, JetBrains Rider, ou VS Code avec l'extension C#
  • Une version .NET compatible :
    • .NET 5 / 6 / 7 / 8 / 9 / 10
    • .NET Framework 4.7.2 / 4.8 / 4.8.1
    • .NET Standard 2.1 (.NET Core 3.x, Mono, Xamarin)

Côté IBM i

  • IBM i V5R4 minimum, V7R4 ou ultérieure recommandée
  • Les services TCP *DATABASE, *RMTCMD et *SIGNON démarrés
  • Une licence NTi active : Démarrer l'essai gratuit

💡 Pour le détail complet des prérequis et de l'installation de la licence, consultez la page Installation.


Étape 1 : Installer NTi via NuGet

Ouvrez un terminal dans votre dossier de projet et ajoutez les packages :

dotnet add package Aumerial.Data.Nti
dotnet add package Dapper

Ou via la console du Gestionnaire de packages Visual Studio :

Install-Package Aumerial.Data.Nti
Install-Package Dapper

💡 Pourquoi Dapper ? Dapper est un micro ORM léger qui mappe automatiquement les résultats SQL sur vos objets C#. Il enrichit votre connexion NTi de méthodes comme Query<T> ou Execute, pour des interactions plus naturelles avec vos données IBM i.


Étape 2 : Ouvrir la connexion

OpenAsync est la voie normale : NTi est asynchrone de bout en bout, sans aucun sync-over-async. L'équivalent synchrone Open reste disponible.

using System;
using System.Data;
using Aumerial.Data.Nti;

using var conn = new NTiConnection("server=serverName;user=userName;password=password");
await conn.OpenAsync();

Console.WriteLine(conn.State == ConnectionState.Open
    ? "Connexion IBM i et licence NTi OK."
    : "Connexion impossible.");

💡 Le pool de connexion est inactif par défaut. Pour une application web (ASP.NET Core, Blazor) ou toute charge concurrente, ajoutez pooling=true à la chaîne de connexion afin de réutiliser les connexions physiques au lieu de les rouvrir à chaque requête. Tous les mots-clés (pool, MFA, TLS, timeouts, bibliothèque par défaut) sont décrits dans la page Chaîne de connexion.


Étape 3 : Lire des données

Avec Dapper, QueryAsync<MyRecord> exécute le SELECT et mappe chaque ligne sur votre classe (correspondance colonne/propriété insensible à la casse). La version synchrone Query<MyRecord> existe aussi.

using System;
using System.Linq;
using Aumerial.Data.Nti;
using Dapper;

using var conn = new NTiConnection("server=serverName;user=userName;password=password");
await conn.OpenAsync();

var records = (await conn.QueryAsync("SELECT ID, LABEL FROM MYLIB.MYTABLE")).ToList();

foreach (var record in records)
    Console.WriteLine($"{record.Id} : {record.Label}");

public class MyRecord
{
    public string Id { get; set; }
    public string Label { get; set; }
}

Étape 4 : Écrire des données

using System;
using Aumerial.Data.Nti;
using Dapper;

using var conn = new NTiConnection("server=serverName;user=userName;password=password");
await conn.OpenAsync();

int rows = await conn.ExecuteAsync(
    "UPDATE MYLIB.MYTABLE SET LABEL = @label WHERE ID = @id",
    new { label = "Updated", id = "REC-001" });

Console.WriteLine($"{rows} ligne(s) mise(s) à jour.");

💡 Pour un exemple complet avec Entity Framework Core (Code First, DB First, CRUD), consultez le tutoriel CRUD avec EF Core.


Étape 5 : Exploiter toutes les ressources IBM i

NTi va bien au-delà du SQL : commandes CL avec ExecuteClCommand, appels de programmes RPG, COBOL ou CL avec CallProgram, procédures exportées des programmes de service avec CallServiceProgram, procédures stockées, le tout avec les variantes asynchrones correspondantes.

💡 Chaque connexion NTi pilote deux jobs serveur : le SQL s'exécute dans un job QZDASOINIT, les commandes et programmes dans un job QZRCSRVS. Leurs QTEMP et CURLIB sont donc séparées.

Exécuter une commande CL

using System;
using Aumerial.Data.Nti;

using var conn = new NTiConnection("server=serverName;user=userName;password=password");
await conn.OpenAsync();

try
{
    await conn.ExecuteClCommandAsync("CRTLIB LIB(MYLIB) TEXT('My new library')");
}
catch (NTiCommandException ex)
{
    // Pile de messages IBM i : identifiant et texte de chaque message
    foreach (var message in ex.Messages)
        Console.WriteLine($"{message.Id} : {message.Text}");
}

💡 Consultez le tutoriel Exécuter une commande CL.

Appeler un programme RPG

using System;
using System.Collections.Generic;
using Aumerial.Data.Nti;

using var conn = new NTiConnection("server=serverName;user=userName;password=password");
await conn.OpenAsync();

var parms = new List
{
    new NTiProgramParameter("Hello", 10).AsInput(),  // CHAR(10) en entrée
    new NTiProgramParameter("", 128).AsOutput()      // CHAR(128) en sortie
};

await conn.CallProgramAsync("MYLIB", "MYPGM", parms);

string result = parms[1].GetString(0, 128);
Console.WriteLine(result);

💡 Consultez le tutoriel Appeler un programme pour les paramètres typés (binaires, décimaux packés et zonés) et les récepteurs à taille variable.

Appeler une procédure d'un programme de service

CallServiceProgram appelle une procédure exportée d'un programme de service, via l'API système QZRUCLSP. Ici, la procédure C gethostname remplit un buffer fourni par l'appelant. Attention : le nom d'export est sensible à la casse.

using System;
using System.Collections.Generic;
using System.Data;
using Aumerial.Data.Nti;

using var conn = new NTiConnection("server=serverName;user=userName;password=password");
await conn.OpenAsync();

var buffer = new NTiProgramParameter(new byte[64], ParameterDirection.InputOutput);
var length = new NTiProgramParameter(64)
{
    // BINARY(4) passé par valeur ; par référence est le défaut
    ServiceProgramParameterFormat = NTiServiceProgramParameterFormat.ByValue
};

var rc = await conn.CallServiceProgramAsync(
    "QSYS", "QSOSRV1", "gethostname",
    new List { buffer, length },
    NTiServiceProgramReturnFormat.Integer);

if (rc != null && rc.GetInt() == 0)
    Console.WriteLine(buffer.GetString(0, 64).TrimEnd('\0', ' '));

💡 Consultez le tutoriel Appeler un programme de service pour les formats de paramètres, les retours Integer et IntegerAndErrno et la gestion des erreurs.

Appeler une procédure stockée

using System;
using System.Data;
using System.Linq;
using Aumerial.Data.Nti;
using Dapper;

using var conn = new NTiConnection("server=serverName;user=userName;password=password");
await conn.OpenAsync();

var parameters = new DynamicParameters();
parameters.Add("myParam", dbType: DbType.Decimal, direction: ParameterDirection.Output);

var records = (await conn.QueryAsync(
    "MYLIB.MYPROC",
    parameters,
    commandType: CommandType.StoredProcedure)).ToList();

Console.WriteLine(parameters.Get("myParam"));

💡 Consultez le tutoriel Procédure stockée pour un exemple complet avec les approches DataReader et Dapper.


Et maintenant ?

Vous êtes opérationnel. Voici quelques pages pour aller plus loin :

Reconnexion au serveur...

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