Connection Manager

Der Connection Manager (IWvdSConnectionManager) ist ein provider-agnostischer Connection Broker auf IHost. Er verwaltet benannte Datenverbindungen zentral für alle Add-ins. Ziel ist eine einheitliche Schnittstelle, über die jedes Add-in auf konfigurierte Datenquellen zugreift, ohne selbst Verbindungsparameter, Treibererkennung oder Instanz-Lebenszyklus verwalten zu müssen. Im Unterschied zu einer direkten Provider-Erzeugung im Add-in-Code liegt die gesamte Konfiguration in der user.config.json — Änderungen an Verbindungsparametern erfordern keinen Code-Eingriff. Der Vorteil besteht darin, dass alle Add-ins dieselbe Provider-Instanz teilen und der Host den Lebenszyklus kontrolliert.

Erreichbar über FHost.Connections.

Zurück zur Service-Referenz oder zur SDK-Übersicht.

Architektur

Der Connection Manager arbeitet in drei Phasen:

  1. Konfiguration lesen: Beim Start liest die Shell das connections-Array aus der user.config.json. Jede Verbindung hat einen Namen, einen Provider-Typ und ein Array mit provider-spezifischen Feldwerten.
  2. Factory-Registry: Für jeden Provider-Typ (mssql, rest, sqlite, etc.) existiert eine IWvdSProviderFactory. Der Host registriert eine eingebaute Factory für mssql (ODBC Driver 17/18). Add-ins können eigene Factories für weitere Provider-Typen registrieren.
  3. Lazy Singleton: Provider-Instanzen werden erst beim ersten GetConnection-Aufruf erzeugt. Danach liefert jeder weitere Aufruf mit demselben Namen dieselbe Instanz zurück. Add-ins erhalten Borrowed References — sie dürfen die Instanz nicht freigeben.
user.config.json                 IWvdSConnectionManager
┌──────────────────┐            ┌─────────────────────────┐
│ "connections": [ │──liest──▶  │ RegisterFactory()        │
│   { name, type,  │            │ GetConnection()          │──▶ Singleton-Provider
│     values }     │            │ GetConnectionNames()     │
│ ]                │            │ CheckInfrastructure()    │
└──────────────────┘            │ TestConnection()         │
                                └─────────────────────────┘

Warum zentral?

1. Eine Verbindung, nicht N. Wenn fünf Add-ins jeweils einen eigenen Provider erzeugen, entstehen fünf ODBC-Verbindungen zum selben Server. Die Shell hält pro benannter Verbindung exakt eine Singleton-Instanz. Das reduziert Ressourcenverbrauch und vermeidet Lizenzprobleme bei verbindungslimitierten Datenbanken.

2. Credential-Hoheit. Passwörter und API-Keys liegen in ISecretStorage (DPAPI-geschützt). Die Shell löst sie beim Verbindungsaufbau auf. Kein Add-in erhält jemals Klartext-Credentials — weder zur Laufzeit noch über die Konfigurationsdatei.

3. Einheitliches Verhalten. TrustCertificate, Timeout, Retry-Strategie, Connection-Monitoring: alles einmal in der Shell implementiert, nicht in jedem Add-in dupliziert. Änderungen am Verbindungsverhalten wirken sofort für alle Consumer.

4. Infrastruktur-Check. Die Shell prüft beim Start, ob die benötigten Treiber und Laufzeitumgebungen installiert sind (ODBC Driver 17/18, libsqlite3, TLS-Zertifikate). Das Ergebnis wird in der Settings-Oberfläche angezeigt — der Anwender sieht sofort, ob die Voraussetzungen erfüllt sind.

API

Hilfs-Records

