Zum Inhalt

Verbindung und Datenzugriff

Diese Seite beschreibt den vollständigen Workflow von der Anmeldung an die microtech Software über COM bis zum Lesen und Schreiben von Datensätzen. Nach dieser Seite kennen Sie die zentralen Konzepte Application, DataSet, Fields und Indices und können eigenständig auf beliebige Tabellen zugreifen.

Verbindung herstellen

Init und SelectMand

Jedes COM-Programm beginnt mit zwei Schritten: dem Erzeugen des Application-Objekts und der Anmeldung per Init, gefolgt von der Mandantenauswahl per SelectMand.

Das Application-Objekt ist das einzige Objekt, das Sie selbst instanzieren müssen - alle weiteren Interfaces werden über dessen Methoden erzeugt.

Die ProgID lautet BpNT.Application.

VisualBasic
Set BpApp = CreateObject("BpNT.Application")
BpApp.Init "Ihr Firmenname", "", "Ihr Anmeldename", "Ihr Passwort"
BpApp.SelectMand "1"
C#
BpNT.Application BpApp = new BpNT.Application();
BpApp.Init("Ihr Firmenname", "", "Ihr Anmeldename", "Ihr Passwort");
BpApp.SelectMand("1");

Die Parameter von Init im Überblick:

Parameter Beschreibung
ConnectionName Ihr Firmenname - exakt wie unter Datei > Hilfe > Support & Hotline angezeigt. Groß-/Kleinschreibung und Leerzeichen beachten.
ConnectionKey Bleibt für reguläre Anwender leer (""). Nur Solution-Partner übergeben hier ihren Key.
UserName Benutzername in der microtech Software.
UserPass Passwort des Benutzers.

SelectMand erwartet die Mandantennummer als String. In der Regel ist die Mandantennummer numerisch (z. B. "1"), sie kann aber auch alphanumerisch sein.

Wichtig

Init ist immer der erste Aufruf. Ohne erfolgreichen Init schlägt jeder weitere Aufruf fehl.

Solution-Partner-Anbindung

Solution-Partner verwenden einen abweichenden Init-Aufruf, bei dem ConnectionName den Partner-Namen und ConnectionKey den Partner-Schlüssel enthält:

Solution-Partner Init
BpApp.Init "Solution Partner Name", "Solution Partner Key", "Anmeldename", "Passwort"

Darüber hinaus steht die Methode AddActivation zur Verfügung, um den endgültigen Aktivierungsschlüssel einer Solution-Partner-Lösung per COM zu übertragen:

