Table of Contents

Generator aus der Anwendung starten

Liefern Sie bpnext-gen mit Ihrem Installer aus, startet Ihre Anwendung den Generator als eigenen Prozess. Ein vollständiger Lauf dauert je nach Instanz 5 bis 10 Minuten. Mit --json meldet der Generator Schritt, Fortschritt und aktuellen Datensatz maschinenlesbar, sodass Sie den Stand in Ihrer eigenen Oberfläche anzeigen können.

Zugangsdaten übergeben

Kennwort und Client-Secret gehören nicht auf die Kommandozeile, dort sind sie in der Prozessliste und in Setup-Logs sichtbar. --credentials-from liest die Werte stattdessen aus einer Datei oder von der Standardeingabe.

Das Format ist ein key=value je Zeile. Die Schlüssel entsprechen den Parametern ohne führendes --, Zeilen mit # am Anfang und Leerzeilen werden übergangen:

# Zugangsdaten für bpnext-gen
user=BENUTZER
password=KENNWORT
mandant=1
endpoint=https://<host>/microtech/erp/<tenant>/graphql/v1
client-id=<client-id>
client-secret=<client-secret>

Neben den Zugangsdaten sind alle Parameter mit Wert erlaubt, etwa out, license oder models-dll.

Aus einer Datei:

bpnext-gen generate --credentials-from C:\ProgramData\MeineAnwendung\zugang.txt

Von der Standardeingabe, angegeben als -:

Get-Content .\zugang.txt | bpnext-gen generate --credentials-from -

Der Generator liest die Standardeingabe bis zum Ende. Eine startende Anwendung schreibt die Zeilen also hinein und schließt den Strom danach, so landen die Werte weder in einer Datei noch in der Prozessliste. Das Beispiel unten zeigt das.

Liegt ein Wert mehrfach vor, gilt: Argument vor Datei vor Umgebungsvariable.

Fortschritt mit --json

Mit --json schreibt der Generator auf stdout ausschließlich JSON, ein Objekt je Zeile, in UTF-8. Farben, Fortschrittsbalken und Rückfragen entfallen. --json schließt --non-interactive ein: Fehlt ein Wert, endet der Lauf mit Exit-Code 6, statt auf eine Eingabe zu warten.

{"type":"start","version":"0.30.2","verb":"generate","ts":"2026-09-27T10:00:00+00:00","elapsedMs":50}
{"type":"step","stage":"com.schemas","message":"Erzeuge Schemas für 319 DataSets...","elapsedMs":1200}
{"type":"progress","stage":"com.schemas","current":12,"total":319,"item":"Artikel","elapsedMs":4100}
{"type":"log","level":"warn","message":"...","elapsedMs":4200}
{"type":"result","exitCode":0,"com":{"assembly":"C:\\...\\BpNexT.NET.Models.dll","dataSets":319,"sourceFiles":4383},"graphQl":{"assembly":"C:\\...\\BpNexT.NET.Models.GraphQL.dll","tables":135,"sourceFiles":1888,"typesInferred":true},"elapsedMs":412000}

Jedes Ereignis trägt type und elapsedMs, die Millisekunden seit Prozessstart.

type Felder Bedeutung
start version, verb, ts erste Zeile eines Laufs
step stage, message ein neuer Abschnitt beginnt
progress stage, current, total, item ein Element des laufenden Abschnitts ist erreicht
log level, message, bei --verbose auch exception Meldung; level ist debug, info, success, warn oder error
result exitCode, error, com, graphQl letzte Zeile, auch bei Fehlern

message ist für Menschen gedacht und kann sich ändern. Werten Sie Abschnitte über stage aus:

stage Abschnitt
license Lizenz prüfen
com.login Anmeldung an microtech ERP
com.schemas Schemas je DataSet lesen, mit Fortschritt
com.models Modelle erzeugen, mit Fortschritt
com.compile COM-Modelle übersetzen
graphql.introspect Schema vom GraphQL-Endpunkt lesen
graphql.models Modelle je Tabelle erzeugen, mit Fortschritt
graphql.compile GraphQL-Modelle übersetzen
check.com, check.graphql Prüfungen von bpnext-gen check

progress kommt für jedes Element, bei com.models also mehrere tausend Mal. Drosseln Sie die Anzeige in Ihrer Oberfläche, nicht das Lesen: ein nicht gelesener Ausgabestrom hält den Generator an.

result fehlt nur, wenn der Prozess hart abbricht. Maßgeblich bleibt der Exit-Code des Prozesses, com und graphQl fehlen für Stufen, die nicht gelaufen sind. Ist der Exit-Code 5, liegen die COM-Modelle trotzdem vollständig vor.

Beispiel

var startInfo = new ProcessStartInfo
{
    FileName = Path.Combine(AppContext.BaseDirectory, "bpnext-gen.exe"),
    ArgumentList = { "generate", "--json", "--credentials-from", "-" },
    RedirectStandardInput = true,
    RedirectStandardOutput = true,
    RedirectStandardError = true,
    StandardOutputEncoding = Encoding.UTF8,
    UseShellExecute = false,
    CreateNoWindow = true
};

using var process = Process.Start(startInfo)!;

await process.StandardInput.WriteLineAsync($"user={benutzer}");
await process.StandardInput.WriteLineAsync($"password={kennwort}");
await process.StandardInput.WriteLineAsync($"mandant={mandant}");
await process.StandardInput.WriteLineAsync($"endpoint={endpunkt}");
await process.StandardInput.WriteLineAsync($"client-id={clientId}");
await process.StandardInput.WriteLineAsync($"client-secret={clientSecret}");
process.StandardInput.Close();

var stderr = process.StandardError.ReadToEndAsync();

while (await process.StandardOutput.ReadLineAsync() is { } line)
{
    using var ev = JsonDocument.Parse(line);
    var root = ev.RootElement;

    switch (root.GetProperty("type").GetString())
    {
        case "step":
            ZeigeAbschnitt(root.GetProperty("stage").GetString(), root.GetProperty("message").GetString());
            break;
        case "progress":
            ZeigeFortschritt(
                root.GetProperty("current").GetInt32(),
                root.GetProperty("total").GetInt32(),
                root.GetProperty("item").GetString());
            break;
        case "result" when root.TryGetProperty("error", out var error):
            ZeigeFehler(error.GetString());
            break;
    }
}

await process.WaitForExitAsync();
await stderr;
var exitCode = process.ExitCode;

stderr wird mitgelesen, damit ein voller Fehlerstrom den Prozess nicht blockiert. Im JSON-Betrieb bleibt er leer, solange der Generator nicht unerwartet abbricht.