type
  { Beschreibt ein Konfigurationsfeld eines Providers }
  TWvdSProviderField = record
    Name: string;         // Feldname, z.B. 'Server', 'Database'
    TypeName: string;     // Typbezeichnung, z.B. 'string', 'boolean', 'integer'
    Description: string;  // Beschreibung für UI/Dokumentation
    DefaultValue: string; // Standardwert als String
  end;
 
  { Ergebnis einer Infrastrukturprüfung }
  TWvdSInfraCheckResult = record
    Available: Boolean;      // True wenn der benötigte Treiber vorhanden ist
    DriverVersion: string;   // Erkannte Treiberversion, z.B. '17.10.6.1'
    Message: string;         // Meldung für den Benutzer
  end;
 
  { Ergebnis eines Verbindungstests }
  TWvdSConnectionTestResult = record
    Success: Boolean;     // True wenn die Verbindung hergestellt werden konnte
    LatencyMs: Integer;   // Antwortzeit in Millisekunden
    Message: string;      // Fehlermeldung oder Statustext
  end;

IWvdSProviderFactory

Eine Provider-Factory erzeugt Provider-Instanzen für einen bestimmten Typ. Der Host nutzt die Factory, um beim ersten GetConnection-Aufruf den passenden Provider zu erzeugen.

type
  IWvdSProviderFactory = interface
    { Gibt den Typ-Bezeichner zurück, z.B. 'mssql', 'rest', 'sqlite' }
    function GetProviderType: string;
 
    { Liefert die Felddefinitionen, die dieser Provider-Typ benötigt.
      Die Reihenfolge entspricht dem values-Array in der Konfiguration. }
    function GetConfigFields: TArray<TWvdSProviderField>;
 
    { Prüft ob die benötigte Infrastruktur vorhanden ist
      (z.B. ODBC-Treiber für mssql, libsqlite3 für sqlite). }
    function CheckInfrastructure: TWvdSInfraCheckResult;
 
    { Erzeugt eine Provider-Instanz mit den gegebenen Feldwerten.
      Die Reihenfolge der Werte entspricht GetConfigFields. }
    function CreateProvider(const AValues: TStringArray): TObject;
  end;
Methode Beschreibung
GetProviderType Gibt den Typ-Bezeichner zurück, z. B. „mssql“, „rest“ oder „sqlite“. Dieser Bezeichner wird in der user.config.json als provider-Feld verwendet.
GetConfigFields Liefert die Felddefinitionen, die dieser Provider-Typ benötigt. Für MSSQL sind das Server, Database, TrustedConnection, Encrypt, Timeout. Diese Felddefinitionen werden in der Settings-Oberfläche als Eingabefelder dargestellt.
CheckInfrastructure Prüft, ob die Laufzeitumgebung für diesen Provider-Typ vorhanden ist. Bei MSSQL wird geprüft, ob ein ODBC-Treiber (17 oder 18) installiert ist. Das Ergebnis wird beim Shell-Start abgefragt und in der Settings-Oberfläche angezeigt.
CreateProvider Erzeugt eine konkrete Provider-Instanz aus den Konfigurationswerten. Das zurückgegebene TObject wird vom Consumer auf den konkreten Typ gecastet (z. B. TWvdSMSSqlProvider).

IWvdSConnectionManager

Das zentrale Interface, erreichbar über FHost.Connections.

type
  IWvdSConnectionManager = interface
    { Registriert eine Factory für einen Provider-Typ.
      Gibt ein IDisposable zurück — an IExtensionContext übergeben. }
    function RegisterFactory(
      AFactory: IWvdSProviderFactory): IDisposable;
 
    { Liefert den Provider für eine benannte Verbindung.
      Erzeugt die Instanz beim ersten Aufruf (Lazy Singleton).
      Borrowed Reference — NICHT freigeben! }
    function GetConnection(const AName: string): TObject;
 
    { Füllt die Liste mit allen konfigurierten Verbindungsnamen. }
    procedure GetConnectionNames(AList: TStringList);
 
    { Prüft die Infrastruktur für einen Provider-Typ
      (z.B. ob der ODBC-Treiber installiert ist). }
    function CheckInfrastructure(
      const AProviderType: string): TWvdSInfraCheckResult;
 
    { Testet eine benannte Verbindung (Aufbau + einfache Query). }
    function TestConnection(
      const AName: string): TWvdSConnectionTestResult;
  end;