AddActivation (C#)
BpNT.Application ComApp = new BpNT.Application();
try
{
    ComApp.Init(aConnectionName, aConnectionKey, aUserName, aUserPass);
    // Endgültig aktivieren - nicht mehrfach aufrufen!
    ComApp.AddActivation("Beispiel GmbH", "00000", "Key");
}
finally
{
    System.Runtime.InteropServices.Marshal.ReleaseComObject(ComApp);
}

Hinweis

AddActivation darf nur wenige Male aufgerufen werden. Bei wiederholten fehlerhaften Aufrufen wird die Funktion stark verzögert ausgeführt. Firmenname und Postleitzahl können nur beim ersten Mal frei gewählt werden; danach müssen die bereits eingetragenen Werte übergeben werden.

Mandantenliste abfragen

Über die Eigenschaft MandList erhalten Sie nach SelectMand ein AutoDataSet mit allen vorhandenen Mandanten:

Mandantenliste (Delphi)
BpTbl := BpApp.MandList;
MessageBox(BpTbl.Fields['MandNr'].AsString);

Konfigurationsdaten lesen und schreiben

Die microtech Software bietet drei Ebenen, um eigene Konfigurationsdaten als Key-Value-Paare zu speichern:

Eigenschaft Gültigkeitsbereich
Konfiguration Mandantenspezifisch und COM-Anbindung-spezifisch
BzrKonfiguration Benutzerspezifisch und COM-Anbindung-spezifisch
GloKonfiguration Global über den gesamten microtech-Datenbestand

Speicherort der Konfigurationsdaten

Bitte beachten Sie, dass die Werte immer innerhalb Ihrer COM Anbindung gespeichert werden.

Alle drei werden identisch als Property mit einem String-Schlüssel angesprochen:

Konfigurationsdaten (Delphi)
BpApp := CreateOleObject('BpNT.Application');
BpApp.Init('Firmenname', '', 'Anmeldename', 'Password');
BpApp.SelectMand('1');

// Schreiben
BpApp.Konfiguration['Wert1']    := 'beliebiger String';
BpApp.BzrKonfiguration['Wert2'] := 'beliebiger String';
BpApp.GloKonfiguration['Wert3'] := 'beliebiger String';

// Lesen
ShowMessage(BpApp.Konfiguration['Wert1']);
ShowMessage(BpApp.BzrKonfiguration['Wert2']);
ShowMessage(BpApp.GloKonfiguration['Wert3']);

Tipp

Halten Sie die gespeicherte Datenmenge möglichst gering.

Verbindung trennen: LogOff und DeInit

Zum Trennen der Verbindung stehen zwei Methoden zur Verfügung:

LogOff schließt die Datenbankverbindung zum aktuellen Mandanten, hält aber die Benutzeranmeldung aufrecht. Danach kann direkt SelectMand mit einem anderen Mandanten aufgerufen werden - ein erneuter Init ist nicht nötig.

DeInit beendet die gesamte Verbindung, einschließlich Benutzeranmeldung. Danach sind sowohl Init als auch SelectMand erneut erforderlich. Verwenden Sie DeInit, wenn Sie mit einem anderen Benutzer weiterarbeiten möchten.

Benutzerwechsel mit DeInit (C#)
class Program
{
    static BpNT.Application ComApp = null;

    static void Main(string[] args)
    {
        try
        {
            ComApp = new BpNT.Application();
            ComApp.Init("Firmenname", "", "BenutzerA", "");
            ComApp.SelectMand("1");

            // ... Arbeit als BenutzerA ...

            ComApp.DeInit();

            ComApp.Init("Firmenname", "", "BenutzerB", "");
            ComApp.SelectMand("1");

            // ... Arbeit als BenutzerB ...
        }
        finally
        {
            ComApp = null;
        }
    }
}
Mandantenwechsel mit LogOff (Delphi)
BpApp.Init('ConnectionName', 'ConnectionKey', 'UserName', 'UserPassword');
BpApp.SelectMand('1');
// ... Arbeit in Mandant 1 ...
BpApp.LogOff;
BpApp.SelectMand('2');
// ... Arbeit in Mandant 2 ...

Verbindungsstabilität: RequestRetryCount und RequestTimeOut

Bei Datenoperationen (z. B. Post) kann es vorkommen, dass der Datenserver beschäftigt ist. Über RequestRetryCount und RequestTimeOut steuern Sie, wie die COM-Instanz damit umgeht:

Eigenschaft Beschreibung Standardwert Wertebereich
RequestRetryCount Anzahl der Wiederholungsversuche 100 0–n
RequestTimeOut Zeit in Millisekunden zwischen den Versuchen - 500–5000

Wird RequestRetryCount auf 0 gesetzt, wird bei einem fehlgeschlagenen Versuch kein Dialog angezeigt und sofort eine Exception ausgelöst. Das ist nützlich, wenn Ihre Anwendung die Fehlerbehandlung selbst übernehmen soll - etwa in Hintergrunddiensten, denen kein UI-Fokus entzogen werden darf.

Eigene Retry-Logik (Pseudocode)
Application.RequestTimeOut = 2000
Application.RequestRetryCount = 0

while TransactionFailed oder Retry < 10 do
    TransactionFailed = DoTransaction
    Retry = Retry + 1
end while

Serverstatus prüfen: GetServerState und GetMandState

Für lang laufende COM-Anwendungen ist es wichtig, den Status des Datenservers und des Mandanten regelmäßig zu prüfen.

GetServerState fragt den aktuellen Status des Datenservers ab.

GetMandState gibt einen Statuscode zurück, der anzeigt, ob die Anwendung sich neu anmelden muss:

Rückgabewert Bedeutung Aktion
0 OK, keine Aktion erforderlich -
1 Tageswechsel (Mandantendatum hat sich geändert) Verbindung trennen und neu herstellen
2 Parameteränderung durchgeführt Am Mandanten erneut anmelden
GetMandState Polling (C#)
BpNT.Application bpApp = new BpNT.Application();
bpApp.Init("ConnName", "", "User", "Pass");
bpApp.SelectMand("1");

while (true)
{
    int mandState = bpApp.GetMandState();
    switch (mandState)
    {
        case 1:
            Console.WriteLine("Tageswechsel - Reconnect erforderlich");
            Reconnect();
            break;
        case 2:
            Console.WriteLine("Parameteränderung - Reconnect erforderlich");
            Reconnect();
            break;
        default:
            break;
    }
}

Datensätze öffnen und navigieren

DataSetInfos und CreateDataSet

Bevor Sie auf Daten zugreifen können, müssen Sie eine Tabelle öffnen. Dazu nutzen Sie die Eigenschaft DataSetInfos des Application-Objekts, die eine Liste aller verfügbaren Tabellen enthält.

Über DataSetInfos greifen Sie per Name oder Index auf ein DataSetInfo-Objekt zu, das Metainformationen über die Tabelle bereitstellt. Die Methode CreateDataSet erzeugt daraus ein AutoDataSet - das zentrale Interface zum Lesen und Schreiben von Daten.

Tabelle öffnen (Delphi)
DataSet := BpApp.DataSetInfos['Adressen'].CreateDataSet;
Tabelle öffnen (VB)
Set DataSet = BpApp.DataSetInfos.Item("Adressen").CreateDataSet

DataSetInfo bietet zudem Metainformationen über die Tabelle:

Eigenschaft Beschreibung
Name Name der Tabelle
OrgName Interner Tabellenname (verwendbar für CreateDataSet)
Bez Bezeichnung / Beschreibung der Tabelle
Fields Zugriff auf AutoFieldInfos (Metainformationen der Felder)
Indices Zugriff auf AutoIndexInfos (Metainformationen der Indizes)
NestedDataSets Zugriff auf AutoNestedDataSetInfos (verschachtelte Tabellen)
isNested True, wenn die Tabelle ein NestedDataSet ist

CreateDataSet vs. CreateDataSetEx: Neben CreateDataSet existiert die Variante CreateDataSetEx, die eine Tabelle mit erweiterter Feldliste erstellt. Bei der Adressen-Tabelle z. B. sind dann auch alle Felder der Rechnungs- und Lieferadresse direkt verfügbar, ohne diese umständlich über Ranges zusammenstellen zu müssen. Vereinfacht gesagt wird die Tabelle um ihre verknüpften Tabellen erweitert.

CreateDataSetFromId: Wenn Sie einen IDString eines Datensatzes besitzen (z. B. aus einer früheren Sitzung), können Sie über BpApp.CreateDataSetFromId(idString) ein DataSet erzeugen, das direkt auf dem entsprechenden Datensatz positioniert ist.

Ein geöffnetes AutoDataSet verhält sich wie ein Cursor, der auf einen Datensatz zeigt. Mit den Navigationsmethoden bewegen Sie diesen Cursor:

Methode / Eigenschaft Beschreibung
First Springt zum ersten Datensatz
Last Springt zum letzten Datensatz
Next Bewegt den Cursor einen Datensatz vorwärts
Prior Bewegt den Cursor einen Datensatz zurück
EOF True, wenn das Ende der Tabelle erreicht ist
BOF True, wenn der Anfang der Tabelle erreicht ist
RecordCount Gibt die Anzahl der Datensätze zurück
IsEmpty True, wenn die Tabelle keine Datensätze enthält
Bookmark Eindeutiger Bezeichner des aktuellen Datensatzes zum späteren Wiederauffinden

Die typische Schleife zum Durchlaufen aller Datensätze:

Alle Datensätze durchlaufen (VB)
Set DataSet = BpApp.DataSetInfos.Item("Adressen").CreateDataSet
DataSet.First
Do While DataSet.EOF = False
    Debug.Print DataSet.Fields("AdrNr").AsString
    DataSet.Next
Loop

Die Eigenschaft Backward kehrt die Sortierreihenfolge um - First ist dann der letzte und Last der erste Datensatz gemäß der ursprünglichen Sortierung.

Suchen: SetKey/GotoKey und FindKey

Um gezielt zu einem bestimmten Datensatz zu springen, gibt es zwei Ansätze:

SetKey / GotoKey (klassisch): Sie rufen SetKey auf, beschreiben anschließend die Index-Felder mit den Suchwerten und rufen GotoKey auf. Der Rückgabewert ist True, wenn der Datensatz gefunden wurde.

SetKey / GotoKey (Delphi)
DataSet.Indices['Nr'].Select;
DataSet.SetKey;
DataSet.Fields['Nr'].AsString := '12345';
if not DataSet.GotoKey then
    ShowMessage('Datensatz nicht gefunden');

Wichtig

Bei GotoKey müssen alle Felder des aktiven Index mit einem Wert belegt werden - auch solche, für die Sie keinen Suchwert haben. Setzen Sie diese auf einen Leerstring: Fields['ZweitesFeld'].AsString := ''. Die Eigenschaft KeyFieldCount gibt Ihnen die Anzahl der Felder im aktuellen Index zurück.

GotoNearest funktioniert wie GotoKey, fährt aber immer eine Position an - auch wenn kein exakter Treffer existiert. Es wird der nächstliegende Datensatz angesprungen.

FindKey (ab IAutoDataSet13): Eine kompaktere Alternative. FindKey erwartet den Index-Namen und die Suchwerte direkt als Parameter:

FindKey (Pseudocode)
' Ein Suchfeld:
DataSet.FindKey("Nr", "12345")

' Mehrere Suchfelder (als Varianten-Array):
DataSet.FindKey("RefWert", Array("Computer", "3"))

Der Rückgabewert ist True bei einem Treffer, False wenn kein Datensatz gefunden wurde.

Ranges setzen und anwenden

Ein Range schränkt die sichtbaren Datensätze auf einen Bereich innerhalb des aktuellen Index ein. Nach dem Setzen eines Range verhält sich die Tabelle so, als enthielte sie nur die Datensätze innerhalb dieses Bereichs - RecordCount, First, Next usw. beziehen sich dann nur auf diesen Ausschnitt.

Der klassische Ablauf:

  1. SetRangeStart - danach die "Von"-Werte in die Index-Felder schreiben
  2. SetRangeEnd - danach die "Bis"-Werte in die Index-Felder schreiben
  3. ApplyRange - den Range aktivieren
  4. CancelRange - den Range wieder aufheben
Range auf Adressen (Delphi)
bpTbl := BpApp.DataSetInfos('Anschriften').CreateDataSet;
bpTbl.Indices.Item('AdrNrAnsNr').Select;

bpTbl.SetRangeStart;
bpTbl.Fields('AdrNr').AsString := '10000';
bpTbl.KeyFieldCount := 1;

bpTbl.SetRangeEnd;
bpTbl.Fields('AdrNr').AsString := '10000' + 'ZZZZZ';
bpTbl.KeyFieldCount := 1;

bpTbl.ApplyRange;

Über KeyFieldCount können Sie die Anzahl der für den Range genutzten Index-Felder einschränken. Ein Index mit drei Feldern kann z. B. auf KeyFieldCount := 1 gesetzt werden, sodass nur das erste Feld für den Range relevant ist.

Achtung bei gemischten Datentypen

Wenn ein Index aus Strings und numerischen Werten besteht und Sie per KeyFieldCount auf die String-Felder reduzieren, steht nur noch der String-Vergleich zur Verfügung. Start- und Endwert müssen sich dann unterscheiden - hängen Sie z. B. 'ZZZZZ' an den Endwert an.

SetRange (ab IAutoDataSet13): Eine kompaktere Alternative, die Index-Name, Start- und Endwerte in einem Aufruf vereint:

SetRange
' Alle Artikel, die mit "1" anfangen:
DataSet.SetRange("Nr", "1", "1Z")

' Vorgänge ab dem 1.1.2017 (offenes Ende):
DataSet.SetRange("Dat", "01.01.2017", Null)

Mit EditRangeStart und EditRangeEnd können Sie einen bestehenden Range nachträglich ändern, ohne ihn komplett neu setzen zu müssen.

Die Eigenschaft IsRanged gibt True zurück, wenn aktuell ein Range aktiv ist.

Filter verwenden

Neben Ranges können Sie Datensätze auch über einen Filter einschränken. Im Unterschied zu Ranges sind Filter nicht an einen Index gebunden, sondern arbeiten mit einem mDL-Filterausdruck.

Eigenschaft Beschreibung
Filter Der Filterausdruck als String (mDL-Syntax)
Filtered True / False - schaltet den Filter ein oder aus

Setzen Sie zuerst die Eigenschaft Filter mit dem gewünschten Ausdruck und aktivieren Sie den Filter anschließend über Filtered := True. Die Details zur mDL-Syntax finden Sie auf der Seite mDL.

WildcardRange (ab IAutoDataSet10): Für einfache Wildcard-Suchen steht eine eigene Methode bereit, die intern einen optimierten Range mit Platzhaltern setzt.

Felder lesen und schreiben

Feldzugriff über Fields und AsString/AsFloat/…

Auf die Felder eines Datensatzes greifen Sie über die Eigenschaft Fields des AutoDataSet zu. Fields ist eine Sammlung von AutoField-Objekten, die per Name oder Index angesprochen werden:

DataSet.Fields['AdrNr'].AsString
DataSet.Fields.Item("AdrNr").AsString
DataSet.Fields(0).AsString

Jedes AutoField bietet typisierten Zugriff über verschiedene Properties:

Property Datentyp Beschreibung
AsString WideString/BSTR Standard-Stringzugriff
AsInteger Long Ganzzahliger Zugriff
AsFloat Double Gleitkomma-Zugriff
AsBoolean Boolean Ja/Nein-Werte
AsCurrency Currency Währungsbeträge
AsDateTime Date Datum/Uhrzeit
AsVariant Variant Generischer Zugriff
Text WideString/BSTR Wie AsString, übernimmt aber Eingabelogik (z. B. Länderkennungen, Datumsangaben)

Text vs. AsString

Text bietet gegenüber AsString den Vorteil, dass es die Eingabelogik der microtech Software übernimmt. Schreiben Sie z. B. "Überweisung" in ein Zahlungsart-Feld per Text, wird automatisch der korrekte interne Wert (z. B. 9) gespeichert. Dieser Service steht für Felder zur Verfügung, die in Eingabemasken der Software verwendet werden.

Weitere nützliche Eigenschaften eines AutoField:

Eigenschaft Beschreibung
Name Feldname zur Identifizierung
Info Langbeschreibung des Feldes (z. B. "Adressnummer")
FieldType Datentyp des Feldes als String (z. B. "String", "Integer", "DateTime")
IsCalcField True, wenn der Feldinhalt berechnet wird
IsNull True, wenn der Inhalt leer oder 0 ist
CanAccess True, wenn eine Zugriffsberechtigung besteht
Clear Löscht den Feldinhalt

Schreibschutz einzelner Felder (versionsabhängig)

Gen. 24

Ob ein Feld über COM beschreibbar ist, ist keine feste Eigenschaft - einzelne Felder können intern mit einem COM-Schreibschutz versehen sein, der im Zuge der Weiterentwicklung wieder aufgehoben werden kann. Ein Beispiel ist das Feld DBKuNa der Tabelle Regeln: Es legt fest, welchem Bereich (z. B. "Eigene Abläufe", "Adressen", "Vorgänge" ...) eine Regel zugeordnet ist. Bislang musste dieser Bereich beim automatisierten Anlegen einer Regel per COM manuell vom Anwender vorgegeben werden; DBKuNa kann jetzt direkt beim Anlegen mitgeschrieben werden.

Voraussetzung

Diese Erweiterung steht nur mit aktiver Programmgeneration 24 (oder neuer) und aktivierter COM-Schnittstelle zur Verfügung. Auf älteren Programmständen bleibt das Feld weiterhin schreibgeschützt.

Prüfen Sie bei ähnlichen Fällen die Eigenschaft CanAccess (siehe Tabelle oben) bzw. testen Sie den Schreibzugriff mit der aktuellen Programmversion, statt sich auf eine einmal dokumentierte Schreibbarkeit zu verlassen.

Spezielle Feldtypen: BetragGrp, RichText, Image, Strings

Einige Felder enthalten keine einfachen Werte, sondern komplexe Datenstrukturen. Der Zugriff erfolgt über GetEditObject oder spezialisierte Interfaces:

BetragGrp (Betragsgruppen): Felder mit der FeldDefArt für Betragsgruppen enthalten Netto-/Brutto-/Steuer-Strukturen. Über GetEditObject erhalten Sie ein IAutoBetragGrp-Interface mit Properties wie Netto, Brutto, Steuer, GesBrutto, GesNetto, GesSteuer, IstBrutto, IstSoll, Save, Clear und Add. Detaillierte Signaturen finden Sie in der Interface-Referenz.

RichText: Für RTF-Felder gibt es zwei Wege:

  • LoadMemo auf dem AutoField - lädt RTF-Inhalte über die Rich-Edit-DLL.
  • AsDataString (ab IAutoField3) - bietet direkten und schnellen Zugriff auf den RTF-Quelltext ohne den Umweg über die Rich-Edit-DLL.

Image: Über GetEditObject erhalten Sie ein IAutoImage-Interface zum Laden und Speichern von Bildern. Die wichtigsten Methoden: LoadFromFile, SaveToFile, LoadFromStream, SaveToStream, Clear, ImageType und LinkFileName. Detaillierte Signaturen finden Sie in der Interface-Referenz.

Strings (IAutoStrings): Dieses Interface dient dem Zugriff auf String-Listen, die in bestimmten Feldern gespeichert sind. Es bietet Methoden wie Add, Delete, Clear, Count und indizierten Zugriff auf einzelne Einträge.

Funktionen zum schnellen Feldzugriff

Ab IAutoDataSet13 stehen Methoden für den blockweisen Zugriff auf Feldwerte zur Verfügung. Diese sind besonders bei großen Datenmengen deutlich performanter als der Einzelfeldzugriff:

Methode Beschreibung
AcquireFieldList Erstellt eine Feldliste für den Blockzugriff
ReleaseFieldList Gibt die Feldliste wieder frei
ReadFieldListValues Liest die Feldwerte aller Felder in der Liste auf einmal
ReadFieldListValuesChunk Liest einen ganzen Block von Datensätzen
WriteFieldListValues Schreibt die Feldwerte aller Felder in der Liste auf einmal

Der typische Ablauf: Zuerst AcquireFieldList aufrufen, um die gewünschten Felder zu registrieren. Dann per ReadFieldListValues oder ReadFieldListValuesChunk lesen (bzw. WriteFieldListValues schreiben). Am Ende ReleaseFieldList zum Freigeben aufrufen.

Detaillierte Signaturen und Beispiele finden Sie in der Interface-Referenz.