Exceptions
Toutes les exceptions du provider dérivent de NTiException, elle-même dérivée de DbException comme l'exige le contrat ADO.NET : un catch (DbException) générique attrape donc tout NTi (référence DbException).
DbException
NTiException base de toutes les exceptions NTi
NTiSqlException erreur SQL du serveur database (SQLCODE négatif)
NTiCommandException échec d'une commande CL ou d'un appel de programme
NTiCommunicationException panne de communication, connexion casséeQuelle condition lève quoi
| Exception | Levée quand | Propriétés spécifiques |
|---|---|---|
NTiException |
erreur du provider hors des trois catégories dérivées : mauvaise utilisation de l'API, conversion impossible (par exemple demander du texte en CCSID 65535), échec d'un rappel MFA (cause d'origine préservée en InnerException) |
celles de DbException |
NTiSqlException |
le serveur database rend un SQLCODE négatif | SqlCode ; SqlState (SQLSTATE de 5 caractères) ; SecondLevelMessage (texte de second niveau quand demandé) |
NTiCommandException |
une commande CL, un appel de programme ou de programme de service échoue | ReturnCode (code retour du serveur commande) ; Messages (pile de messages IBM i, voir ci-dessous) |
NTiCommunicationException |
échec de connexion, timeout, fin de flux inattendue, trame malformée, erreur TLS | IsTransient, toujours vrai : la connexion fautive est cassée et jetée, une nouvelle tentative sur une connexion fraîche peut réussir (stratégies de retry EF/Polly) |
Sur net6 et plus, SqlState redéfinit DbException.SqlState : le code générique catch (DbException e) when (e.SqlState == "42704") fonctionne tel quel.
NTiCommandResult : la pile de messages
NTiCommandException.Messages porte la pile de messages IBM i (CPFxxxx, MCHxxxx...) rendue par le serveur commande ; chaque entrée est un NTiCommandResult :
| Propriété | Contenu |
|---|---|
Id |
identifiant du message (CPF2105, MCH0602, SQL0204...) |
Type |
code du type de message |
Severity |
sévérité, 0 à 99 |
File |
fichier de messages |
Library |
bibliothèque du fichier de messages |
Text |
texte de premier niveau |
SubstitutionData |
données de substitution du message |
Help |
texte de second niveau (aide) |
La même pile est disponible côté succès dans connection.Result (voir NTiConnection).
ToString et StackTrace : sans pile d'appels
Sur toute la hiérarchie, StackTrace est vide et ToString() est rendu SANS pile d'appels : le produit livré est obfusqué et la pile ne fait pas partie de la surface de diagnostic (politique v4). L'information actionnable est le message et la chaîne des causes internes (InnerException), toujours préservée.
Annulation : connexion cassée par contrat
Annuler le jeton d'une opération en vol lève OperationCanceledException portant le jeton de l'APPELANT (pas une exception NTi) et casse la connexion PAR CONTRAT : la trame en vol est perdue, la connexion n'est jamais réutilisée ni rendue au pool. Rouvrir la connexion (ou en reprendre une du pool) avant de continuer.
using System;
using System.Data.Common;
using System.Threading;
using System.Threading.Tasks;
using Aumerial.Data.Nti;
class ExceptionDemo
{
static async Task Main()
{
await using var connection = new NTiConnection("server=MYIBMI;user=MYUSER;password=MYPASSWORD");
await connection.OpenAsync();
try
{
await connection.ExecuteClCommandAsync("DLTLIB LIB(NOPE)");
}
catch (NTiCommandException e) // échec CL ou programme
{
Console.WriteLine($"Code retour {e.ReturnCode}");
foreach (var m in e.Messages) // pile de messages IBM i
Console.WriteLine($"{m.Id} [{m.Severity}] {m.Text}");
}
try
{
using var command = connection.CreateCommand();
command.CommandText = "SELECT * FROM MYLIB.NOPE";
await using var reader = await command.ExecuteReaderAsync();
}
catch (NTiSqlException e) // erreur SQL : SQLCODE négatif
{
Console.WriteLine($"SQLCODE {e.SqlCode}, SQLSTATE {e.SqlState}");
Console.WriteLine(e.SecondLevelMessage);
}
catch (DbException e) // filet générique : tout NTi dérive de DbException
{
Console.WriteLine(e.Message); // ToString() et Message sont rendus sans pile
}
using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(5));
try
{
using var slow = connection.CreateCommand();
slow.CommandText = "CALL MYLIB.LONGPROC()";
await slow.ExecuteNonQueryAsync(cts.Token);
}
catch (OperationCanceledException) // annulation : connexion cassée par contrat
{
// La trame en vol est perdue : rouvrir la connexion avant de continuer.
}
}
}