Methode Beschreibung
RegisterFactory Registriert eine Provider-Factory für einen bestimmten Typ. Gibt ein IDisposable zurück, das an IExtensionContext.Subscribe übergeben werden muss. Die Shell registriert die eingebaute mssql-Factory beim Start. Add-ins können eigene Factories nachregistrieren.
GetConnection Gibt die Provider-Instanz für die benannte Verbindung zurück. Beim ersten Aufruf wird der Provider über die zuständige Factory erzeugt und gecacht. Weitere Aufrufe liefern dieselbe Instanz (Singleton pro Name). Wichtig: Die zurückgegebene Referenz ist geborgt (Borrowed Reference) — das Add-in darf sie nicht freigeben. Gibt nil zurück, wenn der Name nicht konfiguriert ist.
GetConnectionNames Füllt die übergebene TStringList mit den Namen aller konfigurierten Verbindungen. Nützlich für dynamische UI-Elemente (z. B. Verbindungs-Auswahl in einem Dialog).
CheckInfrastructure Delegiert an die Factory des angegebenen Provider-Typs und gibt das Infrastruktur-Prüfergebnis zurück.
TestConnection Führt einen Verbindungstest für die benannte Verbindung durch. Erzeugt ggf. den Provider, baut die Verbindung auf und misst die Latenz. Das Ergebnis enthält Erfolg/Misserfolg, Latenz in Millisekunden und eine Diagnosemeldung.

Nutzung im Add-in

Ein Add-in greift über FHost.Connections auf eine konfigurierte Verbindung zu. Der zurückgegebene TObject wird auf den konkreten Provider-Typ gecastet.

var
  LProvider: TObject;
begin
  LProvider := FHost.Connections.GetConnection('mydb');
  if LProvider = nil then
  begin
    FHost.Log.Error('Verbindung "mydb" nicht konfiguriert');
    Exit;
  end;
 
  // Cast auf den konkreten Provider-Typ
  TWvdSSQLDBProvider(LProvider).FillWithParams(FDataSet, SSqlExecTree, []);
end;

Wichtig: Der Provider ist eine Borrowed Reference. Das Add-in darf Free darauf nicht aufrufen. Der Connection Manager verwaltet den Lebenszyklus — beim Herunterfahren des Hosts werden alle Provider-Instanzen automatisch freigegeben.

Eigene Provider-Factory registrieren

Add-ins können eigene Provider-Typen bereitstellen, indem sie eine IWvdSProviderFactory implementieren und beim Connection Manager registrieren.

type
  TMyRestFactory = class(TInterfacedObject, IWvdSProviderFactory)
  public
    function GetProviderType: string;
    function GetConfigFields: TArray<TWvdSProviderField>;
    function CheckInfrastructure: TWvdSInfraCheckResult;
    function CreateProvider(const AValues: TStringArray): TObject;
  end;
 
function TMyRestFactory.GetProviderType: string;
begin
  Result := 'rest';
end;
 
function TMyRestFactory.GetConfigFields: TArray<TWvdSProviderField>;
begin
  SetLength(Result, 3);
  Result[0].Name := 'BaseUrl';
  Result[0].TypeName := 'string';
  Result[0].Description := 'REST API Basis-URL';
  Result[0].DefaultValue := '';
 
  Result[1].Name := 'ApiKey';
  Result[1].TypeName := 'string';
  Result[1].Description := 'API-Schlüssel';
  Result[1].DefaultValue := '';
 
  Result[2].Name := 'Timeout';
  Result[2].TypeName := 'integer';
  Result[2].Description := 'Timeout in Sekunden';
  Result[2].DefaultValue := '30';
end;
 
function TMyRestFactory.CheckInfrastructure: TWvdSInfraCheckResult;
begin
  Result.Available := True;
  Result.DriverVersion := '';
  Result.Message := 'REST-Provider benötigt keine lokale Infrastruktur';
end;
 
function TMyRestFactory.CreateProvider(
  const AValues: TStringArray): TObject;
begin
  Result := TMyRestProvider.Create(
    AValues[0], AValues[1], StrToIntDef(AValues[2], 30));
end;

Registrierung im Activate:

