QTEMP-Konfliktmanagement auf IBM i in C# mit NTi

Dieser Artikel befasst sich hauptsächlich mit den Komplikationen, die bei der Interaktion zwischen modernen Programmen (z. B. Anwendungen in C#) und Legacy-Systemen wie IBM i auftreten, wo alte Funktionen wie QTEMP weiterhin isoliert von anderen Komponenten des Systems funktionieren.

Illustrationsbild zu Artikel

Einleitung

Die IBM i ist das beste Informationssystem, und das muss nicht mehr bewiesen werden. Aber das Bestreben, weiterhin alle Techniken der Vorgängermaschinen zu unterstützen, zwingt ihn dazu, Ressourcen zu unterstützen, die zu einer bestimmten Zeit sehr nützlich waren, heute aber zu Seltsamkeiten geworden sind.

Ein Beispiel dafür ist die QTEMP-Bibliothek, die von AS400 und S/38 übernommen wurde. Ich möchte nicht im Detail auf ihre Funktionsweise eingehen; Sie sollten nur wissen, dass sie systematisch beim Start eines jeden Jobs erstellt und wieder gelöscht wird, wenn der Job beendet wird. QTEMP gehört ausschließlich dem Job, der es erstellt hat, und nur diesem, und es kann nur von dem, was in diesem Job ausgeführt wird, erreicht werden.

Während die Dinge bei einer AS400-Nutzung der IBM i durch die Subsysteme QINTER und QBATCH klar sind, wird es kompliziert, wenn die Nutzung der Ressourcen der IBM i über das Subsystem QUSRWRK durch Jobs der Typen QZDASOINIT und QZRCSRVS erfolgt. Ich werde dies anhand eines Beispiels in C# erläutern.

Code-Beispiel

Die folgende C#-Codefolge liefert genau das gleiche Ergebnis: das Duplizieren einer Tabelle in QTEMP, wobei TARTICL1 durch einen CPYF-Befehl und dann TARTICL2 durch einen SQL CREATE TABLE-Befehl erstellt werden (dieser Code verwendet natürlich NTi Data Provider).

Try
{
   cx.ExecuteClCommand("CPYF FROMFILE(AIRELLES/TARTICL) TOFILE(QTEMP/TARTICL1) MBROPT(*REPLACE) CRTFILE(*YES)"); 
  
   cx.Execute("CREATE TABLE QTEMP.TARTICL2 AS (SELECT * FROM AIRELLES.TARTICL) WITH DATA");
}
catch (Exception ex)
{
   Console.WriteLine(ex.ToString());
}

💡 Auf dem letzten Bracket wurde ein Haltepunkt gesetzt.

Zur Laufzeit, wenn der Prozess den Haltepunkt erreicht, stellen wir auf der IBM i mit dem Befehl WRKACTJOB SBS(QUSRWRK) fest, dass zwei Jobs in QUSRWRK vorhanden sind: QZDASOINIT und QZRCSRVS.

article 11

QZZDASOINIT ist Datenbankoperationen über SQL gewidmet.

QZRCSRVS unterstützt alle typischen AS400-Operationen wie CL-Befehle, Programme usw.

Betrachtung der Struktur dieser beiden Jobs.

Nachfolgend die Liste der Bibliotheken von QZDASOINIT:

article 11

Und die von QZRCSRVS:

article 11

In beiden Fällen stellen wir fest, dass eine QTEMP-Bibliothek vorhanden ist.

Betrachten wir den Inhalt dieser Bibliotheken durch die Option 5 von WRKACTJOB.

Die QTEMP-Bibliothek von QZDASOINIT enthält die Datei TARTICL2:

article 11

QTEMP von QZRCSRVS enthält die Datei TARTICL1 :

article 11

Folgen

Hier wird es kompliziert.

  • Wenn ich die SQL-Abfrage ausführe:
select * from qtemp.tarticl1

⚠️ Ich erhalte die Meldung « Datei TARTICL1 in der QTEMP-Bibliothek nicht gefunden ».

  • Wenn ich den CL-Befehl ausführe
CPYF FROMFILE(QTEMP/TARTICL2) TOFILE(QGPL/TARTICL2) MBROPT(*REPLACE) CRTFILE(*YES)**

⚠️ Ich erhalte die Meldung « Datei TARTICL2 in der QTEMP-Bibliothek nicht gefunden ».

  • Wenn ich die SQL-Abfrage ausführe :
select * from qtemp.tarticl2

Alles endet normal.

  • Wenn ich den Befehl CL :
CPYF FROMFILE(QTEMP/TARTICL1) TOFILE(QGPL/TARTICL2) MBROPT(*REPLACE) CRTFILE(*YES)

Es liegt kein Fehler vor.

Das Problem rührt daher, dass eine SQL-Abfrage die QTEMP-Bibliothek anspricht, die mit QZDASOINIT verknüpft ist, während Programme, Befehle und andere reine AS400-Ressourcen die QTEMP des QZRCSRVS-Jobs verwenden.

Da die Verwendung der QTEMP-Bibliothek etwas aus der Mode gekommen ist, wird sie sehr oft von älteren Anwendungen (vor der Verbreitung von SQL) verwendet. Die Nutzdaten befinden sich fast immer in der QTEMP von QZRCSRVS, sind also für eine SQL-Abfrage nicht zugänglich.

Sie haben verstanden, wenn Sie modernisieren wollen, indem Sie bestehende RPG, COBOL, CLP Programme in .NET wiederverwenden, werden Sie auf Fälle stoßen, in denen das nicht funktioniert. Sie können sich damit behelfen, indem Sie ein CLP-Programm verwenden, das die Datei in die richtige QTEMP einliest und Ihnen die Daten in einem C#-Programm zurückgibt. Aber nur für dieses Problem müssen Sie Ihrem .NET-Entwickler beibringen, wie man CL-Code schreibt.

Eine Lösung

Die folgende C#-Funktion erhält als Parameter die Definition der zuvor hergestellten Verbindung zum IBM i (cx), die auszuwertende QTEMP-Datei (QtempFile) und gibt die Daten in einer DataTable zurück. Man kann auch andere Mittel verwenden, um die Daten zu empfangen.

Um das Schreiben der Kommentare zu vereinfachen, werde ich die folgenden Notationen verwenden, um auszudrücken, an welche QTEMP-Bibliothek die Ausführung einer Anweisung gerichtet ist:

  • QZDASOINIT/QTEMP.
  • QZRCSRVS/QTEMP.

Diese Funktion wird die gewünschte Datei von QZRCSRVS/QTEMP nach QZDASOINIT/QTEMP verschieben, damit sie über eine SQL-Abfrage zugänglich wird.

public static DataTable QtempDt (NTiConnection cx, string QtempFile)
{
    var adapter = new NTiDataAdapter(); // Definition eines DataAdapters
    DataTable dt = new DataTable(); // Definition der DataTable Ergebnis
    var cmd = cx.CreateCommand(); // Befehlsdefinition in der NTi-Verbindung

    cmd.CommandText = "SELECT * FROM QTEMP." + QtempFile; // Zusammensetzung der SQL-Abfrage mit dem Dateinamen
    adapter.SelectCommand = cmd; // Zuordnung des Befehls zum DataAdapter

    // ** Die Daten werden mithilfe einer SQL-Abfrage abgerufen, daher wird die Datei QtempFile in der Bibliothek QTEMP gesucht, die mit dem Job QZDASOINIT verknüpft ist.

    try
    {
        // Wenn die gesuchte Datei auf dem IBM i durch eine SQL-Abfrage erstellt wurde, wird die folgende Anweisung erfolgreich ausgeführt (die Datei QtempFile befindet sich in QZDASOINIT/QTEMP). 
        // Daten werden in die DataTable integriert 
        adapter.Fill(dt); 
    }
    catch 
    {
    // Andernfalls müssen Sie die Datei nach QZRCSRVS/QTEMP kopieren.
    // Hierfür benötigen wir eine Zwischenbibliothek.  
    // Hier erstelle ich zur Vermeidung von Duplikaten eine Bibliothek, deren Name sich aus der Verkettung der Zeichen  « LB » und der aktuellen Uhrzeit (HHMMSS) zusammensetzt. 
    // Anders geht es mit Random-Funktionen

        string BibTemporaire = "LB" +         
        DateTime.Now.ToString().Substring(11).Replace(":", "");
        cx.ExecuteClCommand("CRTLIB LIB(" + BibTemporaire + ")");
        
        // Verwendet QZRCSRVS/QTEMP: die Datei wird in die temporäre Bib kopiert
        cx.ExecuteClCommand("CPYF FROMFILE(QTEMP/" + QtempFile + ") TOFILE(" +     
        BibTemporaire + "/" + QtempFile + ") MBROPT(*REPLACE) CRTFILE(*YES)");
        
        // Wird die Datei mit einer Kopie der Daten in QZDASOINIT/QTEMP erstellt. Auf diese QTEMP kann über eine SQL-Abfrage zugegriffen werden
        cx.Execute("CREATE TABLE QTEMP." + QtempFile + " AS (SELECT * FROM " + 
        BibTemporaire + "." + QtempFile + ") WITH DATA");
  
        // Die temporäre Bibliothek wird abgeschafft
        cx.ExecuteClCommand("DLTLIB LIB(" + BibTemporaire + ")");

        // Daten werden in die DataTable integriert
        adapter.Fill(dt);

        // Die Datei QtempFile gelöscht wird in 
        cx.Execute("Drop table qtemp." + QtempFile);
    }

    // Die DataTable wird zurückgegeben
    return dt;
}

Dieses Programm verwendet natürlich den NTi Data Provider Connector.

Seine schnelle Ausführung ermöglicht es, diese Folge von Anweisungen einzufügen, ohne die Antwortzeiten zu benachteiligen.

Bonus

Im Laufe dieses Artikels haben wir die Jobs QZZDASOINIT und QZZRCSRVS besprochen. Bei diesen Jobs handelt es sich normalerweise um sogenannte Frühstartjobs. Das heißt, dass sie nach dem Start im Zustand « WAIT » bleiben, verfügbar, um eine neue Anfrage an die IBM i aufzunehmen. Diese Arbeiten werden von ODBC, OleDb, ACS und vielen anderen, wie einem gewissen « NTi Data Provider », angefordert. Dieser « WAIT »-Zustand ist sehr nützlich, um die Langsamkeit von Tools außerhalb der IBM i auszugleichen: Der Job ist in der IBM i bereit, er muss nur durch eine Anfrage von einem Drittprodukt geweckt werden.

Der folgende Screenshot zeigt die beiden Jobs, die für eine Abfrage aus einem C#-Programm, das OleDb verwendet, verwendet wurden:

article 11

Beide Jobs befinden sich im Zustand « TIMW » und sind bereit, eine neue Anfrage aufzunehmen. Das Problem ist, dass die Bibliothek QTEMP der vorherigen Anfrage immer noch vorhanden ist... Ich überlasse es Ihnen, selbst auf die Ungereimtheiten zu schließen, die sich daraus ergeben könnten.

Normalerweise verschwinden diese Jobs nach einer gewissen Zeit, in der sie nicht benutzt werden, wieder. Manchmal bleiben diese Jobs jedoch auf unbestimmte Zeit im Zustand « TIMW », ohne wieder verwendet zu werden; dann gibt es einen Stapel von Zeilen in QUSRWRK, von denen man nicht weiß, was man tun soll: sie beenden, sie aufbewahren... Auch das Nachschlagen auf Internetseiten bringt keine Klarheit.

Dieses Risiko besteht bei NTi Data Provider nicht, da die von NTi gestarteten QZDASOINIT- und QZRCSRVS-Jobs niemals in den Zustand « TIMW » oder « PSRW » wechseln, wenn die C#-Sitzung beendet wird. Sie werden systematisch zerstört, selbst bei einem abrupten Abbruch des laufenden Programms (und somit jedes Mal neu gestartet und zurückgesetzt). Es besteht keine Gefahr, dass sie später wiederverwendet werden.

Und dieser Neustart hat keinerlei Auswirkungen auf die Leistung von .NET mit NTi.

Laurent Rouillot