procedure TMyPlugin.Activate(const AContext: IExtensionContext);
begin
  AContext.Subscribe(
    FHost.Connections.RegisterFactory(TMyRestFactory.Create)
  );
end;

Danach können alle Add-ins Verbindungen vom Typ rest über GetConnection abrufen, sofern eine entsprechende Verbindung in der user.config.json konfiguriert ist.

Konfiguration

Verbindungen werden in der user.config.json (~/.wvdsx/settings.json) im connections-Array konfiguriert. Jeder Eintrag hat drei Felder:

Feld Typ Beschreibung
name string Eindeutiger Verbindungsname, über den Add-ins zugreifen
provider string Provider-Typ (muss einer registrierten Factory entsprechen)
Weitere Felder je nach Provider Benannte Feldwerte gemäß GetConfigFields der zuständigen Factory

Beispiel: MSSQL-Verbindung

{
  "connections": [
    {
      "name": "WIS",
      "provider": "mssql",
      "server": "localhost",
      "database": "WIS",
      "integratedSecurity": "true",
      "trustCertificate": "true",
      "timeout": "30"
    },
    {
      "name": "archive",
      "provider": "mssql",
      "server": "dbserver.local\\INST2",
      "database": "WIS_ARCHIVE",
      "integratedSecurity": "false",
      "trustCertificate": "false",
      "timeout": "60",
      "username": "appuser",
      "password": "secret"
    }
  ]
}

Die Felder der eingebauten mssql-Factory:

Feld Typ Beschreibung Default
server string Servername oder Instanz localhost
database string Datenbankname / Catalog (leer)
integratedSecurity boolean Windows-Authentifizierung true
trustCertificate boolean Serverzertifikat vertrauen true
timeout integer Verbindungs-Timeout in Sekunden 30
username string SQL-Login (nur bei integratedSecurity=false) (leer)
password secret SQL-Passwort (leer)

Infrastruktur-Prüfung

Bevor eine Verbindung hergestellt wird, kann ein Add-in prüfen, ob die benötigte Infrastruktur vorhanden ist. Für mssql prüft der Host, ob ODBC Driver 17 oder 18 installiert ist.

procedure TWISPlugin.CheckDrivers;
var
  LResult: TWvdSInfraCheckResult;
begin
  LResult := FHost.Connections.CheckInfrastructure('mssql');
  if LResult.Available then
    FHost.Log.Info('MSSQL-Treiber verfügbar: ' + LResult.DriverVersion)
  else
  begin
    FHost.Log.Error('MSSQL-Treiber fehlt: ' + LResult.Message);
    FHost.Notifications.ShowError(
      'ODBC Driver 17/18 ist nicht installiert. ' +
      'Bitte installieren Sie den Microsoft ODBC Driver für SQL Server.');
  end;
end;

Verbindungstest

Der Verbindungstest geht einen Schritt weiter: Er baut die Verbindung tatsächlich auf und misst die Antwortzeit.

procedure TWISPlugin.TestWISConnection;
var
  LResult: TWvdSConnectionTestResult;
begin
  LResult := FHost.Connections.TestConnection('wis');
  if LResult.Success then
    FHost.Log.Info(Format('Verbindung "wis" erfolgreich (%d ms)',
      [LResult.LatencyMs]))
  else
    FHost.Log.Error('Verbindung "wis" fehlgeschlagen: ' +
      LResult.Message);
end;

Fehlerbehandlung

Situation Verhalten
Verbindungsname nicht in user.config.json GetConnection gibt nil zurück
Provider-Typ ohne registrierte Factory GetConnection gibt nil zurück, Log-Eintrag auf Error-Level
Infrastruktur fehlt (z.B. ODBC-Treiber) CheckInfrastructure gibt Available = False zurück
Verbindungsaufbau schlägt fehl TestConnection gibt Success = False mit Fehlermeldung zurück
Factory-Registrierung für existierenden Typ Überschreibt die bestehende Factory (letzte Registrierung gewinnt)

Weiter zur Service-Referenz oder zurück zur SDK-Übersicht.

Zuletzt geändert: den 18.03.2026 um 21:42