Das ist eine für den Ausdruck optimierte Ansicht des gesamten Kapitels inkl. Unterseiten. Druckvorgang starten.

Zur Standardansicht zurückkehren.

Dokumentation

Vollständige Dokumentation der dsyr.DataBridge — von der Installation bis zum täglichen Betrieb.

Willkommen in der dsyr.DataBridge-Dokumentation. Hier findest du alles, was du für Einrichtung, Konfiguration und Betrieb einer DataBridge-Instanz benötigst.


Überblick

Neu hier? Starte mit dem konzeptuellen Einstieg.

Wie es funktioniert

Der Datenfluss in fünf Schritten — verständlich ohne technischen Ballast.

→ Zum Artikel

Technische Architektur

Azure-Komponenten, Queue-Struktur, Service-Schicht und Connector-Hierarchie.

→ Zum Artikel


Einrichtung

Voraussetzungen

Benutzerkonten, Berechtigungen, Azure-Provider, Change Tracking.

→ Zum Artikel

Installation

Azure-Ressourcen per Bicep anlegen, Function App deployen.

→ Zum Artikel

Umgebungsvariablen

Alle App-Settings mit Standardwerten und Wirkung.

→ Zum Artikel


Konfiguration

Die DataBridge wird vollständig über vier Azure Tables gesteuert.

TabelleInhaltSeite
connectionsZugangsdaten zu Quell- und Zielsystemen→
jobsSynchronisations-Jobs: Richtung, Zeitplan, Priorität→
mappingsFeld-Mappings und Transformationen→
watermarkLetzter synchronisierter Stand pro Job→

Konnektoren

KonnektorSystemLesenSchreibenSeite
D365BCBusiness Central v17+✅✅→
D365CEDataverse v9.1+✅✅→
D365NAVDynamics NAV 2018+✅✅→
MSSQLSQL Server✅ Preview—→
MySQLMySQL / MariaDB⚠️—→
OracleOracle Database⚠️—→
PostgreSQLPostgreSQL⚠️—→

Die Versionsangaben sind getestete Mindestversionen; im Code findet keine Versionsprüfung statt. ⚠️ = nur ungefiltertes Vollauslesen — Details auf der jeweiligen Konnektorseite.


MCP-Server

Eingebauter, read-only MCP-Server für KI-Assistenten wie Claude Code: Jobs, Mappings, Service-Bus-Queues und echte Connector-Schemas (D365BC/NAV/CE, OpenAPI und die SQL-Connectoren) zum Validieren von Mappings.

→ Zum Artikel


Betrieb & Wartung

Logging

Gepuffertes Logging, Sampling von Abhängigkeiten, Custom Metrics, KQL-Abfragen.

→ Zum Artikel

Monitoring & Alerts

Action Groups, Alert Rules, empfohlene Schwellwerte.

→ Zum Artikel

Grafana-Dashboard

Mitgeliefertes Betriebsdashboard: Erfolgsraten, Laufzeiten, Änderungen je Job, Exceptions.

→ Zum Artikel

Updates & Upgrades

Upgrade-Pfade, Breaking Changes, Wartungsfenster-Checkliste.

→ Zum Artikel


Versionshistorie

Änderungen, neue Features und Breaking Changes je Release: → Versionshistorie

1 - Überblick

Was die dsyr.DataBridge ist, wie sie funktioniert und wie sie technisch aufgebaut ist.

Dieses Kapitel erklärt die dsyr.DataBridge auf zwei Ebenen: konzeptuell für alle, die verstehen wollen wie die Synchronisation abläuft — und technisch für diejenigen, die den genauen Aufbau der Komponenten kennen möchten.

SeiteZielgruppeInhalt
Wie es funktioniertAlle NutzerDatenfluss in einfachen Schritten
ArchitekturTechniker, AdministratorenAzure-Komponenten, Services, Queues

1.1 - Wie es funktioniert

Der Datenfluss der dsyr.DataBridge in fünf Schritten — verständlich erklärt ohne technischen Ballast.

Das Grundprinzip

Die dsyr.DataBridge arbeitet wie ein kontinuierlicher Datenstrom zwischen zwei Systemen. Sie fragt regelmäßig: „Was hat sich seit dem letzten Mal geändert?" — und überträgt genau diese Änderungen ins Zielsystem. Nichts mehr, nichts weniger.

Kein manueller Export. Kein Doppelpflegen. Keine Vollsynchronisation, die alles neu schreibt. Nur die Differenz, automatisch, im Minutentakt.


Der Datenfluss in fünf Schritten

flowchart LR
    A([Quelldaten<br/>ändern sich]) -->|"alle 15 Sekunden<br/>prüfen"| B[DataBridge<br/>erkennt Änderung]
    B -->|"Datensätze<br/>puffern"| C[(Service Bus<br/>Queue)]
    C -->|"Nachricht<br/>verarbeiten"| D[Felder<br/>mappen & wandeln]
    D -->|"schreiben"| E([Zielsystem<br/>aktualisiert])
    E -->|"Stand merken"| F[(Watermark<br/>gespeichert)]
    F -.->|"nächster Lauf<br/>startet hier"| B

    style A fill:#e8f5f1,stroke:#18b28d,color:#111111
    style E fill:#e8f5f1,stroke:#18b28d,color:#111111
    style C fill:#fff3e0,stroke:#f57c00,color:#111111
    style F fill:#e3f2fd,stroke:#1565c0,color:#111111

Schritt 1 — Änderungen erkennen

Alle 15 Sekunden (konfigurierbar über GetChangesTimerExpression) startet ein Timer. Die DataBridge fragt das Quellsystem: „Gib mir alle Datensätze, die seit meinem letzten Stand geändert wurden."

Dafür nutzt sie den gespeicherten Watermark — einen Zeitstempel oder einen Änderungstoken, der beim letzten erfolgreichen Lauf hinterlegt wurde.

Schritt 2 — Änderungen puffern

Gefundene Datensätze landen nicht sofort im Zielsystem, sondern zunächst in einer Azure Service Bus Queue. Das entkoppelt Erkennung und Verarbeitung: Selbst wenn das Zielsystem kurz nicht erreichbar ist, gehen keine Daten verloren. Die Queue speichert die Nachrichten bis zur erfolgreichen Verarbeitung.

Schritt 3 — Felder mappen und wandeln

Jede Nachricht aus der Queue wird einzeln verarbeitet. Die DataBridge liest die konfigurierten Mappings und weiß: Quellfeld A geht in Zielfeld B, Wert X wird zu Wert Y übersetzt, ein fehlendes Feld bekommt einen Standardwert.

Dieser Schritt ist vollständig konfigurierbar — ohne Programmierung.

Schritt 4 — Ins Zielsystem schreiben

Der gemappte Datensatz wird ins Zielsystem geschrieben. Die DataBridge prüft dabei anhand eines konfigurierten Schlüssels, ob der Datensatz dort bereits existiert:

  • Existiert: Update des vorhandenen Datensatzes
  • Existiert nicht: Neuanlage

Schritt 5 — Watermark aktualisieren

Nach erfolgreichem Durchlauf speichert die DataBridge den neuen Stand als Watermark. Beim nächsten Timer-Aufruf dient dieser Wert als Startpunkt — so werden Datensätze weder doppelt übertragen noch übersprungen.


Bidirektionale Synchronisation

Die fünf Schritte gelten für eine Richtung der Synchronisation. Wenn Daten in beide Richtungen fließen sollen (z. B. BC → Dataverse und Dataverse → BC), werden zwei unabhängige Jobs konfiguriert — jeder mit eigenem Quell- und Zielsystem, eigenem Mapping und eigenem Watermark.


Prioritäten und Parallelverarbeitung

Nicht alle Datensätze sind gleich wichtig. Die DataBridge kennt drei Prioritätsstufen:

PrioritätTypischer Einsatz
HochZeitkritische Stammdaten (z. B. Preise, Kundendaten)
StandardNormale Synchronisation (Bestellungen, Kontakte)
NiedrigHistorische Daten, große Datenmengen ohne Zeitdruck

Nachrichten mit hoher Priorität werden bevorzugt verarbeitet, ohne dass niedrigere Prioritäten blockiert werden.


Was passiert bei Fehlern?

Schlägt die Verarbeitung einer Nachricht fehl, gibt es mehrere Sicherheitsnetze:

  1. Automatischer Wiederholungsversuch — die Nachricht wird nach kurzer Wartezeit erneut verarbeitet (bis zu einer konfigurierten Maximalanzahl).
  2. Transitive Queue — meldet das Zielsystem einen Konflikt (z. B. ein Duplikat), wird die Nachricht in eine separate Queue abgelegt, damit sie den Hauptpfad nicht blockiert. Dort wird sie nicht automatisch erneut verarbeitet, sondern bei Bedarf manuell zurückgestellt.
  3. Dead-Letter Queue — dauerhaft nicht verarbeitbare Nachrichten werden dort gesammelt und können analysiert oder manuell re-queued werden.

In keinem dieser Fälle gehen Daten still verloren. Das Monitoring und die konfigurierbaren Alerts informieren aktiv, wenn die Dead-Letter Queue Einträge enthält.

1.2 - Technische Architektur

C4-Modell (Context, Container, Component), Azure-Komponenten, Service-Schichten, Queue-Struktur und Connector-Hierarchie der dsyr.DataBridge.

Azure-Komponenten

Die DataBridge besteht aus mehreren Azure-Diensten, die in einer gemeinsamen Ressourcengruppe betrieben werden:

KomponenteDienstRolle
Function AppAzure Functions (Isolated Worker, .NET 10)Ausführungsumgebung für alle Funktionen
App Service PlanMicrosoft.Web/serverfarmsCompute-Plan: Y1 (Consumption, Standard) oder EP1–EP3 (Premium)
Service BusAzure Service Bus NamespacePuffer zwischen Erkennung und Verarbeitung
Table StorageAzure Storage AccountLaufzeit-Konfiguration (Connections, Jobs, Mappings, Watermark)
Application InsightsAzure MonitorLogging, Telemetrie, Alerting; Traces und ausgehende Aufrufe werden gesampelt
Grafana-DashboardMicrosoft.Dashboard/dashboardsVorkonfiguriertes Betriebsdashboard im Application-Insights-Blade (seit v2.1, wird immer mitdeployt)
Metric Alerts + Action GroupAzure MonitorDead-Letter- und Queue-Längen-Alerts, optional über enableAlerts

Architektur nach dem C4-Modell

Die folgenden drei Diagramme folgen dem C4-Modell (Context → Container → Component) — von außen nach innen, jede Ebene zoomt weiter in die DataBridge hinein.

Ebene 1 — System Context

Wer nutzt die DataBridge, und mit welchen Systemen tauscht sie Daten aus?

flowchart TB
    admin(["👤 Administrator"])
    ki(["🤖 KI-Assistent<br/><small>z. B. Claude Code</small>"])

    databridge["dsyr.DataBridge<br/><small>Synchronisiert Datensätze zwischen<br/>Quell- und Zielsystemen</small>"]

    bc["D365 Business Central<br/><small>ERP — Quelle/Ziel</small>"]
    ce["D365 CE / Dataverse<br/><small>CRM — Quelle/Ziel</small>"]
    nav["D365 NAV<br/><small>ERP — Quelle/Ziel</small>"]
    sql[("SQL-Datenbanken<br/><small>nur Quelle</small>")]
    rest["Beliebige REST-API<br/><small>über OpenAPI-Spec</small>"]

    admin -->|"konfiguriert, überwacht"| databridge
    ki -->|"MCP über HTTPS"| databridge
    databridge <-->|"OData v4, OAuth2/API-Key"| bc
    databridge <-->|"Dataverse SDK"| ce
    databridge <-->|"OData, Basic Auth"| nav
    databridge -->|"ADO.NET"| sql
    databridge <-->|"REST, aus Spec aufgelöst"| rest

    style databridge fill:#1565c0,color:#fff,stroke:#0d47a1,stroke-width:2
    style admin fill:#08427b,color:#fff,stroke:#052e56
    style ki fill:#08427b,color:#fff,stroke:#052e56
    style bc fill:#8b8b8b,color:#fff,stroke:#666
    style ce fill:#8b8b8b,color:#fff,stroke:#666
    style nav fill:#8b8b8b,color:#fff,stroke:#666
    style sql fill:#8b8b8b,color:#fff,stroke:#666
    style rest fill:#8b8b8b,color:#fff,stroke:#666

Ebene 2 — Container

Innerhalb des eigenen Azure-Abonnements des Kunden besteht die DataBridge aus fünf Containern (im C4-Sinn: separat betreibbare/skalierbare Einheiten):

flowchart TB
    admin(["👤 Administrator"])
    ki(["🤖 KI-Assistent"])

    subgraph RG["Ressourcengruppe des Kunden"]
        func["Function App<br/><small>.NET 10, Isolated Worker</small>"]
        sb[["Service Bus<br/><small>7 Queues</small>"]]
        ts[("Table Storage<br/><small>connections/jobs/<br/>mappings/watermark</small>")]
        appins["Application Insights"]
        graf["Grafana-Dashboard"]

        func <-->|"Nachrichten"| sb
        func <-->|"Konfiguration & Watermark"| ts
        func -->|"Telemetrie"| appins
        appins --> graf
    end

    systems["Quell-/Zielsysteme<br/><small>D365 BC/CE/NAV, SQL, REST</small>"]

    admin -->|"konfiguriert (Table Storage)"| func
    ki -->|"ruft MCP-Tools auf"| func
    func <-->|"liest/schreibt Datensätze"| systems

    style func fill:#1565c0,color:#fff,stroke:#0d47a1,stroke-width:2
    style sb fill:#1565c0,color:#fff,stroke:#0d47a1
    style ts fill:#1565c0,color:#fff,stroke:#0d47a1
    style appins fill:#1565c0,color:#fff,stroke:#0d47a1
    style graf fill:#1565c0,color:#fff,stroke:#0d47a1
    style admin fill:#08427b,color:#fff,stroke:#052e56
    style ki fill:#08427b,color:#fff,stroke:#052e56
    style systems fill:#8b8b8b,color:#fff,stroke:#666
    style RG fill:#f5f5f5,stroke:#999,color:#111111,stroke-dasharray: 4 2

Ebene 3 — Component

Innerhalb des Containers Function App sind die Funktionen (Trigger-Ebene) von der Service-Schicht (Logik-Ebene) getrennt. Um das Diagramm lesbar zu halten, wird die Service-Schicht hier als ein Component dargestellt und direkt im Anschluss (Abschnitt Service-Schicht) sowie in der Connector-Hierarchie weiter aufgeschlüsselt — das C4-Modell ist für genau dieses schrittweise Hineinzoomen gedacht.

flowchart TB
    subgraph FA["Function App"]
        getchanges["GetChanges<br/><small>Timer</small>"]
        processq["Process*QueueItems<br/><small>Service-Bus-Trigger (3x)</small>"]
        executejob["ExecuteJob / ExecuteHttpRequest<br/><small>HTTP</small>"]
        diagapi["CheckList / Validate / JobApi /<br/>MappingApi<br/><small>HTTP, read-only</small>"]
        mcp["McpApi<br/><small>MCP-Server für KI-Assistenten</small>"]
        orch["Orchestrator<br/><small>Durable, alternativ zu GetChanges</small>"]

        services["Service-Schicht<br/><small>InitializationService + 8 Services<br/>— siehe Detail-Diagramm unten</small>"]
        connectors["Connector-Schicht<br/><small>IConnector-Implementierungen<br/>— siehe Connector-Hierarchie</small>"]

        getchanges --> services
        processq --> services
        executejob --> services
        diagapi --> services
        mcp --> services
        orch --> services
        services --> connectors
    end

    style services fill:#1565c0,color:#fff,stroke:#0d47a1,stroke-width:2
    style connectors fill:#1565c0,color:#fff,stroke:#0d47a1,stroke-width:2
    style FA fill:#f5f5f5,stroke:#999,color:#111111,stroke-dasharray: 4 2

Die Azure Functions

Alle Funktionen laufen in derselben Function App und teilen sich eine gemeinsame Service-Schicht:

FunktionTriggerAufgabe
GetChangesTimer (konfigurierbar über GetChangesTimerExpression)Fragt alle fälligen Quellsysteme nach Änderungen und schreibt Nachrichten in die Service Bus Queues
ProcessStandardQueueItemsService Bus sbqexportVerarbeitet Nachrichten mit Standard-Priorität
ProcessHighPriorityQueueItemsService Bus sbqhighexportVerarbeitet Nachrichten mit hoher Priorität
ProcessLowPriorityQueueItemsService Bus sbqlowexportVerarbeitet Nachrichten mit niedriger Priorität
ExecuteJobHTTP POSTFührt einen einzelnen Job manuell und sofort aus
ExecuteHttpRequestHTTP POSTDiagnose: führt eine ad-hoc HTTP-GET-Anfrage gegen eine beliebige URL aus, unabhängig von den konfigurierten Connectoren
GetInfoHTTP GETGibt die Versionsnummer der Function App zurück
CheckListHTTP GETDiagnose: prüft Mappings gegen die Attribute der beteiligten Systeme
ValidateHTTP GETDiagnose: validiert die hinterlegte Konfiguration
JobApi (GET /job, GET /job/{rowKey})HTTP GETDiagnose: gibt die eingelesene jobs-Konfiguration (alle bzw. ein einzelner Job) zurück
MappingApi (GET /mapping, GET /mapping/{name})HTTP GETDiagnose: gibt die eingelesene mappings-Konfiguration (alle bzw. ein einzelnes Mapping anhand seines Namens) zurück
McpApi (POST /mcp)HTTP POSTRead-only MCP-Server für KI-Assistenten — bündelt Jobs/Mappings/Queues/Connector-Metadaten als Tools

Zusätzlich existiert eine optionale Durable-Orchestrierung als Alternative zum Timer-Pfad (StartGetChangesOrchestration, GetChangesOrchestrator, GetChangesActivity und die Durable Entity JobState). Sie wird ausschließlich per HTTP gestartet und ist im Regelbetrieb nicht aktiv.

Alle Funktionen lassen sich einzeln per App-Setting deaktivieren (AzureWebJobs.<Funktionsname>.Disabled), ohne die Function App neu zu deployen.


Queue-Struktur im Service Bus

Der Service Bus Namespace enthält sieben Queues:

flowchart TD
    GC[GetChanges] -->|Priorität: Hoch| QH[sbqhighexport]
    GC -->|Priorität: Standard| QS[sbqexport]
    GC -->|Priorität: Niedrig| QL[sbqlowexport]

    QH --> PH[ProcessHighPriorityQueueItems]
    QS --> PS[ProcessStandardQueueItems]
    QL --> PL[ProcessLowPriorityQueueItems]

    PH -->|"Konflikt im Ziel<br/>(HTTP 409)"| TH[sbqtransitivehigh]
    PS -->|"Konflikt im Ziel<br/>(HTTP 409)"| TS[sbqtransitivestandart]
    PL -->|"Konflikt im Ziel<br/>(HTTP 409)"| TL[sbqtransitivelow]

    PH -->|"SkipOutdatedMessages"| IGN[sbqignored]
    PS --> IGN
    PL --> IGN

    PH -->|"max. Zustellversuche<br/>überschritten"| DLQ[("Dead-Letter-Subqueue<br/>je Queue")]
    PS --> DLQ
    PL --> DLQ

    style QH fill:#fce4ec,stroke:#c62828,color:#111111
    style QS fill:#fff3e0,stroke:#e65100,color:#111111
    style QL fill:#e8f5e9,stroke:#2e7d32,color:#111111
    style TH fill:#fce4ec,stroke:#c62828,color:#111111,stroke-dasharray: 4 2
    style TS fill:#fff3e0,stroke:#e65100,color:#111111,stroke-dasharray: 4 2
    style TL fill:#e8f5e9,stroke:#2e7d32,color:#111111,stroke-dasharray: 4 2
    style IGN fill:#f3e5f5,stroke:#6a1b9a,color:#111111
    style DLQ fill:#b0bec5,stroke:#37474f,color:#111111
QueueZweck
sbqhighexport / sbqexport / sbqlowexportPrimäre Verarbeitungs-Queues nach Priorität
sbqtransitivehigh / sbqtransitivestandart / sbqtransitivelowZiel-Queues bei Konflikten im Zielsystem (HTTP 409 / Duplikat-Datensatz), damit die Nachricht den Hauptpfad nicht blockiert
sbqignoredVeraltete Nachrichten (Quelldatum < Zieldatum bei aktivem SkipOutdatedMessages)
Dead-Letter-SubqueueJede Queue besitzt eine eigene Dead-Letter-Subqueue (<queuename>/$deadletterqueue) für Nachrichten, die nach maxDeliveryCount Versuchen nicht verarbeitet werden konnten

Service-Schicht

Alle Funktionen teilen eine gemeinsame Service-Schicht. Die Services selbst sind DI-Singletons; lazy und thread-sicher nachgeladen wird die Konfiguration beim ersten Aufruf. Das folgende Diagramm zoomt gegenüber dem C4-Component-Diagramm oben gezielt auf den InitializationService-Teilbaum und dessen Initialisierungsreihenfolge. InitializationService (bis v2.3 CoreService) ist reduziert auf Initialisierung und Ready/Error-Zustand — er ist kein Service-Locator: ProcessJobsForRetrievalInOrderService und ProcessQueueMessageService werden von den Functions, die sie brauchen (GetChanges/ExecuteJob bzw. Process*QueueItems), direkt injiziert, nicht über ihn geroutet:

flowchart TB
    IS["InitializationService<br/>Lazy Konfigurations-Laden<br/>thread-sicher"]

    IS --> STORE["StorageService<br/>Table-Storage-CRUD"]
    IS --> CONS["ConnectionService<br/>Verwaltet Connector-Instanzen<br/>je Connection-Name"]
    IS --> MS["MappingService<br/>Gruppiert FieldMappings<br/>je Mapping-Name"]
    IS --> JS["JobService<br/>Prüft Schedules<br/>validiert Job-Konfiguration"]
    IS --> CHK["CheckListService<br/>Diagnose"]

    PJRS["ProcessJobsForRetrievalInOrderService<br/>Erkennungs-Pfad"] --> WS["WatermarkService"]
    PJRS --> SBS["ServiceBusService"]
    PQS["ProcessQueueMessageService<br/>Verarbeitet einzelne<br/>Nachrichten End-to-End"] --> SBS
    PQS --> WS
    JS --> WS
    WS --> STORE

    style IS fill:#e8f5f1,stroke:#18b28d,color:#111111,stroke-width:2

Daneben stehen Querschnitts-Services, die nicht unter dem InitializationService-Teilbaum hängen:

  • SettingsService — liest App-Settings und Connection-Strings aus der Konfiguration (nicht aus Table Storage)
  • MetricsService — sendet vorab aggregierte Custom Metrics an Application Insights (z. B. ChangesDetected je Job)
  • McpToolService — bündelt die Tool-Logik hinter dem MCP-Server: ruft JobService, MappingService, ServiceBusService und ConnectionService auf und stößt darüber (wie JobApi/MappingApi) selbst einmalig InitializationService.EnsureInitializedAsync an — hängt also nicht im InitializationService-Baum, sondern davor

Initialisierungsreihenfolge:

  1. StorageService prüft die vier Tabellen und legt fehlende an
  2. ConnectionService liest connections, deserialisiert die JSON-Verbindungsdaten und führt je System einen Verbindungstest aus
  3. MappingService liest mappings und gruppiert die Einträge nach Mapping-Name
  4. JobService liest jobs und validiert sie: existieren Verbindungen und Mappings? Ist die Cron-Expression gültig?

Schlägt ein Verbindungstest fehl, wird der Fehler protokolliert und der laufende Aufruf mit einer Exception beendet. Die Initialisierung wird zurückgesetzt und beim nächsten Timer-Aufruf bzw. bei der nächsten Queue-Nachricht erneut versucht (Self-Repair). Bei Queue-Nachrichten bedeutet das: die Nachricht wird abandoned und zählt gegen maxDeliveryCount.


Connector-Hierarchie

Detailansicht der Komponente Connector-Schicht aus dem C4-Component-Diagramm oben — als UML-artiges Klassendiagramm, da C4 Vererbungshierarchien nicht sauber abbilden kann. Jedes Quell- und Zielsystem wird durch einen Konnektor abstrahiert. Alle Konnektoren implementieren dasselbe Interface und sind damit gegeneinander austauschbar:

flowchart TD
    IC["«interface»<br/>IConnector"]

    CB["ConnectorBase<br/>Serialisierung, Caching,<br/>Filter-Templating,<br/>Key-Vault-Integration"]

    HCB["HttpConnectorBase<br/>OAuth2 / Basic Auth,<br/>Token-Lifecycle,<br/>HTTP-Client-Management"]

    SCB["SqlConnectorBase<br/>ADO.NET, Schema-Caching,<br/>Paging (OFFSET / FETCH)"]

    IC --> CB
    CB --> HCB
    CB --> SCB
    CB --> CE["CeConnector<br/>Dataverse SDK"]

    HCB --> BC["BcConnector<br/>OData v4 API"]
    HCB --> NAV["NavConnector<br/>OData / Basic Auth"]
    HCB --> OA["OpenApiConnector<br/>Generische REST-API,<br/>Pfade aus OpenAPI-Spec"]

    SCB --> SQL[MsSqlConnector]
    SCB --> PG[PostgreSqlConnector]
    SCB --> MY[MySqlConnector]
    SCB --> OR[OracleConnector]

    style IC fill:#e3f2fd,stroke:#1565c0,color:#111111
    style CB fill:#e8f5f1,stroke:#18b28d,color:#111111
    style HCB fill:#fff3e0,stroke:#e65100,color:#111111
    style SCB fill:#fce4ec,stroke:#c62828,color:#111111

Für die Typumwandlung greifen zwei Bausteine ineinander:

  • TargetValueMapper — wählt anhand des Zielsystem-Metadatentyps (EDM bei BC/NAV, AttributeMetadata bei Dataverse) den passenden Resolver aus. Existiert je Connector.
  • AttributeResolver — führt die eigentliche Typumwandlung durch (Boolean, Date, DateTime, Decimal, Guid, Integer, String; bei Dataverse zusätzlich Picklist, Lookup, Money, State/Status, MultiSelect). Liegt als Resolver-Familie je Systemtyp vor.

IConnector stellt zusätzlich GetTableMetadata(tableName, apiPath) bereit: liefert Typ, Nullable, maximale Länge und Primary-Key-Information je Spalte direkt aus dem Zielsystem — implementiert für alle Connector-Typen, seit v2.3 auch für die SQL-Familie. Dient vor allem dem MCP-Server zur Validierung von Mappings gegen das echte Schema.


Änderungserkennung pro Connector-Typ

Die Methode, mit der Änderungen erkannt werden, unterscheidet sich je nach System:

ConnectorMechanismusWatermark-Typ
D365BCOData-$filter auf das im Job konfigurierte Feld WatermarkField (z. B. lastModifiedDateTime)datetime
D365CE / DataverseDataverse Change Tracking API (opaker Token)string
D365NAVOData-$filter auf das konfigurierte WatermarkFielddatetime oder date
SQL-DatenbankenSQL-WHERE-Klausel auf das konfigurierte WatermarkFielddate oder datetime
OpenAPIWatermarkField wird als JSON-Pfad auf jeden gelesenen Datensatz angewendet; Filter wird unverändert als Query-String an die API angehängt (API-abhängig)datetime

Der Watermark-Wert wird pro Job in der Tabelle watermark gespeichert und am Ende der Erkennungsphase fortgeschrieben. Ein manuelles Leeren des Watermarks erzwingt einen vollständigen Neulauf des jeweiligen Jobs.


Über CRUD hinaus: Aktions-Jobs, job-eigene Pfade, eingebettete Kinder

Drei neuere Erweiterungen des Job-/Mapping-Modells betreffen nicht nur den OpenAPI-Konnektor, sind dort aber am relevantesten:

  • InvokeAction — ein Job mit einem Action-Wert, der keinem bekannten JobActionType entspricht, ruft statt eines CRUD-Vorgangs einen benannten, nicht-CRUD-Endpunkt auf (z. B. einen OpenAPI-operationId oder ein CE-OrganizationRequest). Details: Jobs → InvokeAction.
  • Job-eigene Source-/Target-Pfade — ein Job kann über Source/Target die SourceTable/TargetTable seiner Mapping-Zeilen überschreiben (bzw. bei fehlendem Mapping ein synthetisches Mapping erzeugen), sodass eine Mapping-Gruppe für mehrere Ressourcen oder Aktionspfade wiederverwendet werden kann. Details: Jobs → Source und Target.
  • EmbedChildren — bettet die Datensätze eines referenzierten Kind-Jobs bereits beim Lesen in die Nachricht des Elterndatensatzes ein, sodass Eltern- und Kind-Datensätze gemeinsam als eine Nachricht verarbeitet werden (derzeit nur mit D365CE als Quellsystem). Details: Mappings → EmbedChildren.

Retry- und Fehlerverhalten

SzenarioVerhalten
Konflikt im Zielsystem (HTTP 409 / Duplikat)Weiterleitung in die passende Transitive-Queue
Transientes Netzwerkproblem, Zielsystem temporär nicht erreichbarBackoff (DeliveryCount² · 1000 ms), Abandon, erneute Zustellung nach Lock-Ablauf
Veraltete Nachricht (Quelle älter als Ziel)Bei aktivem SkipOutdatedMessages: Weiterleitung in sbqignored
Maximale Zustellversuche überschrittenWeiterleitung in die Dead-Letter-Subqueue der jeweiligen Queue, Alert ausgelöst (wenn konfiguriert)
Initialisierungsfehler (Verbindung)Fehler wird geloggt, laufender Aufruf bricht ab, nächster Aufruf versucht erneut

2 - Einrichtung

Von null zur laufenden DataBridge-Instanz: Voraussetzungen, Installation und Konfiguration der Umgebung.

Dieses Kapitel führt Schritt für Schritt durch die erstmalige Einrichtung einer dsyr.DataBridge-Instanz. Am Ende steht eine vollständig deployete und konfigurierte Azure Function App, die synchronisieren kann.

Empfohlene Reihenfolge:

  1. Voraussetzungen — Benutzer, Berechtigungen, Azure-Provider
  2. Installation — Azure-Ressourcen per Bicep anlegen, Function deployen
  3. Umgebungsvariablen — Alle Einstellungen im Function-App-Kontext

Ergänzend, außerhalb des regulären Installationswegs:

  • Container-Betrieb — Betrieb als Container-Image. Beta, nicht für den Kundenbetrieb vorgesehen.

2.1 - Voraussetzungen

Benutzerkonten, Berechtigungen, Azure-Provider und Software, die vor der Installation bereitstehen müssen.

Bevor die DataBridge installiert werden kann, müssen die folgenden Voraussetzungen in Azure, Dataverse und Business Central erfüllt sein.


Azure-Abonnement

Ein aktives Azure-Abonnement ist Pflicht. Im Abonnement müssen die folgenden Resource Provider registriert sein:

ProviderZweckPflicht
Microsoft.InsightsApplication Insights✅
Microsoft.OperationalInsightsLog Analytics✅
Microsoft.ServiceBusService Bus Namespace✅
Microsoft.StorageTable Storage, Blob Storage✅
Microsoft.WebAzure Function App✅
Microsoft.DashboardGrafana-Dashboard in Application Insights✅
Microsoft.KeyVaultSichere Speicherung von Connection Stringsoptional
Microsoft.RelayHybrid Connections (OnPremise-Anbindung)optional
Microsoft.AppContainer Appsveraltet / deprecated

Provider können im Azure Portal unter Abonnement → Ressourcenanbieter aktiviert werden.


Empfohlene Instanzen-Struktur

Pro Umgebung wird eine eigene DataBridge-Instanz (= Ressourcengruppe) benötigt. Als Regelfall werden zwei Instanzen empfohlen:

InstanzZweck
TestValidierung von Mappings und Konfigurationsänderungen
ProduktionLiveverbindung zwischen den Produktivsystemen

Eine Entwicklungsinstanz ist meist nicht nötig, da die Entwicklung der DataBridge selbst bei WYSA liegt und sich eine Synchronisation im Entwicklungssystem selten lohnt.

Eine dritte Instanz für Entwicklung ist dann sinnvoll, wenn im Entwicklungssystem eigene Anpassungen an Dataverse oder Business Central entstehen, die vor dem Test gegen eine laufende Synchronisation geprüft werden sollen — etwa neue Felder, geänderte Optionssets oder umgebaute Entitäten. In diesem Fall werden drei Ressourcengruppen benötigt (Entwicklung, Test, Produktion).


Laufzeitumgebung

Die DataBridge läuft als Azure Function App. Die folgenden Eckdaten werden vom Deployment gesetzt und müssen nicht vorbereitet werden — sie sind hier als Referenz für die Abstimmung mit der IT aufgeführt:

MerkmalWert
Laufzeit.NET 10, Isolated Worker
Azure Functions Runtimev4
BetriebssystemWindows
App Service PlanY1 (Consumption) oder EP1–EP3 (Premium), wählbar beim Deployment

Beim containerisierten Betrieb wird zusätzlich Zugriff auf die WYSA-Container-Registry benötigt. Das aktuelle Image ist acrwysatest.azurecr.io/containered-connector:v2.3.0.


Unterstützte Quell- und Zielsysteme

SystemLesenSchreibenAnmerkung
Dynamics 365 Business Central✅✅Cloud und OnPremise
Dynamics 365 / Dataverse✅✅Change Tracking erforderlich (siehe unten)
Microsoft Dynamics NAV✅✅OnPremise, wahlweise Basic Auth oder NTLM
Microsoft SQL Server⚠️ Preview—
MySQL / MariaDB⚠️ Preview—
Oracle Database⚠️ Preview—
PostgreSQL⚠️ Preview—

Für die vier SQL-Systeme gelten die unten beschriebenen Anforderungen an Anwendungsbenutzer und Change Tracking nicht — dort werden lediglich ein Connection String und die Netzwerkerreichbarkeit aus Azure heraus benötigt. Die Konnektoren sind als Vorabversion eingestuft und unterstützen kein Schreiben; Details auf den Konnektor-Seiten.


Benutzerkonten

Die folgenden Anforderungen gelten für die Anbindung von Dataverse und Business Central. Für SQL-Quellsysteme genügt ein Datenbankbenutzer mit Leserechten.

Administrations-/Betreuungsbenutzer (Entra-Benutzer)

Für die Einrichtung und Pflege von Dataverse und Business Central wird ein menschlicher Benutzer benötigt, der in Entra ID existiert und entsprechend lizensiert ist.

SystemMindestrechte
DataverseRolle „Systemadministrator" + mindestens eine Power Platform-Lizenz
Business CentralAdministrationsrechte + BC-Lizenz zugewiesen

Anwendungsbenutzer (Service Principal / App Registration)

Anwendungsbenutzer ermöglichen der DataBridge den automatisierten, sicheren Zugriff auf die Systeme — ohne personengebundene Credentials.

Minimum: 1 Anwendungsbenutzer pro DataBridge-Instanz.

Empfehlung: 3 Anwendungsbenutzer für maximale Sicherheit und klare Trennung:

AnwendungsbenutzerZweckBenötigte Rechte
Deployment-UserInstallation und Updates der DataBridgeAzure: Contributor auf Ressourcengruppe (bei aktiviertem enableManagedIdentity: Owner oder User Access Administrator, da Rollen zugewiesen werden) · Dataverse: S2S-Benutzer + Systemadministrator
Dataverse-UserDatenaustausch mit Dataverse im laufenden BetriebDataverse: S2S-Benutzer + Systemadministrator oder individuelle Sicherheitsrolle
BC-UserDatenaustausch mit Business Central im laufenden BetriebBC: Registriert + individuelle Sicherheitsrolle

Alle Anwendungsbenutzer müssen als App Registration in Entra ID angelegt und in den jeweiligen Systemen als S2S-Benutzer registriert sein.


Dataverse: Change Tracking aktivieren

Für alle Dataverse-Tabellen, die als Quelle genutzt werden sollen, muss Change Tracking aktiviert sein. Ohne Change Tracking kann die DataBridge keine effizienten Differenzabfragen durchführen.

Aktivierung: Power Platform Admin Center → Umgebung → Tabellen → Tabelle auswählen → Eigenschaften → „Änderungsnachverfolgung aktivieren" ✅


Benötigte Software für die Installation

Für das Deployment per Bicep / Azure CLI muss auf dem ausführenden Rechner installiert sein:

SoftwareDownloadMindestversion
Azure CLIlearn.microsoft.comaktuell
Bicep Extensionüber Azure CLI≥ 0.35

Bicep-Extension installieren und verifizieren:

az bicep install
az bicep version   # Ausgabe sollte ≥ 0.35 zeigen

Die Installation selbst kann durch WYSA durchgeführt werden, sofern der Deployment-Anwendungsbenutzer mit Contributor-Rechten auf der Ressourcengruppe bereitgestellt wurde. Soll der Preview-Schalter enableManagedIdentity genutzt werden, benötigt der Deployment-User Owner oder User Access Administrator, da dabei Rollenzuweisungen angelegt werden.

2.2 - Installation

Die DataBridge über den Azure Marketplace oder per Bicep in Azure einrichten.

Für die Installation gibt es zwei Wege:

WegWann geeignet
Azure MarketplaceRegelfall. Läuft vollständig im Azure Portal, keine lokale Software nötig.
Bicep / Azure CLIAutomatisierte oder wiederholbare Deployments, Integration in eine Pipeline.

Beide Wege legen dieselben Ressourcen an und sind gleichwertig im Ergebnis.


Weg 1 — Azure Marketplace (empfohlen)

1. Ressourcengruppe anlegen

Im Azure Portal eine neue Ressourcengruppe für die DataBridge-Instanz erstellen. Pro Umgebung (Test, Produktion) wird eine eigene Ressourcengruppe verwendet.

Ressourcengruppe anlegen

2. DataBridge aus dem Marketplace auswählen

In der Ressourcengruppe Erstellen wählen und im Marketplace nach der WYSA DataBridge suchen.

DataBridge im Marketplace auswählen

3. Region und Kurzname festlegen

FeldBedeutung
RegionAzure-Region, in der alle Ressourcen angelegt werden
Shortname for DataBridgeNamensbestandteil für alle Ressourcen dieser Instanz

Region und Kurzname

4. Produkteinstellungen

Unter Product Settings werden die fachlichen Einstellungen gruppiert:

BereichEinstellungen
Function SettingsGetChanges Timer Expression — Taktung des Quellabrufs (Vorgabe: alle 15 Sekunden)
TelemetrySampling-Override sowie die drei Sampling-Raten für Logs und Abhängigkeiten
AlertAlerts aktivieren, Benachrichtigungs-E-Mail und die vier Dead-Letter-Schwellwerte
PreviewSkipOutdatedMessages

Skipping und Alerts

Alle Telemetrie-Einstellungen können auf den Vorgabewerten bleiben und später jederzeit in den Umgebungsvariablen angepasst werden.

5. Service Bus

FeldVorgabeBedeutung
Maximum Delivery Count3Zustellversuche, bevor eine Nachricht in die Dead-Letter-Queue geht
Message Time To Live (TTL)P14DAufbewahrungszeit einer Nachricht (ISO 8601)
Lock DurationPT30SSperrdauer während der Verarbeitung

Service-Bus-Einstellungen

6. Storage Account

Storage Account

Als Speicherkonto-Typ steht ausschließlich StorageV2 zur Verfügung.

7. App Service Plan

FeldVorgabeBedeutung
Maximum Scale-Out Count12Obergrenze für parallele Instanzen
App Service Plan SKUConsumption (Y1)Alternativ Premium (EP1) bis Premium (EP3)

App Service Plan

Anschließend über Überprüfen und erstellen das Deployment starten.


Weg 2 — Bicep / Azure CLI

1. Bicep-Dateien bereitstellen

Die Bicep-Templates befinden sich im Repository unter bicep/. Klone das Repository oder lade die Dateien auf den Rechner, von dem aus die Installation durchgeführt wird.

2. Bei Azure anmelden

az login

Es öffnet sich ein Browser-Fenster. Melde dich mit dem Deployment-Anwendungsbenutzer oder dem Administrations-Benutzer an, der Contributor-Rechte auf der Ziel-Ressourcengruppe hat. Den Haken bei „Zugriffseinwilligung für alle Benutzer" nicht setzen.

3. Abonnement prüfen

az account show

Vergleiche die angezeigte Abonnement-ID mit der ID im Azure Portal. Falls mehrere Abonnements vorhanden sind, das richtige setzen:

az account set --subscription "<Abonnement-ID>"

4. Ressourcen deployen

az deployment group create --template-file ./bicep/main.bicep --resource-group <Name-der-Ressourcengruppe> --parameters name=<instanzname> -c

Das Flag -c zeigt eine Vorschau der Änderungen an. Mit Y oder J bestätigen.

Bicep-Parameter im Überblick

Infrastruktur:

ParameterStandardwertBeschreibung
namecustomername-databridgeNamensbestandteil für alle Ressourcen. Immer überschreiben.
locationRegion der RessourcengruppeZielregion für alle Ressourcen
storageKindStorageV2Speicherkonto-Typ (einziger erlaubter Wert)
appServiceSkuY1Y1 (Consumption) oder EP1–EP3 (Premium)
functionScaleLimit12Obergrenze für parallele Instanzen
maxDeliveryCount3Zustellversuche pro Nachricht (1–100)
defaultMessageTimeToLiveP14DAufbewahrungszeit für Nachrichten (ISO 8601)
lockDurationPT30SSperrdauer während der Verarbeitung (ISO 8601)
tagsByResource(leer)Tags je Ressourcentyp

Laufzeit und Telemetrie:

ParameterStandardwertBeschreibung
getChangesTimerExpression*/15 * * * * *Taktung des Quellabrufs
skipOutdatedMessagesfalseNachrichten mit älterem Quell-Zeitstempel überspringen
telemetryControlTRACE_SAMPLETRACE_SAMPLE, TRACE_ALL oder TRACE_NONE
telemetryInfoSampleRate20Anteil der Information-Einträge bei erfolgreichen Läufen (%)
telemetryDependencySampleRate10Anteil erfolgreicher, schneller Abhängigkeits-Aufrufe (%)
telemetryDependencySlowMs2000Schwellwert für „langsam" in Millisekunden

Alerts:

ParameterStandardwertBeschreibung
enableAlertsfalseAlerts für Dead-Letter-Queue und Queue-Länge anlegen
notificationEmail(leer)Empfänger der Alert-Benachrichtigungen. Nur bei enableAlerts = true erforderlich.
dlqLowThreshold1Schwellwert Severity „Informational"
dlqMediumThreshold50Schwellwert Severity „Warning"
dlqHighThreshold200Schwellwert Severity „Error"
dlqCriticalThreshold500Schwellwert Severity „Critical"

Was angelegt wird

Nach erfolgreichem Deployment existieren in der Ressourcengruppe:

RessourceNameDetails
Function Appfunc-<name>Windows, .NET 10 Isolated Worker, Functions v4, FTPS-only
App Service Planasp-<name>Y1 (Consumption) oder EP1–EP3 (Premium)
Storage Accountst<name>StorageV2, Standard_LRS, TLS 1.2
Service Bus Namespacesb<name>SKU Basic, mit allen sieben Queues
Application Insightsappi-<name>90 Tage Aufbewahrung
Grafana-Dashboarddash-<name>Wird immer mit angelegt, kein Opt-out
Alerts + Action Groupag-<name>Nur bei enableAlerts = true

Service-Bus-Queues: sbqexport · sbqhighexport · sbqlowexport · sbqignored · sbqtransitivehigh · sbqtransitivestandart · sbqtransitivelow

Table-Storage-Tabellen: connections, jobs, mappings, watermark — sie werden vom Deployment leer angelegt.


Phase 2 — Function App befüllen

Beim Marketplace-Weg entfällt dieser Schritt in der Regel; das Paket wird mit ausgeliefert. Beim Bicep-Weg wird der Anwendungscode separat eingespielt.

Option A: Container-Image

Wird die DataBridge containerisiert betrieben, verweist die Function App auf das bereitgestellte Image:

acrwysatest.azurecr.io/containered-connector:v2.3.0

Option B: Zip-Upload per Azure Portal

  1. Im Azure Portal die Function App öffnen
  2. Entwicklungstools → Erweiterte Tools (Kudu) öffnen
  3. In Kudu: Tools → Zip Push Deploy
  4. Die bereitgestellte .zip-Datei per Drag & Drop hochladen
  5. Nach dem Upload startet die Function App automatisch neu

Option C: Deployment-Pipeline

Das Deployment kann über eine CI/CD-Pipeline automatisiert werden. WYSA stellt hierfür Azure-Pipeline-Definitionen zur Verfügung (cicd/-Verzeichnis). Alternativ kann eine eigene Azure-DevOps- oder GitHub-Actions-Pipeline verwendet werden.


Nach der Installation: Konfigurationstabellen befüllen

Nach dem Deployment laufen die Azure Functions — aber noch ohne Konfiguration. Die vier Table-Storage-Tabellen existieren, sind aber leer:

ReihenfolgeTabelleInhalt
1connectionsZugangsdaten für alle Quell- und Zielsysteme
2mappingsFeld-Mappings zwischen Quell- und Zieltabellen
3jobsSynchronisations-Jobs (welche Tabelle, welche Richtung, welcher Schedule)
4watermarkWird zur Laufzeit automatisch befüllt — kein manueller Eintrag nötig

Die Tabellen können über den Azure Storage Explorer oder direkt im Azure Portal (Storage Account → Tables) bearbeitet werden.

Details zu den Tabellen und den erwarteten Feldern sind im Kapitel Konfiguration beschrieben.


Umgebungsvariablen prüfen

Alle Verbindungs-Strings sowie der Timer-Ausdruck und die Telemetrie-Einstellungen werden vom Deployment gesetzt. Nachträglich anzupassen sind sie nur, wenn vom Vorgabewert abgewichen werden soll.

Eine vollständige Übersicht findet sich auf der Seite Umgebungsvariablen.


Installation prüfen

Sobald Konfigurationstabellen und Umgebungsvariablen gesetzt sind, kann die Installation geprüft werden:

GET https://func-<name>.azurewebsites.net/api/GetInfo?code=<Function-Key>

Eine erfolgreiche Antwort sieht so aus:

{
  "Version": "2.1.2026.802"
}

Alternativ: Im Azure Portal → Function App → Funktionen → GetInfo → Testen/Ausführen — dort wird der Schlüssel automatisch mitgegeben.

Für eine inhaltliche Prüfung der Konfiguration stehen zusätzlich die Diagnose-Funktionen Validate (prüft die hinterlegte Konfiguration) und CheckList (gleicht Mappings gegen die Attribute der beteiligten Systeme ab) zur Verfügung.

2.3 - Umgebungsvariablen

Alle Konfigurationsparameter der DataBridge Function App — mit Standardwerten und Erläuterung.

Die DataBridge Function App wird über Anwendungseinstellungen (Application Settings) konfiguriert. Diese sind im Azure Portal unter Function App → Einstellungen → Umgebungsvariablen zu finden und zu bearbeiten.


Infrastruktur-Verbindungen

Diese Variablen verbinden die Function App mit den zugehörigen Azure-Diensten. Sie werden durch das Deployment automatisch befüllt.

VariablePflichtBeschreibung
AzureWebJobsStorage✅¹Connection String zum Azure Storage Account (Table Storage für die Konfiguration + Function-interner Storage).
ExportQueueServiceBus✅¹Connection String zum Service Bus Namespace (alle Verarbeitungs-Queues)
APPLICATIONINSIGHTS_CONNECTION_STRINGfaktischConnection String zu Application Insights. Ohne den Wert startet die DataBridge trotzdem, gibt aber nur eine Warnung aus und sendet keinerlei Telemetrie — kein Logging, keine Metriken, keine Fehlerdiagnose.

¹ Schlüssel- oder identitätsbasiert. Standardmäßig werden die beiden Connection Strings gesetzt. Zusätzlich setzt das Deployment in diesem Modus TARGET_BASED_SCALING_ENABLED=0: Target-based Scaling ist damit abgeschaltet, der Host skaliert direkt über den Metrics Monitor und die entsprechende Warnung im Log entfällt. Ist der Preview-Schalter enableManagedIdentity (Bicep) aktiv, entfallen sie und werden durch die identitätsbasierten Varianten ersetzt — die Function nutzt dann die Managed Identity:

Variable (Identity-Modus)Beschreibung
AzureWebJobsStorage__accountNameName des Storage Accounts. Der Functions-Host leitet Blob/Queue/Table-Endpunkte daraus ab und authentifiziert über die Managed Identity.
ExportQueueServiceBus__fullyQualifiedNamespaceVoll qualifizierter Service-Bus-Namespace (z. B. sbfoo.servicebus.windows.net) für den identitätsbasierten Zugriff.

Lokal (local.settings.json) bleiben die schlüsselbasierten Connection Strings maßgeblich.


Laufzeit-Einstellungen

VariableTypStandardwertPflichtBeschreibung
GetChangesTimerExpressionCRON (6-stellig)*/15 * * * * *✅Taktung des GetChanges-Timers. Bei einer Neuinstallation alle 15 Sekunden.
SkipOutdatedMessagesBooleanfalse—Wenn true: Nachrichten, deren Quell-Zeitstempel älter ist als der Zieldatensatz, werden nicht verarbeitet, sondern nach sbqignored verschoben.

CRON-Ausdruck — Format

Der CRON-Ausdruck nutzt das 6-stellige NCrontab-Format (inkl. Sekunden):

┌─────────── Sekunde  (0–59)
│ ┌───────── Minute   (0–59)
│ │ ┌─────── Stunde   (0–23)
│ │ │ ┌───── Tag      (1–31)
│ │ │ │ ┌─── Monat    (1–12)
│ │ │ │ │ ┌─ Wochentag (0–6, Sonntag=0)
│ │ │ │ │ │
*/15 * * * * *  →  alle 15 Sekunden (Vorgabe)
0 */2 * * * *   →  alle 2 Minuten
0 */5 * * * *   →  alle 5 Minuten
0 0 */1 * * *   →  stündlich

Einzelne Jobs können über das Feld Schedule einen eigenen, seltener greifenden Zeitplan bekommen — siehe Jobs.


Logging & Telemetrie

Die DataBridge steuert ihr Telemetrie-Volumen über zwei Mechanismen: gepuffertes Logging für Log-Einträge und Sampling für ausgehende Aufrufe. Ausführlich beschrieben unter Betrieb → Logging.

VariableTypStandardwertBeschreibung
TELEMETRY_CONTROLStringTRACE_SAMPLEGlobaler Override für beide Sampling-Mechanismen. Werte: TRACE_SAMPLE (selektives Sampling, entspricht auch einem leeren Wert), TRACE_ALL (sendet alles), TRACE_NONE (unterdrückt alles).
TELEMETRY_INFO_SAMPLE_RATEInteger 0–10020Anteil der Information-Einträge, die bei erfolgreichen Läufen gesendet werden. Bei Läufen mit Warnungen oder Fehlern immer 100 %.
TELEMETRY_DEPENDENCY_SAMPLE_RATEInteger 0–10010Anteil der erfolgreichen und schnellen Aufrufe an Quell- und Zielsysteme (D365 OData, SQL, Table Storage, Service Bus), die gesendet werden. Fehlgeschlagene und langsame Aufrufe sind davon nicht betroffen.
TELEMETRY_DEPENDENCY_SLOW_MSInteger (ms)2000Schwellwert, ab dem ein Aufruf als „langsam" gilt und immer gesendet wird — auch bei Erfolg.

Telemetrie-Verhalten im Überblick

Lauf-ErgebnisTraceDebugInformationWarningError
Fehlerfreier Laufnie—~20 % (konfigurierbar)✅✅
Lauf mit Warnungennie—✅ alle✅✅
Lauf mit Fehlernie✅ alle✅ alle✅✅

Trace-Einträge erreichen Application Insights grundsätzlich nie — auch nicht bei Fehler-Läufen. Sie sind ausschließlich live im Log Stream der Function App bzw. im Container-Log sichtbar.

Abhängigkeiten

AufrufWird gesendet
Fehlgeschlagenimmer vollständig
Langsam (≥ TELEMETRY_DEPENDENCY_SLOW_MS)immer vollständig
Erfolgreich und schnellanteilig gemäß TELEMETRY_DEPENDENCY_SAMPLE_RATE

Die Entscheidung fällt hier pro Aufruf und nicht pro Funktionsausführung — ein fehlgeschlagener Lauf enthält deshalb nicht zwangsläufig die erfolgreichen Aufrufe davor.


Funktionen gezielt deaktivieren

Einzelne Azure Functions können ohne Redeployment deaktiviert werden. Das ist nützlich, um z. B. die Quellabruf-Funktion zu stoppen, ohne die Queue-Verarbeitung zu unterbrechen.

VariableBetrifftWert zum Deaktivieren
AzureWebJobs.GetChanges.DisabledTimer-Funktion (Quellabruf)true
AzureWebJobs.ProcessStandardQueueItems.DisabledStandard-Prioritäts-Queuetrue
AzureWebJobs.ProcessHighPriorityQueueItems.DisabledHoch-Prioritäts-Queuetrue
AzureWebJobs.ProcessLowPriorityQueueItems.DisabledNiedrig-Prioritäts-Queuetrue

Das Namensschema AzureWebJobs.<Funktionsname>.Disabled gilt für jede Function der App. Neben den vier betrieblich relevanten oben sind das:

ExecuteJob · ExecuteHttpRequest · GetInfo · Validate · CheckList · StartGetChangesOrchestration · GetChangesOrchestrator · GetChangesActivity · JobState

Die letzten vier gehören zur optionalen Durable-Orchestrierung, die im Regelbetrieb ohnehin nicht aktiv ist.


Plattform-Einstellungen — nicht verändern

Diese Werte setzt das Deployment. Sie steuern die Azure-Functions-Laufzeit selbst und haben keinen fachlichen Bezug zur DataBridge:

VariableWertBedeutung
FUNCTIONS_EXTENSION_VERSION~4Version der Azure-Functions-Runtime
FUNCTIONS_WORKER_RUNTIMEdotnet-isolatedWorker-Modell (.NET Isolated Worker)
WEBSITE_CONTENTAZUREFILECONNECTIONSTRING= AzureWebJobsStorageAblage des Function-Contents
WEBSITE_RUN_FROM_PACKAGE1Ausführung direkt aus dem Deployment-Paket

Nur im Container-Betrieb

VariableWertBedeutung
AzureWebJobsSecretStorageTypefilesWeist die Runtime an, Function-Keys ins Dateisystem zu schreiben. Ohne diesen Wert sind HTTP-Trigger mit Function-Authentifizierung nicht aufrufbar.

Key Vault (optional)

Statt Connection Strings direkt in den Anwendungseinstellungen zu speichern, können diese in einem Azure Key Vault hinterlegt werden. Die Function App referenziert das Secret dann per Key Vault Reference:

@Microsoft.KeyVault(SecretUri=https://<vault>.vault.azure.net/secrets/<name>/)

Diese Referenz wird direkt als Wert der jeweiligen Umgebungsvariable eingetragen. Azure löst sie zur Laufzeit automatisch auf — ohne Änderungen am Code.

Für die Zugangsdaten zu Quell- und Zielsystemen gibt es einen zweiten, davon unabhängigen Weg über den Parameter keyVaultUrl im Verbindungs-JSON — siehe Verbindungen.

2.4 - Container-Betrieb

Die DataBridge als Container bauen, in eine Azure Container Registry veröffentlichen und lokal starten — Beta.

Die DataBridge lässt sich als Container-Image bauen und betreiben. Das Image basiert auf der Azure-Functions-Laufzeit und enthält dieselbe Anwendung wie das reguläre Deployment.

Sinnvoll ist der Container-Betrieb derzeit vor allem für lokale Tests und Entwicklung: Die HTTP-Trigger lassen sich damit ohne Azure-Deployment aufrufen.

Voraussetzungen

WerkzeugZweck
Docker Desktop oder PodmanImage bauen und Container starten
Azure CLI (az)Anmeldung an der Azure Container Registry (nur zum Veröffentlichen)

Alle Befehle auf dieser Seite werden aus dem Ordner src/ ausgeführt — dort liegen Dockerfile, compose.yml und die Start-Scripts.

Image bauen

Das Dockerfile liegt in src/. Bei Bedarf lässt sich dort das Basis-Image anpassen.

docker build --tag acrwysatest.azurecr.io/containered-connector:v2.3.0 .
podman build --tag acrwysatest.azurecr.io/containered-connector:v2.3.0 .

Allgemein aufgebaut ist der Tag so:

{acr-name}.azurecr.io/{image-name}:{version}
BestandteilBedeutung
acr-nameName der Azure Container Registry
image-nameName des Images, z. B. containered-connector
versionVersionsnummer, üblicherweise dreistellig in der Form v2.1.0

In die Azure Container Registry veröffentlichen

Zunächst an Azure und anschließend an der Registry anmelden:

az login
az acr login --name acrwysatest

Danach das gebaute Image veröffentlichen:

docker push acrwysatest.azurecr.io/containered-connector:v2.3.0

Damit steht das Image unter der angegebenen Versionsnummer in der Registry bereit.

Lokal betreiben

Umgebungsdatei anlegen

In src/.podman/ liegt die Vorlage podman.env.sample. Diese kopieren und unter dem Namen podman.<Umgebung>.env ablegen, zum Beispiel podman.wysatest.env:

cp .podman/podman.env.sample .podman/podman.wysatest.env

Anschließend die Datei mit den Verbindungsdaten der Zielumgebung füllen. Sie enthält unter anderem AzureWebJobsStorage, ConnectionStrings__ExportQueueServiceBus, APPLICATIONINSIGHTS_CONNECTION_STRING, den Timer-Ausdruck GetChangesTimerExpression sowie die Funktions-Toggles.

Danach in src/compose.yml prüfen, dass env_file auf die richtige Datei verweist:

    env_file:
      - .podman/podman.wysatest.env

Der Container veröffentlicht seinen Port unter 7071 und setzt AzureWebJobsSecretStorageType=files, damit die Function-Keys im Container abgelegt werden können.

Start-Script (empfohlen)

Der einfachste Weg sind die mitgelieferten Start-Scripts in src/. Sie bauen das Image, starten den Container und geben anschließend alle HTTP-Trigger-URLs inklusive Authentication-Key aus.

bash start-local.sh
.\start-local.ps1

Die Ausgabe sieht dann ungefähr so aus:

============================================
 DataBridge lokal gestartet
============================================
 Master Key: abc123...

 HTTP-Trigger URLs:

 [GET]  http://127.0.0.1:7071/api/GetInfo?code=abc123...
 [GET]  http://127.0.0.1:7071/api/CheckList?code=abc123...
 [POST] http://127.0.0.1:7071/api/ExecuteJob?code=abc123...
 [POST] http://127.0.0.1:7071/api/ExecuteHttpRequest?code=abc123...
============================================

Die URLs lassen sich direkt im Browser öffnen oder in Bruno beziehungsweise curl verwenden.

Manueller Start

Wer den Container selbst steuern möchte, startet ihn aus dem Ordner src/:

podman compose up --build -d

Logs anzeigen:

podman compose logs -f

Container beenden:

podman compose down

Installation prüfen

Ob der Container korrekt läuft, lässt sich über die HTTP-Trigger prüfen — GetInfo liefert die Version, CheckList und Validate prüfen Mappings und Konfiguration. Die Details dazu stehen unter Installation.

3 - Konfiguration

Die vier Azure Tables als zentrales Konfigurationssystem: Verbindungen, Jobs, Mappings und Watermarks.

Die dsyr.DataBridge hat keine Konfigurationsdateien. Alle Laufzeit-Einstellungen — Verbindungen zu Quell- und Zielsystemen, Synchronisations-Jobs, Feld-Mappings und Änderungsstände — leben in vier Azure Tables innerhalb der zugehörigen Ressourcengruppe.

Das hat einen entscheidenden Vorteil: Anpassungen werden ohne Redeployment wirksam. Die Tabellen werden beim nächsten Funktionsaufruf automatisch neu eingelesen.

TabelleInhalt
connectionsZugangsdaten zu Quell- und Zielsystemen
jobsWelche Tabellen werden wann in welche Richtung synchronisiert
mappingsWelche Felder werden wie übertragen und transformiert
watermarksLetzter synchronisierter Stand pro Job

3.1 - Verbindungen (connections)

Die connections-Tabelle speichert Zugangsdaten zu allen Quell- und Zielsystemen.

Die Tabelle connections ist die Grundlage jeder DataBridge-Konfiguration. Sie enthält die Zugangsdaten zu allen Systemen, mit denen die DataBridge kommuniziert — sowohl Quell- als auch Zielsysteme.


Tabellenstruktur

SpalteWertBeschreibung
PartitionKeyconnectionMuss immer connection lauten
RowKeyfrei wählbarEindeutiger Name dieser Verbindung. Wird in der jobs-Tabelle als Referenz verwendet.
TimestampautomatischSystemseitig gesetzt, nicht bearbeitbar
TypeConnector-TypGibt an, um welches System es sich handelt (siehe Tabelle unten)
JsonJSON-ObjektSystem-spezifische Verbindungsparameter (unterscheidet sich je nach Type)

Unterstützte Connector-Typen

Type-WertSystemLesenSchreiben
D365BCDynamics 365 Business Central✅✅
D365CEDynamics 365 / Dataverse✅✅
D365NAVMicrosoft Dynamics NAV✅✅
MSSQLMicrosoft SQL Server⚠️ Preview—
MYSQLMySQL / MariaDB⚠️ Preview—
ORACLEOracle Database⚠️ Preview—
PGSQLPostgreSQL⚠️ Preview—
OPENAPIGenerische REST-API über OpenAPI-Spezifikation✅✅ (inkl. Löschen)

Universelle JSON-Parameter

Diese Parameter sind für alle Connector-Typen gültig und werden in das Json-Feld eingetragen. Alle sind optional:

ParameterStandardwertBeschreibung
keyVaultUrl(leer)URL zu einem Azure-Key-Vault-Secret. Wenn gesetzt, werden alle übrigen Parameter aus dem Secret geladen — das Json-Feld selbst enthält dann nur diesen einen Eintrag.
maxPageSize5000Maximale Anzahl Datensätze pro Abruf-Seite. Kann bei Performance-Problemen reduziert werden.
languagede-DEKultur für die Interpretation von Zahlen- und Datumsformaten.
httpClientTimeoutSeconds100Zeitlimit für einzelne Aufrufe in Sekunden. Relevant bei langsamen Quell- oder Zielsystemen.

Zusätzliche Parameter für HTTP-Konnektoren (D365BC, D365NAV, OpenAPI)

Diese Parameter stehen allen HTTP-basierten Konnektoren zur Verfügung, auch wenn sie bislang vor allem für den OpenAPI-Konnektor relevant sind:

ParameterStandardwertBeschreibung
apiKeyHeader(leer)Name des HTTP-Headers für API-Key-Authentifizierung (z. B. X-API-Key). Gesetzt ⇒ API-Key-Authentifizierung wird verwendet.
apiKeyValue(leer)Wert des API-Keys
tokenUrl(leer)Eigener OAuth2-Token-Endpunkt. Ohne Angabe wird weiterhin der Microsoft-Login-Endpunkt verwendet.
authmethod(leer)Erzwingt eine Authentifizierungsmethode: BasicAuthentication, OAuth2, APIKey oder ManagedIdentity (Groß-/Kleinschreibung egal). Ohne Angabe wird die Methode aus den vorhandenen Zugangsdaten abgeleitet. ManagedIdentity wird nie automatisch gewählt, sondern nur über diesen Parameter. Ein unbekannter Wert führt zu einem Fehler.

JSON-Parameter je Connector-Typ

D365BC — Business Central

Für Cloud-Umgebungen (OAuth2 Client Credentials):

{
  "clientId": "<App-Registration Client ID>",
  "clientSecret": "<App-Registration Client Secret>",
  "tenantId": "<Entra Tenant ID>",
  "environment": "<BC-Umgebungsname, z. B. production>",
  "companyId": "<BC Company ID (GUID)>"
}

Für OnPremise-Umgebungen (Basic Auth):

{
  "userName": "<domain\\benutzername>",
  "password": "<Passwort>",
  "url": "https://<server>:<port>/<pfad>"
}

Zusätzlicher optionaler Parameter:

ParameterStandardwertBeschreibung
scopehttps://api.businesscentral.dynamics.com/.defaultOAuth2-Scope beim Token-Abruf. Nur anzupassen, wenn ein abweichender Endpunkt genutzt wird.

Die Auswahl des Authentifizierungsverfahrens erfolgt automatisch: Sind userName und password gesetzt, verwendet die DataBridge Basic Auth, andernfalls OAuth2 Client Credentials aus clientId, clientSecret und tenantId.

D365CE — Dataverse / Customer Engagement

{
  "connectionstring": "<XRM-Connection-String>"
}

Alternativ über Managed Identity (ohne Secret):

{
  "url": "https://<org>.crm4.dynamics.com",
  "clientid": "<Client-ID der user-assigned Identity, optional>"
}

Ohne clientid wird die system-zugewiesene Identity verwendet. Ist connectionstring gesetzt, hat er immer Vorrang. Details siehe D365CE.

Das Format des XRM-Connection-Strings beschreibt die Microsoft-Dokumentation.

Beispiel:

AuthType=ClientSecret;Url=https://<org>.crm4.dynamics.com;ClientId=<id>;ClientSecret=<secret>

D365NAV — Microsoft Dynamics NAV

Für Cloud-Umgebungen (OAuth2 Client Credentials):

{
  "clientId": "<App-Registration Client ID>",
  "clientSecret": "<App-Registration Client Secret>",
  "tenantId": "<Entra Tenant ID>",
  "environment": "<NAV-Umgebungsname>",
  "companyId": "<Company ID (GUID)>"
}

Für OnPremise-Umgebungen:

{
  "userName": "<domain\\benutzername>",
  "password": "<Passwort>",
  "url": "https://<server>:<port>/<pfad>/",
  "baseAddress": "https://<server>:<port>/",
  "authenticationMethod": "NTLM"
}

Zusätzliche NAV-Parameter:

ParameterStandardwertBeschreibung
scopehttps://api.businesscentral.dynamics.com/.defaultOAuth2-Scope beim Token-Abruf
baseAddress(leer)Basis-Adresse für den NTLM-Credential-Cache. Nur bei authenticationMethod = NTLM relevant.
authenticationMethodBasicSiehe Hinweis unten

OPENAPI — generischer REST-Konnektor

{
  "baseUrl": "https://api.example.com",
  "openApiSpecUrl": "https://api.example.com/openapi.yaml",
  "clientId": "<Client-ID>",
  "clientSecret": "<Client-Secret>",
  "tenantId": "<Entra Tenant ID>",
  "tokenUrl": "https://api.example.com/oauth/token",
  "apiKeyHeader": "X-API-Key",
  "apiKeyValue": "<API-Key>",
  "customHeaders": { "X-Custom-Header": "wert" }
}

Vollständige Parameterliste, Authentifizierungsauswahl, Paginierung und Besonderheiten: Konnektoren → OpenAPI.

SQL-Datenbanken (MSSQL, MYSQL, ORACLE, PGSQL)

Alle SQL-Konnektoren verwenden einen einzelnen Connection String:

{
  "connectionstring": "<datenbankspezifischer Connection String>"
}

Zusätzlicher optionaler Parameter:

ParameterStandardwertBeschreibung
commandTimeOut30Zeitlimit für einzelne SQL-Befehle in Sekunden. Bei großen Tabellen oder langsamen Abfragen zu erhöhen.

Diagnose-Schalter (D365BC und D365NAV)

Für die Fehlersuche lassen sich zusätzliche Protokollierungen einschalten. Alle drei Schalter stehen standardmäßig auf false:

ParameterBeschreibung
logCreateRequestsProtokolliert die vollständigen Anfragen beim Anlegen von Datensätzen
logUpdateRequestsProtokolliert die vollständigen Anfragen beim Aktualisieren von Datensätzen
logReceivedDataProtokolliert die vollständigen Antwortdaten des Systems

Key Vault — Verbindungsdaten sicher speichern

Statt Credentials direkt in Table Storage zu speichern, kann ein Azure-Key-Vault-Secret referenziert werden. Das Json-Feld enthält dann nur die Referenz:

{
  "keyVaultUrl": "https://<vault>.vault.azure.net/secrets/<secret-name>/"
}

Das Secret selbst enthält das vollständige JSON-Objekt der Verbindung — also alle Parameter, die sonst direkt im Json-Feld stünden. Beim Verbindungsaufbau lädt die DataBridge das Secret und ersetzt damit die gesamte Verbindungskonfiguration.

Optional lässt sich eine bestimmte Secret-Version adressieren, indem die Versions-ID an den Pfad angehängt wird.


Hinweise

  • Der RowKey ist der Bezeichner, der in der jobs-Tabelle unter SourceConnection bzw. TargetConnection eingetragen wird. Sprechende Namen wählen (z. B. bc-produktion, dataverse-test).
  • Eine Verbindung kann von mehreren Jobs gleichzeitig genutzt werden.
  • Beim Start prüft die DataBridge jede Verbindung mit einem Verbindungstest. Schlägt er fehl, bricht der laufende Aufruf ab und die Initialisierung wird beim nächsten Aufruf erneut versucht.
  • Änderungen in der connections-Tabelle werden bei der nächsten Initialisierung eingelesen — kein Redeployment nötig.

3.2 - Jobs (jobs)

Die jobs-Tabelle definiert, welche Daten wann und in welche Richtung synchronisiert werden.

Ein Job beschreibt genau eine Synchronisationsrichtung zwischen zwei Systemen: er verbindet eine Quelltabelle mit einer Zieltabelle, legt den Zeitplan fest und verweist auf das zugehörige Mapping.


Tabellenstruktur

SpaltePflichtBeschreibung
PartitionKey✅Muss immer job lauten.
RowKey✅Eindeutiger Jobname. Wird im Logging und als Referenz für ResponseJobs verwendet.
IsActive✅true = Job wird von GetChanges ausgeführt. false = Job wird nur indirekt (als ResponseJob) aufgerufen.
SourceConnection✅RowKey-Referenz auf einen Eintrag in der connections-Tabelle (Quelldaten).
TargetConnection✅RowKey-Referenz auf einen Eintrag in der connections-Tabelle (Zieldaten).
Mapping✅PartitionKey-Referenz auf eine Gruppe von Einträgen in der mappings-Tabelle.
Source—Quellressource (Tabelle, Entität, Pfad oder Aktion). Überschreibt die SourceTable der Mapping-Zeilen für diesen Job (siehe unten).
Target—Zielressource (Tabelle, Entität, Pfad oder Aktion). Überschreibt die TargetTable der Mapping-Zeilen für diesen Job (siehe unten).
Order✅Ganzzahl. Legt die Ausführungsreihenfolge fest — muss über alle Jobs eindeutig sein.
WatermarkField—Feldname im Quellsystem, der als Änderungsindikator genutzt wird (meistens ein Datums-/Zeitstempelfeld). Nur bei WatermarkType = date oder datetime erforderlich; bei string (Dataverse Change Tracking) bleibt das Feld leer.
WatermarkType✅Typ des Watermark-Wertes: date, datetime oder string (Details: Watermarks).
Filter—Filterausdruck für die Quellanfrage. Format ist systemabhängig (siehe unten).
Schedule—CRON-Ausdruck (6-stellig, NCrontab). Wenn leer, läuft der Job bei jedem Timer-Aufruf.
Action—Steuert, welche Änderungsarten gelesen werden, oder benennt eine Aktion. Leer = NewUpdate. Wirkt bei den CRUD-Werten (Delete, DeleteOnUpdate) nur bei D365CE als Quellsystem (siehe unten); ein nicht erkannter Wert schaltet den Job auf InvokeAction (siehe unten).
Priority—Standard (Vorgabe), High oder Low. Steuert die Service-Bus-Queue.
ResponseJob—RowKey eines anderen Jobs, der nach erfolgreichem Schreiben automatisch ausgeführt wird.
IsValid—Reserviert. Die Spalte kommt in bestehenden Tabellen vor, wird von der DataBridge aber nicht gelesen und hat keine Wirkung.

Ausführungsreihenfolge

Das Feld Order bestimmt, in welcher Reihenfolge die fälligen Jobs bei jedem Timer-Aufruf abgearbeitet werden. Empfehlung: BC-Jobs vor CE-Jobs ausführen, um sicherzustellen, dass IDs aus BC schon in Dataverse existieren, bevor sie referenziert werden.

Order 10: BC → CE (Stammdaten, z. B. Kunden)
Order 20: BC → CE (Bewegungsdaten, z. B. Angebote)
Order 30: CE → BC (Rückmeldungen)

Der Order-Wert muss über alle Jobs eindeutig sein — auch über inaktive ResponseJobs hinweg. Ein doppelter Wert führt dazu, dass die betroffenen Jobs als unvollständig markiert und nicht ausgeführt werden.


Filter-Syntax

Der Filter bestimmt, welche Datensätze aus der Quelle abgerufen werden.

D365BC, D365NAV und SQL — Filter mit Watermark-Platzhalter

Der Filterausdruck wird als Bedingung in die Quellanfrage eingesetzt. Zwei Elemente steuern das Verhalten:

  • %watermark% wird zur Laufzeit durch den gespeicherten Watermark-Wert ersetzt
  • {{ … }} klammert einen Block, der komplett entfällt, wenn die Watermark leer ist
{{lastModifiedDateTime gt %watermark%}}

Ist die Watermark leer (Erstlauf oder manuell geleert), verschwindet der gesamte Block und der Job liest den vollständigen Bestand. Genau dafür ist die Klammerung gedacht.

Richtig kombiniert wird, indem die Zusatzbedingung außerhalb der Klammer steht und das and mit in den Watermark-Block wandert:

blocked eq false {{and lastModifiedDateTime gt %watermark%}}
WatermarkErgebnis
gesetztblocked eq false and lastModifiedDateTime gt 2026-08-01T10:00:00.000Z
leerblocked eq false

Beide Varianten sind gültig — der Erstlauf liest den vollen Bestand, gefiltert auf blocked eq false.

D365CE / Dataverse — JSON-Operationen

Dataverse nutzt die Change Tracking API; ein OData-Filter kommt dort nicht zum Einsatz. Stattdessen erwartet das Filter-Feld ein JSON-Objekt, das nach dem Abruf clientseitig ausgewertet wird — passende Datensätze werden verarbeitet, alle anderen verworfen.

{ "Operations": [ { "Name": "NotNull", "Parameters": { "SourceField": "wysa_number" } } ] }
{ "Operations": [ { "Name": "StatusCode", "Parameters": { "Value": 3 } } ] }
OperationParameterWirkung
EqualsSourceField, ValueFeldwert entspricht dem angegebenen Wert
BooleanSourceField, ValueBoolesches Feld entspricht dem angegebenen Wert
OptionSetValueSourceField, ValueOptionsset-Feld entspricht dem angegebenen Wert
NotNullSourceFieldFeld ist gefüllt
IsNullSourceFieldFeld ist leer
StatusCodeValueDatensatz hat den angegebenen Statuscode

Ein nicht aufgeführter Operationsname führt zu einem Fehler (Undefined FilterOperation). Die unter SourceField genannten Felder werden automatisch mit abgefragt — sie müssen nicht zusätzlich im Mapping stehen.


Schedule — jobbezogener CRON-Ausdruck

Wenn ein Job seltener als der globale Timer laufen soll, kann ein eigener CRON-Ausdruck eingetragen werden. Die DataBridge prüft anhand von LastCronRun im Watermark, ob das Intervall bereits abgelaufen ist.

Beispiele:

ScheduleBedeutung
0 */2 * * * *Alle 2 Minuten
0 */15 * * * *Alle 15 Minuten
0 0 * * * *Stündlich
0 0 6 * * *Täglich um 06:00 Uhr
(leer)Greift bei jedem Timer-Aufruf

Der globale Timer (GetChangesTimerExpression) steht bei einer Neuinstallation auf */15 * * * * * — also alle 15 Sekunden. Ein Job ohne eigenen Schedule wird damit bei jedem dieser Läufe geprüft.


Priorität

WertService-Bus-QueueTypischer Einsatz
HighsbqhighexportZeitkritische Stammdaten (Preise, Kundenstatus)
StandardsbqexportNormalbetrieb (Vorgabe, auch bei leerem oder unbekanntem Wert)
LowsbqlowexportGroße Historien-Importe, Bulk-Daten

Jede der drei Queues hat einen eigenen, gleichrangigen Verarbeiter. Sie laufen unabhängig und parallel — dadurch bleibt eine hochpriore Nachricht nicht hinter einem Rückstau niedrigerer Priorität stehen. Eine echte Vorrang-Reihenfolge zwischen den Queues gibt es nicht.


Action — welche Änderungsarten gelesen werden

WertWirkung
(leer) / NewUpdateNur Neuanlagen und Änderungen (Vorgabe, auch bei unbekanntem Wert)
DeleteNur Löschungen
DeleteOnUpdateLöschungen, die im Quellsystem als Änderung auftreten
AllNeuanlagen, Änderungen und Löschungen in einem Job
ein beliebiger anderer WertDer Job wird als Aktions-Job (InvokeAction) behandelt — siehe unten

InvokeAction — Aktions-Jobs

(neu) Enthält Action weder einen leeren Wert noch den Namen eines bekannten JobActionType (NewUpdate, Delete, TimeStamp, DeleteOnUpdate), wird der Job zu einem Aktions-Job: Statt eines CRUD-Zyklus ruft er einen benannten, nicht-CRUD-Endpunkt auf. Der Wert von Action wird dabei unverändert als Aktionsname übernommen — z. B. ein OpenAPI-operationId oder der Name eines CE-OrganizationRequest.

Action = SendByEInvoice

Aktuell implementiert der OpenAPI-Konnektor InvokeAction (ResourcePath aus Source/Target bzw. TargetPath + TargetTable der Mapping-Zeilen). Details und Konfigurationsbeispiel: Konnektoren → OpenAPI → InvokeAction.


Source und Target — job-eigene Ressourcenpfade

(neu) Bislang bestimmten ausschließlich die Mapping-Zeilen (SourceTable/TargetTable), welche Ressource ein Job liest bzw. beschreibt. Ein Job kann jetzt zusätzlich eigene Felder Source und Target tragen, die diese Werte überschreiben:

  • Sind Source/Target am Job leer, gilt weiterhin das bisherige Verhalten — die Mapping-Zeilen bestimmen die Ressource (mit einer einmaligen Warnung im Log, die auf die ältere Konfigurationsform hinweist).
  • Ist mindestens eines der beiden Felder gesetzt, wird nur dieses überschrieben; das jeweils andere fällt weiterhin auf den Mapping-Wert zurück.
  • Fehlt sogar das Mapping-Feld am Job komplett, wird aus Source/Target ein synthetisches Mapping ohne TargetKey erzeugt (JOB-<Jobname>) — nützlich für reine Aktions-Jobs ohne Feld-Mapping.
  • Enthält Source/Target einen Schrägstrich (pfad/ressource), wird der Teil vor dem letzten Schrägstrich als Pfad (SourcePath/TargetPath), der Teil danach als Ressourcenname (SourceTable/TargetTable) interpretiert.
Source = v2/clients
Target = v2/client_invoices/{id}/send_by_einvoice

Damit lässt sich ein und dieselbe Mapping-Definition für mehrere Ressourcen bzw. Aktionspfade wiederverwenden, statt für jede Kombination eine eigene Mapping-Gruppe anzulegen.


ResponseJob — Rückschreiben von IDs

Ein ResponseJob löst das Problem, dass Business Central beim Anlegen eines Datensatzes eine neue interne ID vergibt, die anschließend in Dataverse gespeichert werden soll.

Ablauf:

  1. Job A schreibt einen Datensatz von CE nach BC
  2. BC antwortet mit dem neu erzeugten Datensatz inkl. der neuen BC-ID
  3. Die DataBridge führt automatisch Job B (den ResponseJob) aus
  4. Job B liest die BC-ID aus der Antwort (ReadFromMessage-Operation) und schreibt sie zurück nach CE

Konfiguration:

Job A:  IsActive = true
        ResponseJob = "JobB-RowKey"

Job B:  IsActive = false   ← wird nur durch Job A ausgelöst, nie direkt
        SourceConnection = BC-Verbindung
        TargetConnection = CE-Verbindung

Felder, die aus der BC-Antwort gelesen werden, werden im Mapping mit einem Unterstrich-Präfix im SourceField markiert und die Operation ReadFromMessage eingetragen (Details: Mappings → ReadFromMessage).

3.3 - Mappings (mappings)

Die mappings-Tabelle definiert, welche Felder wie zwischen Quell- und Zielsystem übertragen und transformiert werden.

Die mappings-Tabelle ist die mächtigste Konfigurationsebene der DataBridge. Sie legt für jedes Feld fest: woher kommt der Wert, wohin geht er, und wie wird er unterwegs transformiert.

Ein Mapping ist eine Gruppe von Zeilen in der Tabelle — alle mit demselben PartitionKey. Jede Zeile beschreibt genau eine Feld-Zuordnung.


Tabellenstruktur

SpaltePflichtBeschreibung
PartitionKey✅Name des Mappings. Wird im Job unter Mapping referenziert. Im Gegensatz zu jobs und connections ist der PartitionKey hier frei wählbar — er dient der Gruppierung.
RowKey✅Eindeutiger Bezeichner innerhalb des Mappings. Konvention: _quellfeld__zielfeld_
SourceTable✅Name der Quelltabelle/-entität. Muss innerhalb eines Mappings konsistent sein.
SourceField—Name des Quellfeldes. Kann leer sein (bei DefaultValue) oder mit _ beginnen — dann handelt es sich um ein virtuelles Feld.
TargetTable✅Name der Zieltabelle/-entität. Muss innerhalb eines Mappings konsistent sein.
TargetField✅Name des Zielfeldes. Muss dem tatsächlichen API-/Spaltenname entsprechen.
TargetKey—JSON-Objekt. Definiert, wie ein bestehender Datensatz im Zielsystem identifiziert wird (für Upsert-Logik).
SourceOperation—JSON-Objekt der Form { "Operations": [ … ] }. Transformation beim Lesen aus der Quelle.
TargetOperation—JSON-Objekt der Form { "Operations": [ … ] }. Transformationen beim Schreiben ins Ziel.

RowKey-Namenskonvention

Die empfohlene Konvention für den RowKey: _quellfeld__zielfeld_

Beispiele:

_systemid__wysa_bcsystemid_
_lastModifiedDateTime__wysa_lastmodified_
_no__wysa_bcnumber_

Doppelter Unterstrich trennt Quell- vom Zielfeld. Die umgebenden Unterstriche kennzeichnen es als Mapping-RowKey. Diese Konvention ist optional, aber empfohlen für Lesbarkeit und Filterbarkeit.


TargetKey — Upsert-Schlüssel

Der TargetKey bestimmt, wie die DataBridge entscheidet, ob ein Datensatz im Zielsystem angelegt (Insert) oder aktualisiert (Update) wird.

Es gibt vier Typen:

PrimaryKey

Nutzt den systemnativen Primärschlüssel des Zielsystems. Der Schlüsselname wird automatisch aus dem SourceField der Mapping-Zeile übernommen.

{ "KeyType": "PrimaryKey" }

Id

Ähnlich wie PrimaryKey, nutzt jedoch die systemspezifische ID-Eigenschaft des Connectors.

{ "KeyType": "Id" }

CustomKey

Benutzerdefinierter Schlüssel aus einem oder mehreren Feldern. Wird eingesetzt, wenn der Datensatz im Zielsystem nicht über den Primärschlüssel, sondern über eine Geschäftsfeld-Kombination identifiziert wird.

{
  "KeyType": "CustomKey",
  "KeyName": "wysa_number",
  "KeyValues": [
    { "keyName": "wysa_segmentno", "attributeName": "segmentNo", "rank": 1 },
    { "keyName": "wysa_lineno",    "attributeName": "lineNo",    "rank": 2 }
  ]
}
FeldBedeutung bei CustomKey
KeyNameZielfeld, das den zusammengesetzten Schlüsselwert enthält
keyNameZielfeld-Name für diesen Schlüsselteil
attributeNameQuellfeld-Name, aus dem der Wert kommt
StaticValueFester Wert statt eines Quellwertes (siehe unten)
rankReihenfolge der Schlüsselteile innerhalb des Schlüssels (aufsteigend)

Filter

Sucht im Zielsystem nach einem Datensatz, der alle angegebenen Feld-Wert-Kombinationen erfüllt. Im Gegensatz zu CustomKey wird kein explizites Schlüsselfeld benötigt.

{
  "KeyType": "Filter",
  "KeyValues": [
    { "keyName": "mailingGroupCode", "attributeName": "listid",   "rank": 1 },
    { "keyName": "contactNo",        "attributeName": "entityid", "rank": 2 }
  ]
}

Statische Schlüsselwerte

(ab v2.1 — verfügbar für D365CE und D365BC)

Ein Schlüsselteil kann gegen einen fest hinterlegten Wert aufgelöst werden, ohne dass dafür ein Quellfeld existieren muss. Dazu wird statt attributeName das Feld StaticValue gesetzt:

{
  "KeyType": "CustomKey",
  "KeyName": "wysa_category",
  "KeyValues": [
    { "keyName": "wysa_category", "StaticValue": "STANDARD", "rank": 1 }
  ]
}

StaticValue greift nur, wenn attributeName leer ist. Ist beides gesetzt, gewinnt der Quellwert.

Bei Business Central werden statische Werte für die Metadaten-Typen string, guid und int32 unterstützt; andere Typen führen zu einem Fehler.

Mehrere Ziel-Schlüssel mit Rangfolge

(ab v2.1)

Ein Mapping kann mehrere Ziel-Schlüssel besitzen. Sie werden anhand des Feldes KeyRank in aufsteigender Reihenfolge geprüft — sobald ein Schlüssel im Zielsystem einen Datensatz findet, wird dieser verwendet und die übrigen werden übersprungen.

Typischer Einsatz: zuerst über eine externe ID suchen, ersatzweise über eine Nummer.

Die Konfiguration unterscheidet sich je nach Ebene:

EbeneVorgehen
Table-MappingJe Schlüssel eine eigene Mapping-Zeile mit eigenem TargetKey-JSON anlegen. Die DataBridge fasst alle TargetKey-Einträge einer Mapping-Gruppe zusammen und sortiert sie nach KeyRank.
Lookup (TargetOperation)In Parameters statt TargetKey ein Array TargetKeys mit mehreren Schlüsseln angeben.
{
  "KeyType": "CustomKey",
  "KeyRank": 1,
  "KeyName": "wysa_externalid",
  "KeyValues": [
    { "keyName": "wysa_externalid", "attributeName": "externalId", "rank": 1 }
  ]
}

Bestehende Mappings mit einem einzelnen TargetKey funktionieren unverändert — intern wird ein einzelner Schlüssel wie eine Liste mit einem Eintrag behandelt.

KeyAttributeFromExisting

Optionales Feld am TargetKey, ausgewertet bei KeyType: Filter von den Konnektoren für Business Central, NAV und OpenAPI. Der Filter findet nur den bestehenden Zieldatensatz. Für die Item-URL beim Aktualisieren (bei OpenAPI auch beim Löschen) wird der Schlüssel aus dem hier angegebenen Feld dieses Datensatzes übernommen.

  • BC/NAV: Pflicht bei Filter. Guid- und Zahl-Schlüssel werden ohne, alle anderen mit Hochkommas in die URL geschrieben, der Typ kommt aus den Metadaten. Fehlt das Feld im bestehenden Datensatz, bricht die Verarbeitung mit einem Fehler ab.
  • OpenAPI: Optional. Ohne Angabe wird der Primärschlüssel der Spezifikation verwendet (id, Id, ID, uuid oder key).

Der Feldname ist case-sensitiv (id ≠ Id).

IgnoreEmptySourceKey

(ab v2.2, nur D365CE)

Optionales Boolesches Feld am TargetKey. Standard ist false.

{
  "KeyType": "CustomKey",
  "KeyRank": 2,
  "KeyName": "wysa_externalid",
  "IgnoreEmptySourceKey": true,
  "KeyValues": [
    { "keyName": "wysa_externalid", "attributeName": "externalId", "rank": 1 }
  ]
}

Zwei verschiedene Arten von „kein Treffer"

Um das Feld einordnen zu können, muss man zwei Situationen auseinanderhalten, die beide danach aussehen, als wäre ein Schlüssel erfolglos geblieben:

SituationWas passiertVerhalten ohne dieses Feld
Schlüssel lässt sich nicht aufbauen — das Quellfeld ist leer, es gibt gar keinen Wert zum SuchenEs wird nicht gesuchtFehler, die Nachricht schlägt fehl
Schlüssel findet nichts — es wurde gesucht, im Zielsystem existiert der Datensatz noch nichtSuche läuft, bleibt ergebnislosDatensatz wird angelegt — kein Fehler

Die zweite Zeile ist der Normalfall für jeden neuen Datensatz: Beim ersten Übertragen findet kein Schlüssel etwas, und genau deshalb wird angelegt. IgnoreEmptySourceKey ändert daran nichts.

Das Feld wirkt ausschliesslich auf die erste Zeile: Es erklärt einen leeren Quellwert für diesen einen Schlüssel zum Normalfall statt zum Fehler.

Wann das Feld überhaupt zum Tragen kommt

Die Schlüssel werden nach KeyRank der Reihe nach geprüft. Sobald ein Schlüssel einen Datensatz findet, bricht die Suche ab und der Datensatz wird aktualisiert — unabhängig davon, was bei den vorherigen Schlüsseln passiert ist und wie sie konfiguriert sind.

Beispiel mit drei Schlüsseln, bei dem Schlüssel 1 leer und nicht markiert ist:

SchrittErgebnis
Schlüssel 1leer, wird übersprungen — gemerkt: „hier war ein Fehler"
Schlüssel 2leer, markiert — wird übersprungen
Schlüssel 3findet den Datensatz → Suche endet
ErgebnisUpdate. Kein Fehler — der gemerkte Fehler wird gar nicht mehr ausgewertet

Das Feld greift also nur dann, wenn die Liste vollständig durchlaufen wurde und kein einziger Schlüssel einen Datensatz gefunden hat. Erst dann steht die Frage „Fehler oder anlegen?" überhaupt im Raum.

Die Entscheidung am Ende

Hat kein Schlüssel etwas gefunden, entscheidet sich Folgendes:

  • Waren alle leer gebliebenen Schlüssel mit IgnoreEmptySourceKey: true markiert (oder blieb gar keiner leer), wird ein neuer Datensatz angelegt.
  • War auch nur einer der leer gebliebenen Schlüssel nicht markiert, bleibt es beim Fehler.

Das Feld gilt also je Schlüssel: Es hebt die Fehlerbehandlung der übrigen Schlüssel nicht auf. Lassen Sie es bei einem Schlüssel bewusst weg, bleibt ein leerer Wert dort ein Fehler — auch wenn ein anderer Schlüssel des Mappings markiert ist.

Typischer Einsatz

Ein optionales Feld wie eine externe ID ist nicht in jedem Quelldatensatz gefüllt, die Belegnummer dagegen immer. Mit IgnoreEmptySourceKey: true am ID-Schlüssel laufen auch die Datensätze ohne externe ID sauber durch und werden über die Belegnummer gefunden.

Das Feld wird derzeit ausschliesslich vom D365CE-Konnektor ausgewertet. Bei Business Central und NAV bleibt es wirkungslos.


Virtuelle Felder

Beginnt das SourceField mit einem Unterstrich, handelt es sich um ein virtuelles Feld: Es wird nicht aus der Quelltabelle gelesen, sondern zur Laufzeit über eine SourceOperation erzeugt.

Der ResponseJob ist dabei nur einer von mehreren Anwendungsfällen:

AnwendungsfallOperation
Wert aus einer anderen Tabelle nachschlagenLookupValue
Wert aus einer ActivityParty-Beziehung lesenActivityParty
Link auf einen Dataverse-Datensatz erzeugenLinkToRecord
Wert aus der Antwortnachricht lesen (ResponseJob)ReadFromMessage
Zeilen eines Kind-Jobs einbettenEmbedChildren

SourceOperation — Transformation beim Lesen

SourceOperations werden angewendet, bevor der Wert in die Service Bus Queue geschrieben wird. Sie sind connector-spezifisch — nicht jede Operation steht in jedem Connector zur Verfügung.

LookupValue

(verfügbar: D365BC, D365CE, D365NAV)

Liest einen Wert aus einer anderen Tabelle im Quellsystem nach. Beispiel: Aus einem Land-Code soll der ausgeschriebene Ländername übertragen werden.

{
  "Operations": [
    {
      "Name": "LookupValue",
      "Parameters": {
        "LookupType": "Attribute",
        "SourceField": "countryRegionCode",
        "LookupTable": "publisher/product/v1.0/countriesRegions",
        "LookupField": "name",
        "UseCache": true
      }
    }
  ]
}
ParameterBeschreibung
LookupTypeAttribute — liest ein bestimmtes Feld aus dem gefundenen Datensatz. Name — liefert den Anzeigenamen des verknüpften Datensatzes; dann entfallen LookupTable und LookupField.
SourceFieldFeld im Quelldatensatz, das den Suchwert enthält
LookupTableAPI-Pfad der nachzuschlagenden Tabelle
LookupFieldFeld im nachgeschlagenen Datensatz, dessen Wert übertragen wird
UseCachetrue dringend empfohlen — siehe Hinweis

ReadFromMessage

(verfügbar: D365BC, D365NAV, D365CE)

Liest einen Wert aus der ursprünglichen Queue-Nachricht — wird typischerweise in ResponseJobs verwendet, um Felder aus der Antwort des Zielsystems (z. B. eine neu vergebene ID) zurückzuschreiben.

{
  "Operations": [
    { "Name": "ReadFromMessage" }
  ]
}

Zugehörige Zeile in der Tabelle:

SpalteWert
SourceField_id (Unterstrich-Präfix!)
TargetFieldwysa_bcid

ActivityParty

(verfügbar: nur D365CE)

Liest ein Feld über eine ActivityParty-Beziehung aus. Wird für CE-Aktivitäten (E-Mails, Aufgaben) verwendet, bei denen Teilnehmer über den activityparty-Datensatz verknüpft sind.

{
  "Operations": [
    {
      "Name": "ActivityParty",
      "Parameters": {
        "PartyParams": {
          "TypeMask": 2,
          "SourceField": "activityid",
          "TargetField": "wysa_number",
          "TargetTable": "contact"
        }
      }
    }
  ]
}
ParameterBeschreibung
TypeMaskTeilnehmertyp (siehe Hinweis)
SourceFieldFeld mit der Activity-ID
TargetFieldFeld im verknüpften Datensatz, das gelesen werden soll
TargetTableEntität des verknüpften Datensatzes

Werden mehrere Teilnehmer gefunden, werden ihre Werte komma-separiert zusammengefügt.

LinkToRecord

(verfügbar: nur D365CE)

Erzeugt eine anklickbare URL zu einem Datensatz in Dataverse. Nützlich, um in BC oder SQL einen direkten Link auf den zugehörigen CE-Datensatz zu speichern.

{
  "Operations": [
    {
      "Name": "LinkToRecord",
      "Parameters": {
        "BaseUrl": "https://<org>.crm4.dynamics.com/main.aspx?appid=<AppId>&pagetype=entityrecord"
      }
    }
  ]
}

An die angegebene Basis-URL werden automatisch &etn=<entity> und &id=<guid> angehängt.

EmbedChildren

(neu, verfügbar: nur D365CE als Quellsystem)

Bettet die Zeilen eines referenzierten Kind-Jobs in die Nachricht des Elterndatensatzes ein, sodass Eltern- und Kind-Datensätze gemeinsam als eine Nachricht durch den Service Bus laufen und gemeinsam ins Ziel geschrieben werden — z. B. eine Rechnung mit ihren Rechnungspositionen.

{
  "Operations": [
    {
      "Name": "EmbedChildren",
      "Parameters": { "JobName": "invoice-lines" }
    }
  ]
}

Zugehörige Zeile in der Tabelle:

SpalteWert
SourceField_lines (Unterstrich-Präfix, beliebiger Name)
TargetFieldPfad im Zieldatensatz, unter dem das Array landet (z. B. lines)

Der referenzierte Kind-Job liefert SourceConnection, Quellressource und Mapping; sein Filter kann {{...%parent.feld%...}}-Platzhalter enthalten, die zur Abrufzeit gegen den Eltern-Quelldatensatz aufgelöst werden. Beim Schreiben in eine OpenAPI-Zielressource wird daraus ein verschachteltes JSON-Array gebaut und unverändert — ohne Feld-für-Feld-Abgleich gegen ein eventuell vorhandenes Zielarray — mit der Elternanfrage übertragen.


TargetOperation — Transformationen beim Schreiben

TargetOperations werden angewendet, bevor der Wert ins Zielsystem geschrieben wird.

DefaultValue

(alle Konnektoren)

Schreibt immer einen festen Wert ins Zielfeld — unabhängig vom Quellwert. Das SourceField kann in diesem Fall leer gelassen werden.

{
  "Operations": [
    { "Name": "DefaultValue", "Parameters": { "DefaultValue": "2" } }
  ]
}

DefaultValueIfNullOrEmpty

(alle Konnektoren)

Schreibt einen festen Wert nur dann, wenn das Quellfeld leer oder null ist. Ist ein Quellwert vorhanden, wird dieser unverändert verwendet.

{
  "Operations": [
    { "Name": "DefaultValueIfNullOrEmpty", "Parameters": { "DefaultValue": "unbekannt" } }
  ]
}

DefaultValueMap

(alle Konnektoren)

Übersetzt den ermittelten Wert anhand einer Zuordnungsliste. Der Schlüsselvergleich erfolgt ohne Beachtung der Groß-/Kleinschreibung.

{
  "Operations": [
    {
      "Name": "DefaultValueMap",
      "Parameters": {
        "Mapping": [
          { "Key": "DE", "Value": "Deutschland" },
          { "Key": "AT", "Value": "Österreich" }
        ]
      }
    }
  ]
}

Findet sich kein passender Schlüssel, bleibt der Wert unverändert — im Unterschied zu OptionMapping, das in diesem Fall einen Fehler wirft.

Lookup

(nur D365CE)

Befüllt ein Lookup-Feld in Dataverse, indem der verknüpfte Datensatz über einen Schlüssel gesucht wird.

{
  "Operations": [
    {
      "Name": "Lookup",
      "Parameters": {
        "TargetTable": "contact",
        "TargetKey": {
          "KeyType": "CustomKey",
          "KeyName": "wysa_number",
          "KeyValues": [
            { "keyName": "wysa_number", "attributeName": "contactNo", "rank": 1 }
          ]
        }
      }
    }
  ]
}

TargetTable ist zwingend — ohne diesen Parameter bricht die Auflösung ab.

ParameterBeschreibung
TargetTableEntität des zu suchenden Datensatzes (Pflicht)
TargetKeyEinzelner Schlüssel, Syntax wie beim TargetKey
TargetKeysArray mehrerer Schlüssel mit KeyRank (ab v2.1, siehe oben)
DoNotCreatetrue verhindert das automatische Anlegen (ab v2.1, siehe unten)
SetNullIfEqualsValueOfFieldSetzt das Lookup auf null, wenn der Wert dem angegebenen Feld der Nachricht entspricht — Schutz gegen Selbstreferenzen
DefaultValueFester Wert; überspringt die Auflösung vollständig

OptionMapping

(verfügbar: D365CE; bei D365BC und D365NAV für String-Felder)

Übersetzt Textwerte aus dem Quellsystem in numerische Optionset-Werte von Dataverse (und umgekehrt). Die Mapping-Liste enthält alle bekannten Schlüssel-Wert-Paare.

{
  "Operations": [
    {
      "Name": "OptionMapping",
      "Parameters": {
        "Mapping": [
          { "Key": "Blank",                       "Value": "" },
          { "Key": "Prospect",                    "Value": "383380000" },
          { "Key": "First_x002D_time_x0020_Donor","Value": "383380001" },
          { "Key": "Recurring_x0020_Donor",       "Value": "383380002" }
        ]
      }
    }
  ]
}

Für MultiSelect-Optionsets ist zusätzlich der Parameter OptionDelimiter anzugeben — er trennt die Einzelwerte im Quellstring. Ohne ihn schlägt die Aufteilung fehl.


Datentyp-Umwandlung

Welche Quelltypen ein Zielfeld akzeptiert, hängt vom Connector und vom Zielfeld-Typ ab. Die vollständigen Matrizen stehen auf den Konnektor-Seiten.

Zwei Punkte, die häufig zu Rückfragen führen:

String zu Dezimalzahl (ab v2.1) — Dezimalfelder akzeptieren jetzt auch Quellwerte vom Typ string. Diese werden immer im Format en-US interpretiert, also mit Punkt als Dezimaltrennzeichen. Ein leerer String wird zu null, ein nicht konvertierbarer Wert erzeugt einen Fehler.

Culture bei echten Zahlentypen — kommt der Wert dagegen bereits als decimal oder money, gilt die im Verbindungs-JSON eingestellte language (Vorgabe de-DE).


Spezialfälle

Feld ohne Quellwert (reiner Standardwert)

Soll ein Zielfeld immer mit einem festen Wert befüllt werden, ohne dass ein Quellfeld existiert:

SpalteWert
SourceField(leer)
TargetFieldwysa_datasource
TargetOperationDefaultValue mit "DefaultValue": "DataBridge"

Wert aus der Antwortnachricht lesen (ResponseJob)

SpalteWert
SourceField_id
TargetFieldwysa_bcid
SourceOperationReadFromMessage

Siehe auch Virtuelle Felder und Jobs → ResponseJob.

Mehrere Operationen in einer Zeile

Ob mehrere Einträge im Operations-Array wirken, hängt davon ab, um welche Art von Operation es sich handelt:

Verhalten bei mehreren Einträgen
SourceOperationNur der erste Eintrag wird ausgeführt, alle weiteren verworfen
TargetOperationMehrere Einträge sind zulässig; sie werden über ihren Namen aufgelöst, nicht über die Reihenfolge. Es gilt die Rangfolge DefaultValue → DefaultValueIfNullOrEmpty → DefaultValueMap.

Eine Verkettung im Sinne von „erst A, dann B auf das Ergebnis von A" gibt es bei SourceOperations damit nicht.

3.4 - Watermarks (watermark)

Die watermark-Tabelle speichert den letzten synchronisierten Stand pro Job — und verhindert so doppelte Übertragungen.

Die Tabelle watermark ist die Gedächtnisfunktion der DataBridge. Nach jedem Job-Lauf wird hier gespeichert, bis zu welchem Stand die Daten gelesen wurden. Beim nächsten Lauf startet der Quellabruf genau an diesem Punkt.

Diese Tabelle wird vom Deployment angelegt und zur Laufzeit automatisch befüllt — manuelle Einträge sind normalerweise nicht nötig. Es gibt jedoch Situationen, in denen ein gezielter Eingriff sinnvoll ist.


Tabellenstruktur

SpalteWertBeschreibung
PartitionKeywatermarkFest vorgegeben — immer watermark
RowKeyJobnameEntspricht dem RowKey des zugehörigen Jobs in der jobs-Tabelle
TimestampautomatischSystemseitig gesetzt, nicht bearbeitbar
WatermarkLetzter StandFormat abhängig vom WatermarkType des Jobs (siehe unten)
LastCronRunZeitstempelBeginn der letzten Ausführung des Jobs. Basis für die CRON-Intervallprüfung.

Watermark-Typen

Der Typ wird im Job über das Feld WatermarkType festgelegt und bestimmt, wie der gespeicherte Wert interpretiert wird.

datetime — Vollständiger Zeitstempel

Für Quellsysteme, die Änderungen über ein Datum-/Uhrzeit-Feld tracken. Typisch für Business Central (lastModifiedDateTime).

Format: yyyy-MM-ddTHH:mm:ss.fffZ

Beispielwert: 2026-05-31T08:15:00.000Z

Der Filter im Job sieht dann so aus:

{{lastModifiedDateTime gt %watermark%}}

date — Nur Datum

Für Quellsysteme, bei denen nur das Datum (ohne Uhrzeit) relevant ist. Typisch für NAV-Systeme mit datumbasiertem Tracking.

Format: yyyy-MM-dd

Beispielwert: 2026-05-31

string — Change Tracking Token

Für Dataverse / D365CE: Die Change Tracking API gibt bei jedem Abruf einen opaken Token zurück, der den aktuellen Änderungsstand repräsentiert. Dieser Token wird als string gespeichert und beim nächsten Lauf direkt an die API übergeben — kein Datumsvergleich, kein Risiko fehlender Änderungen.

Format: opake Zeichenkette (von Dataverse vergeben)

Beispielwert: 892290!08/10/2026 10:00:00

Schreibweise und unbekannte Typen

Der Typ wird ohne Beachtung von Groß-/Kleinschreibung und ohne umgebende Leerzeichen erkannt — datetime, DateTime und DATETIME sind gleichwertig.


Wann wird der Watermark aktualisiert?

Der Watermark wird am Ende der Erkennungsphase fortgeschrieben — also sobald alle gefundenen Änderungen in die Queue geschrieben sind, und bevor das Zielsystem beschrieben wurde. Für die Zustellung ins Ziel sorgen danach die Queue-Mechanismen (Wiederholung, Dead-Letter).

Im Einzelnen:

  1. Beim Lesen führt die DataBridge den Änderungsmarker (ChangeTracker) der verarbeiteten Datensätze mit
  2. Bei date und datetime wird jeweils der höhere der beiden Werte behalten, bei string gewinnt immer der zuletzt gelesene Token — ein opaker Token hat keine Ordnung
  3. Nach dem Leeren des letzten Batches wird der Endstand in die Tabelle geschrieben
  4. LastCronRun wird auf den Zeitpunkt gesetzt, zu dem der Job begonnen hat

Bricht ein Job mit einem Fehler ab, bleibt der Watermark auf dem zuletzt geschriebenen Stand. Beim nächsten Lauf wird ab diesem Punkt erneut gelesen.

Zeitzonen

Zeitstempel werden zeitzonenunabhängig verglichen. Bis v2.0 wurde die Zeitangabe in die lokale Zeit des Servers umgerechnet — beim Typ date konnte das über eine Tagesgrenze springen und das Vergleichsergebnis je nach Betriebsstandort verändern.


Erster Lauf — kein Eintrag vorhanden

Existiert für einen Job noch kein Watermark-Eintrag, legt die DataBridge ihn beim ersten Lauf automatisch an. Als Startpunkt wird ein möglichst früher Wert verwendet:

TypInitialwert
datetime0001-01-01T00:00:00.000Z
date0001-01-01
string(leer)

Das bedeutet: der erste Lauf lädt alle vorhandenen Datensätze aus dem Quellsystem. Bei großen Datenmengen sollte dies zu einem Zeitpunkt mit geringer Last geschehen.


Manueller Eingriff — Wann und warum

Vollständigen Neulauf erzwingen

Den Wert in der Watermark-Spalte leeren oder auf den Initialwert zurücksetzen. Beim nächsten Lauf werden alle Datensätze erneut übertragen.

Typische Anwendungsfälle:

  • Mapping wurde grundlegend geändert und alle Datensätze sollen neu gemappt werden
  • Zielsystem wurde zurückgesetzt oder neu aufgebaut
  • Datenverlust im Zielsystem

Partiellen Neulauf ab bestimmtem Datum

Den Watermark-Wert manuell auf ein Datum setzen, ab dem neu gelesen werden soll:

2026-01-01T00:00:00.000Z

Damit werden nur Datensätze übertragen, die seit diesem Datum geändert wurden.

Bei WatermarkType = string ist das nicht möglich — der Token stammt aus Dataverse und lässt sich nicht sinnvoll von Hand konstruieren. Dort bleibt nur das vollständige Leeren.

LastCronRun zurücksetzen

Wenn ein Job mit eigenem Schedule nicht zum erwarteten Zeitpunkt ausgeführt wurde, kann LastCronRun manuell auf einen früheren Wert gesetzt werden. Die DataBridge erkennt dann, dass das Intervall abgelaufen ist, und führt den Job beim nächsten Timer-Aufruf aus.

4 - Konnektoren

Die unterstützten Quell- und Zielsysteme: Dynamics 365, Dataverse, NAV und SQL-Datenbanken.

Jedes Quell- oder Zielsystem wird über einen Konnektor angebunden. Ein Konnektor übernimmt die systemspezifische Kommunikation: Authentifizierung, Datenabfrage, Änderungserkennung und das Schreiben von Datensätzen.

Unterstützte Konnektoren

KonnektorBeschreibungLesenJob-FilterSchreibenStatus
D365BCDynamics 365 Business Central✅✅✅Freigegeben
D365CEDynamics 365 / Dataverse✅✅✅Freigegeben
D365NAVMicrosoft Dynamics NAV✅✅✅Freigegeben
MSSQLMicrosoft SQL Server⚠️ Preview✅—Preview
MySQLMySQL / MariaDB⚠️ Preview——Preview, nicht produktionsreif
OracleOracle Database⚠️ Preview——Preview, nicht produktionsreif
PostgreSQLPostgreSQL⚠️ Preview——Preview
OpenAPIGenerische REST-API über OpenAPI-Spezifikation✅⚠️ API-abhängig✅ (inkl. Löschen)In Entwicklung

Die Konnektoren D365BC, D365CE und D365NAV kommunizieren über HTTP/OData-APIs. MSSQL, MySQL, Oracle und PostgreSQL verwenden direkte Datenbankverbindungen (ADO.NET). Der OpenAPI-Konnektor kommuniziert ebenfalls über HTTP, leitet Pfade, Wrapper-Schlüssel und Feldtypen jedoch aus einer OpenAPI-Spezifikation ab, statt sie fest zu verdrahten.


Schreiben in SQL-Systeme

Keiner der vier SQL-Konnektoren unterstützt das Schreiben. Ein SQL-System als TargetConnection führt zur Laufzeit zu einem Abbruch. Die SQL-Konnektoren sind derzeit ausschließlich als Quellsystem einsetzbar.


Der OpenAPI-Konnektor

Der OpenAPI-Konnektor unterscheidet sich grundlegend von den übrigen HTTP- Konnektoren: Er ist nicht auf ein bestimmtes System zugeschnitten, sondern bindet beliebige REST-APIs an, für die eine OpenAPI-Spezifikation vorliegt. Pfade, Wrapper-Schlüssel und Feldtypen werden zur Laufzeit aus der Spezifikation aufgelöst, statt je System hartcodiert zu sein. Er befindet sich aktuell noch in Entwicklung.


Versionsangaben

Die auf den Konnektor-Seiten genannten Mindestversionen sind getestete Versionsstände. Die DataBridge prüft die Version des angebundenen Systems nicht — ältere Stände können funktionieren, sind aber nicht abgesichert. Bei Fragen zu einer konkreten Version bitte WYSA kontaktieren.

4.1 - D365BC — Business Central

Konnektor für Microsoft Dynamics 365 Business Central. Unterstützt Lesen und Schreiben über OData v4.

Der D365BC-Konnektor verbindet die DataBridge mit Microsoft Dynamics 365 Business Central über die OData-v4-REST-API (API-Version 2.0). Er unterstützt sowohl Cloud-Umgebungen (OAuth2) als auch OnPremise-Installationen (Basic Auth).

EigenschaftWert
Lesen✅ unterstützt
Schreiben✅ unterstützt
Job-Filter✅ wird als OData-$filter an BC übergeben
StatusFreigegeben
Getestete MindestversionBusiness Central v17.0 (Oktober 2020)

Verbindungsparameter

Cloud-Umgebung (empfohlen)

{
  "clientId": "<App-Registration Client ID>",
  "clientSecret": "<App-Registration Client Secret>",
  "tenantId": "<Entra Tenant ID>",
  "environment": "<BC-Umgebungsname, z. B. production>",
  "companyId": "<Company ID (GUID)>"
}

Cloud-Umgebung mit Managed Identity (ab v2.3)

Ohne Client Secret, über die Managed Identity der Function App:

{
  "authmethod": "ManagedIdentity",
  "clientId": "<Client-ID der user-assigned Identity, optional>",
  "environment": "<BC-Umgebungsname, z. B. production>",
  "companyId": "<Company ID (GUID)>"
}
  • Ohne clientId wird die system-zugewiesene Identity der Function App verwendet, mit clientId die benutzer-zugewiesene.
  • clientSecret und tenantId sind nicht nötig. Der Token wird mit dem scope der Connection angefordert und rechtzeitig vor Ablauf erneuert.
  • Managed Identity wird nur über authmethod aktiviert, nie automatisch.
  • Voraussetzung: Die Identity ist in Business Central unter Microsoft-Entra-Anwendungen angelegt, aktiviert und mit Berechtigungssätzen versehen.

OnPremise-Umgebung

{
  "userName": "<domain\\benutzername>",
  "password": "<Passwort>",
  "url": "https://<server>:<port>/<pfad>",
  "companyId": "<Company ID (GUID)>"
}

Ist url gesetzt, wird daraus die Basisadresse gebildet (<url>/api/) und environment nicht ausgewertet. Ist url leer, verwendet der Konnektor den Cloud-Endpunkt und bildet die Adresse aus environment.

Die Auswahl des Authentifizierungsverfahrens erfolgt automatisch: Sind userName und password gesetzt, wird Basic Auth verwendet, andernfalls OAuth2 Client Credentials aus clientId, clientSecret und tenantId. Mit authmethod lässt sich die Methode ausdrücklich festlegen.

Optionale Parameter

Gelten für Cloud und OnPremise:

ParameterStandardwertBeschreibung
scopehttps://api.businesscentral.dynamics.com/.defaultOAuth2-Scope beim Token-Abruf
maxPageSize5000Maximale Datensätze pro Abruf-Seite
languagede-DEKultur für die Interpretation von Zahlen- und Datumsformaten
httpClientTimeoutSeconds100Zeitlimit für einzelne HTTP-Aufrufe in Sekunden
keyVaultUrl(leer)Key-Vault-Secret-URL; ersetzt sämtliche übrigen Parameter
logCreateRequestsfalseProtokolliert die vollständigen Anfragen beim Anlegen
logUpdateRequestsfalseProtokolliert die vollständigen Anfragen beim Aktualisieren
logReceivedDatafalseProtokolliert die vollständigen Antwortdaten

Die drei Diagnose-Schalter erzeugen erhebliche Datenmengen und können Geschäftsdaten im Klartext protokollieren — nur gezielt für die Dauer einer Analyse aktivieren. Vollständige Beschreibung unter Verbindungen.


Tabellen- und Feldnamen

SourceTable / TargetTable

Der Tabellenname leitet sich aus dem API-Pfad der Business-Central-API v2.0 ab — ohne den Company-Teil der URL.

API-TypURL-StrukturSourceTable
Standard-API.../api/v2.0/companies({id})/customersv2.0/customers
Custom API.../api/{publisher}/{product}/{version}/entities{publisher}/{product}/{version}/entities

SourceField / TargetField

Exakter Feldname aus der BC-API-Seite (case-sensitive). Zu finden in der BC-API-Dokumentation oder über den API-Explorer in Business Central.


Unterstützte TargetKey-Typen

TypBeschreibung
PrimaryKeySystemnativer BC-Primärschlüssel. Der Wert wird in Anführungszeichen gesetzt.
IdBC-interne ID (GUID). Der Wert wird ohne Anführungszeichen eingesetzt.
CustomKeyBenutzerdefinierter Schlüssel über ein oder mehrere Felder
FilterSuche über Feldwert-Kombination

Statische Schlüsselwerte (ab v2.1)

In einem KeyValue kann statt attributeName ein StaticValue angegeben werden. Der Schlüsselteil wird dann nicht aus dem Quelldatensatz gelesen, sondern fest gesetzt.

{
  "KeyType": "CustomKey",
  "KeyName": "wysa_number",
  "KeyValues": [
    { "keyName": "sourceSystem", "StaticValue": "DataBridge",   "rank": 1 },
    { "keyName": "customerNo",   "attributeName": "bcCustomerNo", "rank": 2 }
  ]
}

Statische Werte werden für die BC-Metadatentypen string, guid und int32 unterstützt. Bei jedem anderen Typ des Schlüsselfeldes bricht die Verarbeitung mit einem Fehler ab (Metadata type … not yet implemented for CreateKeyString).


SourceOperations

LookupValue

Liest einen Wert aus einer anderen BC-API-Tabelle nach.

{
  "Operations": [
    {
      "Name": "LookupValue",
      "Parameters": {
        "LookupType": "Attribute",
        "SourceField": "countryRegionCode",
        "LookupTable": "publisher/product/v1.0/countriesRegions",
        "LookupField": "name",
        "UseCache": true
      }
    }
  ]
}

LookupType kennt die beiden Werte Attribute und Name. Business Central unterstützt ausschließlich Attribute; der Wert Name führt zu einem Abbruch (SourceOperation for Lookups). Name ist dem D365CE-Konnektor vorbehalten.

ReadFromMessage

Liest einen Wert aus der BC-Antwort — nur in ResponseJobs. Das SourceField muss einen _-Prefix haben.

{
  "Operations": [
    {
      "Name": "ReadFromMessage"
    }
  ]
}

Datentypen — Zieltyp und akzeptierte Quelltypen

Beim Schreiben bestimmt der Datentyp des Zielfeldes in den BC-Metadaten, welche Umwandlung angewendet wird. Der Quelltyp stammt aus dem Feldtyp des Quellsystems und entscheidet, ob die Umwandlung gelingt.

Zieltyp (BC-Metadaten)Akzeptierte QuelltypenVerhalten und Sonderfälle
Booleanboolean, stringLeerwert → null. Ein nicht als Wahrheitswert lesbarer Text führt zum Abbruch.
Datedate, datetime, stringAusgabe im Format yyyy-MM-dd. Leerwert → 0001-01-01. Nicht lesbares Datum → Abbruch.
DateTimeOffsetdate, datetime, stringAusgabe im Format yyyy-MM-ddTHH:mm:ss.fffZ. Leerwert → 0001-01-01T00:00:00.000Z.
Decimaldecimal, money, doubleInterpretation mit der Kultur aus language. Leerwert → null.
Decimalstring (ab v2.1)Interpretation immer als en-US (Punkt als Dezimaltrennzeichen), unabhängig von language.
Double (ab v2.1)decimal, money, doubleInterpretation mit der Kultur aus language. string wird nicht akzeptiert und führt zum Abbruch.
Guidguid, string, entityreferenceLeerwert → 00000000-0000-0000-0000-000000000000, nicht null.
Int32int32, optionsetvalue, stringLeerwert → null. Nicht als Ganzzahl lesbarer Wert → Abbruch.
Stringalle QuelltypenWert wird getrimmt. Leerwert → leerer String, das Zielfeld wird also geleert. Liegt eine OptionMapping-TargetOperation vor, wird stattdessen der zugeordnete Wert geschrieben; ein unbekannter Quellwert führt zum Abbruch.

Jeder in der Tabelle nicht genannte BC-Zieltyp führt beim Schreiben zu einem Abbruch (No Resolver defined for edmPrimitiveTypeKind …).


Schreibvorgang und Fehlerverhalten

Vor jedem Schreibvorgang sucht der Konnektor den bestehenden Zieldatensatz — je nach TargetKey-Typ über den Schlüssel oder über einen Filter. Wird ein Datensatz gefunden, erfolgt ein Update, andernfalls eine Neuanlage.


ResponseJob — BC-IDs zurückschreiben

Wenn BC beim Anlegen eines Datensatzes eine neue ID vergibt (z. B. die Kundennummer), kann diese über einen ResponseJob automatisch ins Quellsystem zurückgeschrieben werden.

Beispiel: CE-Kontakt nach BC anlegen und BC-ID zurück nach CE schreiben

Job A (Hauptjob):

SourceConnection: ce-produktion
TargetConnection: bc-produktion
Mapping:          ce-contact-to-bc-customer
ResponseJob:      bc-id-back-to-ce
IsActive:         true

Job B (ResponseJob):

SourceConnection: bc-produktion
TargetConnection: ce-produktion
Mapping:          bc-response-to-ce-contact
IsActive:         false

Mapping für Job B — das BC-ID-Feld:

SourceFieldTargetFieldSourceOperation
_idwysa_bcidReadFromMessage

Änderungserkennung (Watermark)

EigenschaftWert
Empfohlener WatermarkTypedatetime
Typisches WatermarkFieldlastModifiedDateTime
Filterformat{{lastModifiedDateTime gt %watermark%}}

Business Central liefert für jede Entität ein lastModifiedDateTime-Feld, das beim Speichern automatisch aktualisiert wird.

Der Filterausdruck aus dem Job wird als OData-$filter an die BC-Anfrage angehängt. Die Regeln zur Klammerung und zum Platzhalter %watermark% sind unter Jobs → Filter-Syntax beschrieben.

4.2 - D365CE — Dataverse / Customer Engagement

Konnektor für Microsoft Dataverse und Dynamics 365 Customer Engagement. Unterstützt Lesen und Schreiben.

Der D365CE-Konnektor verbindet die DataBridge mit Microsoft Dataverse und Dynamics 365 Customer Engagement über das offizielle Dataverse SDK. Er nutzt die Change Tracking API für eine lückenlose Erkennung aller Änderungen.

EigenschaftWert
Lesen✅ unterstützt
Schreiben✅ unterstützt
Job-Filter✅ clientseitig nach dem Abruf
StatusFreigegeben
Getestete MindestversionDataverse / D365CE v9.1

Die Mindestversion beschreibt den getesteten Stand; eine Versionsprüfung findet im Code nicht statt.


Voraussetzung: Change Tracking aktivieren

Für alle Dataverse-Tabellen, die als Quelle genutzt werden, muss Change Tracking aktiviert sein. Ohne Change Tracking kann der Konnektor keine Differenzabfragen durchführen.

Aktivierung: Power Platform Admin Center → Umgebung → Tabellen → Tabelle auswählen → Eigenschaften → Änderungsnachverfolgung aktivieren ✅


Verbindungsparameter

{
  "connectionstring": "<XRM-Connection-String>"
}

Beispiel (Client Secret Authentication):

AuthType=ClientSecret;Url=https://<org>.crm4.dynamics.com;ClientId=<id>;ClientSecret=<secret>

Alternative: Managed Identity (ab v2.3)

Statt eines Connection-Strings mit Secret kann die Verbindung über eine Managed Identity aufgebaut werden:

{
  "url": "https://<org>.crm4.dynamics.com",
  "clientid": "<Client-ID der user-assigned Identity, optional>"
}
  • Ohne clientid wird die system-zugewiesene Identity der Function App verwendet, mit clientid die benutzer-zugewiesene.
  • Ist connectionstring gesetzt, hat er immer Vorrang; url wird dann ignoriert.
  • Sind weder connectionstring noch url gesetzt, bricht der Verbindungsaufbau mit einem Fehler ab.
  • Voraussetzung: Die Identity ist in der Dataverse-Umgebung als Application User angelegt und hat eine passende Sicherheitsrolle (Power Platform Admin Center → Umgebung → Einstellungen → Benutzer + Berechtigungen → Anwendungsbenutzer).

Die vollständige XRM-Connection-String-Referenz findet sich in der Microsoft-Dokumentation.

Zusätzlich stehen die universellen Parameter keyVaultUrl, maxPageSize, language und httpClientTimeoutSeconds zur Verfügung — siehe Verbindungen.


Tabellen- und Feldnamen

SourceTable / TargetTable

Logischer Entitätsname in Kleinbuchstaben — so wie er in den Dataverse-Metadaten steht.

EntitätSourceTable
Accountaccount
Contactcontact
Custom Entitywysa_bcsegment

SourceField / TargetField

Logischer Attributname in Kleinbuchstaben.

AttributSourceField
Account Namename
Custom Fieldwysa_bcsystemid

Unterstützte TargetKey-Typen

TypBeschreibung
PrimaryKeyDataverse GUID (Primärschlüssel der Entität)
CustomKeyBenutzerdefinierter Schlüssel über ein oder mehrere Felder (Alternate Key)
FilterSuche über Feldwert-Kombination ohne expliziten Schlüssel

Bei CustomKey werden Schlüsselteile vom Typ int32, guid und string unterstützt; jeder andere Typ führt zum Abbruch (keytype … not yet supported for key …). Leere Schlüsselteile werden übersprungen — bleibt danach kein Teil übrig, gilt der Schlüssel als nicht auflösbar.

Ist der Alternativschlüssel in Dataverse nicht angelegt oder nicht aktiv, bricht die Suche mit einem Fehler ab (ab v2.3). Die Nachricht landet nach den Wiederholungen in der Dead-Letter-Queue und wird nicht als neuer Datensatz angelegt. Bis v2.2 wurde dieser Fall wie „Datensatz nicht gefunden“ behandelt, was zu Dubletten führen konnte.

Ein KeyValue kann statt attributeName ein StaticValue enthalten; der Schlüsselteil wird dann fest gesetzt und als Typ string behandelt.

Mehrere Ziel-Schlüssel mit Rangfolge (ab v2.1)

Statt eines einzelnen TargetKey lässt sich eine Liste TargetKeys angeben. Die Schlüssel werden nach KeyRank aufsteigend geprüft; der erste Schlüssel, der im Zielsystem einen Datensatz findet, gewinnt — alle weiteren werden übersprungen.

{
  "Operations": [
    {
      "Name": "Lookup",
      "Parameters": {
        "TargetTable": "wysa_countryregion",
        "TargetKeys": [
          {
            "KeyType": "CustomKey",
            "KeyRank": 1,
            "KeyName": "wysa_externalid",
            "KeyValues": [
              { "keyName": "wysa_externalid", "attributeName": "externalId", "rank": 1 }
            ]
          },
          {
            "KeyType": "CustomKey",
            "KeyRank": 2,
            "KeyName": "wysa_code",
            "KeyValues": [
              { "keyName": "wysa_code", "attributeName": "countryRegionCode", "rank": 1 }
            ]
          }
        ]
      }
    }
  ]
}

Bestehende Mappings mit einem einzelnen TargetKey funktionieren unverändert weiter.


SourceOperations

LookupValue

Liest einen Wert aus einem verknüpften Dataverse-Datensatz nach.

{
  "Operations": [
    {
      "Name": "LookupValue",
      "Parameters": {
        "LookupType": "Attribute",
        "SourceField": "wysa_countrycode",
        "LookupTable": "wysa_country",
        "LookupField": "wysa_name",
        "UseCache": true
      }
    }
  ]
}

LookupType kennt zwei Werte. Nur der CE-Konnektor unterstützt beide:

WertWirkung
AttributeDer genannte LookupField-Wert wird aus dem verknüpften Datensatz gelesen
NameDer Anzeigename der Referenz wird direkt aus dem Quelldatensatz übernommen — ohne zusätzliche Abfrage

ReadFromMessage

Liest Werte aus einer vorherigen Quellanfrage — nur in ResponseJobs. Verhalten wie beim D365BC-Konnektor.

ActivityParty

Liest ein Feld über eine ActivityParty-Beziehung. Wird für CE-Aktivitäten (E-Mails, Aufgaben, Anrufe) verwendet, bei denen Teilnehmer über den activityparty-Datensatz verknüpft sind.

{
  "Operations": [
    {
      "Name": "ActivityParty",
      "Parameters": {
        "PartyParams": {
          "TypeMask": 2,
          "SourceField": "activityid",
          "TargetField": "wysa_number",
          "TargetTable": "contact"
        }
      }
    }
  ]
}

TypeMask entspricht dem Dataverse-Feld participationtypemask und wird von der DataBridge unverändert an die Abfrage durchgereicht. Die gültigen Werte gibt die Dataverse-Plattform vor; die gebräuchlichsten sind:

TypeMaskTeilnehmerrolle
1From (Absender)
2To (Empfänger)
3CC
4BCC

Diese Tabelle ist eine Plattform-Referenz, keine Festlegung der DataBridge. Die verbindliche und vollständige Liste steht in der Microsoft-Dokumentation zur Entität activityparty.

LinkToRecord

Erzeugt eine anklickbare URL zu einem Datensatz in Dataverse.

{
  "Operations": [
    {
      "Name": "LinkToRecord",
      "Parameters": {
        "BaseUrl": "https://<org>.crm4.dynamics.com/main.aspx?appid=<AppId>&pagetype=entityrecord"
      }
    }
  ]
}

TargetOperations

Lookup

Befüllt ein Lookup-Feld in Dataverse, indem der verknüpfte Datensatz über eine Feldwert-Kombination gesucht wird. Die Operation gehört in die Spalte TargetOperation des Mappings und benötigt zwingend den Operations-Rahmen, den Namen Lookup sowie TargetTable.

{
  "Operations": [
    {
      "Name": "Lookup",
      "Parameters": {
        "TargetTable": "wysa_countryregion",
        "TargetKey": {
          "KeyType": "CustomKey",
          "KeyName": "wysa_code",
          "KeyValues": [
            { "keyName": "wysa_code", "attributeName": "countryRegionCode", "rank": 1 }
          ]
        }
      }
    }
  ]
}

Wird kein passender Datensatz gefunden, legt die DataBridge standardmäßig einen neuen an.

Weitere Parameter der TargetOperation

ParameterWirkung
TargetTableEntität, in der gesucht wird. Bei Lookup Pflicht.
TargetKey / TargetKeysSchlüssel bzw. Schlüsselliste zur Suche (siehe oben)
DefaultValueFester Wert, der ohne Suche gesetzt wird. Bei Lookup-Feldern eine GUID; bei Options-Feldern der numerische Optionswert.
SetNullIfEqualsValueOfFieldName des Primärschlüsselfeldes. Stimmt der aufzulösende Wert mit dem Schlüssel des Quelldatensatzes überein, wird null gesetzt statt eine Selbstreferenz zu erzeugen.
OptionDelimiterTrennzeichen, mit dem ein Quellwert für ein MultiSelect-Feld in Einzelwerte zerlegt wird
DoNotCreatetrue verhindert die automatische Neuanlage (siehe unten)

DoNotCreate (ab v2.1)

Ist DoNotCreate auf true gesetzt, wird der Zieldatensatz eines Lookups nur dann verknüpft, wenn er bereits existiert. Wird er nicht gefunden, bleibt das Feld leer — es entsteht kein neuer Datensatz.

{
  "Operations": [
    {
      "Name": "Lookup",
      "Parameters": {
        "TargetTable": "wysa_countryregion",
        "DoNotCreate": true,
        "TargetKey": {
          "KeyType": "CustomKey",
          "KeyName": "wysa_code",
          "KeyValues": [
            { "keyName": "wysa_code", "attributeName": "countryRegionCode", "rank": 1 }
          ]
        }
      }
    }
  ]
}

OptionMapping

Übersetzt Quellwerte in numerische Dataverse-Optionset-Werte.

{
  "Operations": [
    {
      "Name": "OptionMapping",
      "Parameters": {
        "Mapping": [
          { "Key": "Blank",                        "Value": "" },
          { "Key": "Prospect",                     "Value": "383380000" },
          { "Key": "First_x002D_time_x0020_Donor", "Value": "383380001" },
          { "Key": "Recurring_x0020_Donor",        "Value": "383380002" }
        ]
      }
    }
  ]
}

Ein leerer Value setzt das Zielfeld auf null. Ein Quellwert, der in der Liste nicht vorkommt, führt zum Abbruch (Could not find TableMapping for …).


Datentypen — Zieltyp und akzeptierte Quelltypen

Beim Schreiben bestimmt der Attributtyp des Zielfeldes aus den Dataverse-Metadaten, welche Umwandlung angewendet wird.

Zielattribut (Dataverse)Akzeptierte QuelltypenVerhalten und Sonderfälle
BigIntAttributeMetadataint64, int32Leerwert → null
BooleanAttributeMetadataboolean, string, byte (ab v2.3), int32 (ab v2.3)Leerwert → null; bei Ganzzahlen 0 → Nein, sonst Ja
DateTimeAttributeMetadatadate, datetime, stringLeerwert → null; auch 0001-01-01 wird zu null
DecimalAttributeMetadatadecimal, moneyInterpretation mit der Kultur aus language
DecimalAttributeMetadatastring (ab v2.1)Interpretation immer als en-US, unabhängig von language
DoubleAttributeMetadatadouble, decimalInterpretation mit der Kultur aus language. string wird nicht akzeptiert.
EntityNameAttributeMetadataalle QuelltypenWird als Text behandelt; Leerwert → null
IntegerAttributeMetadataint32, optionsetvalue, stringLeerwert → null
LookupAttributeMetadatatypunabhängigErfordert eine Lookup- oder DefaultValue-TargetOperation, sonst Lookup Operation missing
MemoAttributeMetadataalle QuelltypenWird als Text behandelt; Leerwert → null
MoneyAttributeMetadatanur decimalmoney, double und string führen zum Abbruch
MultiSelectPicklistAttributeMetadatanur stringErfordert OptionMapping; Zerlegung über OptionDelimiter. Leerwert → null
PicklistAttributeMetadatastring, int32Erfordert OptionMapping oder DefaultValue
StateAttributeMetadataboolean, string, int32, byte (ab v2.3)Erfordert OptionMapping oder DefaultValue
StatusAttributeMetadatastring, int32Erfordert OptionMapping oder DefaultValue
StringAttributeMetadataalle QuelltypenWert wird getrimmt; Leerwert → null

Sonderfälle jenseits der Tabelle:

  • UniqueIdentifierAttributeMetadata sowie Primärschlüssel- und logische Attribute (z. B. statuscodename) werden beim Schreiben stillschweigend übersprungen
  • FileAttributeMetadata und ImageAttributeMetadata werden nicht unterstützt (type not supported)
  • Jeder sonstige, nicht aufgeführte Attributtyp führt zum Abbruch (Type … not yet implemented)

Sonderentitäten

Einige Dataverse-Entitäten werden nicht über den generischen Weg verarbeitet.

listmember — Marketinglisten

Datensätze der Entität listmember werden nicht über den generischen Schreibpfad angelegt oder gelöscht, sondern über die Dataverse-Nachrichten AddMemberList bzw. RemoveMemberList. Das Mapping muss daher die Felder listid und entityid befüllen — sie liefern die Kennungen für die beiden Nachrichten.

Entitäten ohne statuscode

Für die folgenden Entitäten wird das Attribut statuscode beim Lesen automatisch aus der Spaltenliste entfernt, weil sie es nicht besitzen:

Entität
listmember
quotedetail
opportunityproduct
salesorderdetail

Ein Job-Filter mit der Operation StatusCode ist für diese Entitäten folglich nicht verwendbar.


Job-Filter

Der CE-Konnektor wertet das Feld Filter des Jobs clientseitig aus: Die Datensätze werden über die Change Tracking API abgerufen und anschließend geprüft; nicht passende Datensätze werden verworfen.

Unterstützt werden die Operationen Equals, Boolean, OptionSetValue, NotNull, IsNull und StatusCode. Ein anderer Operationsname führt zum Abbruch (Undefined FilterOperation).

Syntax, Parameter und Beispiele sind unter Jobs → Filter-Syntax beschrieben.


Änderungserkennung (Watermark)

EigenschaftWert
Empfohlener WatermarkTypestring
WatermarkFieldbeliebig (wird von der Change Tracking API intern verwaltet)

Der CE-Konnektor nutzt die Dataverse Change Tracking API. Diese liefert bei jedem Abruf einen opaken Token, der beim nächsten Aufruf übergeben wird. So werden alle Änderungen erfasst — auch wenn der Timer genau während einer Änderung ausgeführt wird.

4.3 - D365NAV — Microsoft Dynamics NAV

Konnektor für Microsoft Dynamics NAV. Unterstützt Lesen und Schreiben über OData.

Der D365NAV-Konnektor verbindet die DataBridge mit Microsoft Dynamics NAV über OData. Er ist auf die ältere NAV-API-Struktur ausgelegt und unterstützt auch OnPremise-Installationen mit NTLM-Authentifizierung.

EigenschaftWert
Lesen✅ unterstützt
Schreiben✅ unterstützt
Job-Filter✅ wird als OData-$filter an NAV übergeben
StatusFreigegeben
Getestete MindestversionNAV 2018

Verbindungsparameter

Cloud-Umgebung (OAuth2)

{
  "clientId": "<App-Registration Client ID>",
  "clientSecret": "<App-Registration Client Secret>",
  "tenantId": "<Entra Tenant ID>",
  "environment": "<NAV-Umgebungsname>",
  "companyId": "<Company ID (GUID)>",
  "url": "https://<server>/<pfad>/"
}

OnPremise-Umgebung (NTLM)

{
  "userName": "<domain\\benutzername>",
  "password": "<Passwort>",
  "url": "https://<server>:<port>/<pfad>/",
  "baseAddress": "https://<server>:<port>/",
  "authenticationMethod": "NTLM"
}

Optionale Parameter

ParameterStandardwertBeschreibung
scopehttps://api.businesscentral.dynamics.com/.defaultOAuth2-Scope beim Token-Abruf
baseAddress(leer)Basis-Adresse für den NTLM-Credential-Cache
authenticationMethodBasicSiehe Hinweis oben
maxPageSize5000Maximale Datensätze pro Abruf-Seite
languagede-DEKultur für die Interpretation von Zahlen- und Datumsformaten
httpClientTimeoutSeconds100Zeitlimit für einzelne HTTP-Aufrufe in Sekunden
keyVaultUrl(leer)Key-Vault-Secret-URL; ersetzt sämtliche übrigen Parameter
logCreateRequestsfalseProtokolliert die vollständigen Anfragen beim Anlegen
logUpdateRequestsfalseProtokolliert die vollständigen Anfragen beim Aktualisieren
logReceivedDatafalseProtokolliert die vollständigen Antwortdaten

Die drei Diagnose-Schalter erzeugen erhebliche Datenmengen und können Geschäftsdaten im Klartext protokollieren — nur gezielt für die Dauer einer Analyse aktivieren. Vollständige Beschreibung unter Verbindungen.


Tabellen- und Feldnamen

  • SourceTable / TargetTable: Name des OData-EntitySets, so wie er in $metadata steht
  • SourceField / TargetField: Exakter Feldname aus der NAV-OData-API (case-sensitive)

Unterstützte TargetKey-Typen

TypBeschreibung
PrimaryKeyNAV-nativer Primärschlüssel. Der Wert wird in Anführungszeichen gesetzt.
IdSystemeigene ID. Der Wert wird ohne Anführungszeichen eingesetzt.
CustomKeyBenutzerdefinierter Schlüssel über ein oder mehrere Felder
FilterSuche über Feldwert-Kombination

Bei CustomKey werden die Metadatentypen int32 und string unterstützt, bei Filter die Typen string und guid. Jeder andere Typ des Schlüsselfeldes führt zum Abbruch (Metadata type … not yet implemented for CreateKeyString).


SourceOperations

LookupValue

Liest einen Wert aus einer anderen NAV-Tabelle nach — Aufbau und Parameter wie beim D365BC-Konnektor.

{
  "Operations": [
    {
      "Name": "LookupValue",
      "Parameters": {
        "LookupType": "Attribute",
        "SourceField": "countryRegionCode",
        "LookupTable": "CountryRegions",
        "LookupField": "Name",
        "UseCache": true
      }
    }
  ]
}

Unterstützt wird ausschließlich LookupType: Attribute. Der Wert Name führt zu einem Abbruch (SourceOperation for Lookups).

ReadFromMessage

Liest einen Wert aus der NAV-Antwort — nur in ResponseJobs. Das SourceField muss einen _-Prefix haben. Verhalten wie bei D365BC.


Datentypen — Zieltyp und akzeptierte Quelltypen

Beim Schreiben bestimmt der Datentyp des Zielfeldes aus den NAV-Metadaten, welche Umwandlung angewendet wird.

Zieltyp (NAV-Metadaten)Akzeptierte QuelltypenVerhalten und Sonderfälle
Booleanboolean, stringLeerwert → null. Ein nicht als Wahrheitswert lesbarer Text führt zum Abbruch.
Datedate, datetime, stringAusgabe im Format yyyy-MM-dd. Leerwert → 0001-01-01.
DateTimeOffsetdate, datetime, stringAusgabe im Format yyyy-MM-ddTHH:mm:ss.fffZ. Leerwert → 0001-01-01T00:00:00.000Z.
Decimaldecimal, money, doubleInterpretation mit der Kultur aus language. Leerwert → null.
Decimalstring (ab v2.1)Interpretation immer als en-US (Punkt als Dezimaltrennzeichen), unabhängig von language.
Double (ab v2.1)decimal, money, doubleInterpretation mit der Kultur aus language. string wird nicht akzeptiert und führt zum Abbruch.
Guidguid, string, entityreferenceLeerwert → 00000000-0000-0000-0000-000000000000, nicht null.
Int32int32, optionsetvalue, stringLeerwert → null. Nicht als Ganzzahl lesbarer Wert → Abbruch.
Stringalle QuelltypenWert wird getrimmt. Leerwert → null. Liegt eine OptionMapping-TargetOperation vor, wird stattdessen der zugeordnete Wert geschrieben; ein unbekannter Quellwert führt zum Abbruch.

Jeder in der Tabelle nicht genannte NAV-Zieltyp führt beim Schreiben zu einem Abbruch (No Resolver defined for edmPrimitiveTypeKind …).


ResponseJob

Der NAV-Konnektor unterstützt das ResponseJob-Muster wie D365BC: Nach erfolgreichem Schreiben in NAV kann ein ResponseJob ausgelöst werden, der Felder aus der NAV-Antwort (z. B. neu vergebene Nummern) zurück ins Quellsystem schreibt. Aufbau und Konfiguration sind unter D365BC → ResponseJob beschrieben.


Änderungserkennung (Watermark)

EigenschaftWert
Empfohlener WatermarkTypedatetime oder date
Typisches WatermarkFieldSystemabhängig — z. B. Last_Date_Modified, Timestamp
Filterformat{{WatermarkField gt %watermark%}}

Im Gegensatz zu Business Central gibt es kein einheitliches lastModifiedDateTime-Feld. Das korrekte Watermark-Feld hängt von der NAV-Version und der verwendeten Seite bzw. Codeunit ab. Im Zweifelsfall mit dem NAV-Administrator klären.

Der Filterausdruck aus dem Job wird als OData-$filter an die NAV-Anfrage angehängt. Die Regeln zur Klammerung und zum Platzhalter %watermark% sind unter Jobs → Filter-Syntax beschrieben.


Fehlersuche

Fehler beim Schreiben werden mit dem Text Error in NAV-Communication protokolliert, zusammen mit der verwendeten relativen Adresse sowie dem neuen und dem bestehenden Datensatz. Bis einschließlich v2.0 lautete derselbe Text irreführend Error in BC-Communication — ältere Logeinträge einer NAV-Verbindung sind also trotz BC-Nennung dem NAV-Konnektor zuzuordnen.

4.4 - MSSQL — Microsoft SQL Server

Konnektor für Microsoft SQL Server. Lesen wird unterstützt — Schreiben nicht.

Der MSSQL-Konnektor ermöglicht das Lesen von Daten aus Microsoft SQL Server-Datenbanken (OnPremise und Azure SQL) und deren Übertragung in andere Systeme.

EigenschaftWert
Lesen⚠️ Preview
Schreiben— nicht unterstützt
Job-Filter✅ wird als WHERE-Klausel übernommen
StatusPreview

Wie die übrigen drei SQL-Konnektoren wertet MSSQL den Filter des Jobs aus und erzeugt gültiges Paging für sein Datenbanksystem.


Verbindungsparameter

{
  "connectionstring": "<ADO.NET Connection String>"
}

Beispiel (SQL Server Authentication):

Server=<server>;Database=<db>;User Id=<user>;Password=<password>;TrustServerCertificate=True;

Beispiel (Windows Authentication / Integrated Security):

Server=<server>;Database=<db>;Integrated Security=True;TrustServerCertificate=True;

Optionale Parameter

ParameterStandardwertBeschreibung
commandTimeOut30Zeitlimit für einzelne SQL-Befehle in Sekunden. Bei großen Tabellen oder langsamen Abfragen zu erhöhen.
maxPageSize5000Anzahl Datensätze je Seite. Steuert TOP bzw. OFFSET/FETCH NEXT der erzeugten Abfrage.
languagede-DEKultur, in der gelesene Zahlen- und Datumswerte formatiert werden
keyVaultUrl(leer)Key-Vault-Secret-URL; ersetzt sämtliche übrigen Parameter

Tabellen- und Feldnamen

EinstellungWert
SourceTableTabellenname, ggf. mit Schema-Präfix (z. B. Customers, dbo.Orders)
SourceFieldSpaltenname. Wird automatisch in eckige Klammern gesetzt (ab v2.3; bis v2.2 nur bei Namen mit Leerzeichen).

Der Wert aus SourceTable wird unverändert hinter FROM eingesetzt.


Änderungserkennung (Watermark)

Die Erkennung basiert auf einer Zeitstempel-Spalte in der SQL-Tabelle. Diese Spalte muss beim Einfügen oder Ändern eines Datensatzes automatisch aktualisiert werden.

EigenschaftWert
WatermarkTypedate oder datetime
WatermarkFieldName der Zeitstempel-Spalte (z. B. ModifiedAt, UpdatedDate); wird wie SourceField in eckige Klammern gesetzt

Die Spalte aus WatermarkField wird zusätzlich zu den gemappten Feldern abgefragt und für die Sortierung (ORDER BY … ASC) verwendet.


Filter — die WHERE-Klausel kommt aus dem Job

Ein typischer Filter für eine MSSQL-Quelle:

{{ModifiedAt > '%watermark%'}}

Die Klammerung {{ … }} sorgt dafür, dass der Block bei leerer Watermark vollständig entfällt — der Erstlauf liest dann den gesamten Bestand. Die vollständigen Regeln stehen unter Jobs → Filter-Syntax.


Datentypen — gelesene Werte

Da Schreiben nicht unterstützt wird, ist beim MSSQL-Konnektor allein der Lesepfad relevant. Der Typ ergibt sich aus dem ADO.NET-Spaltentyp; der so ermittelte Typname wird zusammen mit dem formatierten Wert an den Zielkonnektor weitergereicht und dort für die Auswahl der Umwandlung verwendet.

ADO.NET-SpaltentypWeitergegebener TypFormatierung des Wertes
Int32int32Wird als Ganzzahl gelesen und in der Kultur aus language ausgegeben
DecimaldecimalWird als Dezimalzahl gelesen und in der Kultur aus language ausgegeben
DoubledoubleWie Decimal — Umwandlung über einen Dezimalwert
DateTimedatetimeFormat u, also yyyy-MM-dd HH:mm:ssZ
alle übrigenName des .NET-Typs (z. B. string, boolean, guid)Unveränderte Textdarstellung

Welche Quelltypen ein Zielsystem akzeptiert, steht auf der jeweiligen Zielkonnektor-Seite: D365BC, D365CE, D365NAV.

4.5 - MySQL — MySQL / MariaDB

Konnektor für MySQL und MariaDB. Preview — derzeit nicht für den produktiven Einsatz geeignet.

Der MySQL-Konnektor ist für das Lesen von Daten aus MySQL- und MariaDB-Datenbanken vorgesehen.

EigenschaftWert
Lesen⚠️ Preview, nicht produktionsreif
Schreiben— nicht unterstützt
Job-Filter✅ wird als WHERE-Klausel übernommen
StatusPreview

Wie beim MSSQL-Konnektor wird der Inhalt des Feldes Filter am Job — nach Ersetzung des Platzhalters %watermark% — wörtlich hinter WHERE eingesetzt. Ohne Eintrag im Feld Filter entsteht keine WHERE-Klausel und der Job liest bei jedem Lauf die vollständige Tabelle. Der Ausdruck muss gültiges MySQL-SQL sein; es findet keine Parametrisierung statt. Details zur Filter-Syntax siehe MSSQL — Filter.


Verbindungsparameter

{
  "connectionstring": "<MySQL Connection String>"
}

Beispiel:

Server=<server>;Database=<db>;User=<user>;Password=<password>;

Optionale Parameter

ParameterStandardwertBeschreibung
commandTimeOut30Zeitlimit für einzelne SQL-Befehle in Sekunden
maxPageSize5000Anzahl Datensätze je Seite
languagede-DEKultur, in der gelesene Zahlen- und Datumswerte formatiert werden
keyVaultUrl(leer)Key-Vault-Secret-URL; ersetzt sämtliche übrigen Parameter

Tabellen- und Feldnamen

EinstellungWert
SourceTableTabellenname (z. B. customers, orders)
SourceFieldSpaltenname, wie in der Tabelle definiert

Änderungserkennung (Watermark)

EigenschaftWert
WatermarkTypedate oder datetime
WatermarkFieldName der Zeitstempel-Spalte

Die Spalte aus WatermarkField wird zusätzlich zu den gemappten Feldern abgefragt und für die Sortierung verwendet.


Datentypen — gelesene Werte

Da Schreiben nicht unterstützt wird, ist allein der Lesepfad relevant. Der Typ ergibt sich aus dem ADO.NET-Spaltentyp; der so ermittelte Typname wird zusammen mit dem formatierten Wert an den Zielkonnektor weitergereicht und dort für die Auswahl der Umwandlung verwendet.

ADO.NET-SpaltentypWeitergegebener TypFormatierung des Wertes
Int32int32Wird als Ganzzahl gelesen und in der Kultur aus language ausgegeben
DecimaldecimalWird als Dezimalzahl gelesen und in der Kultur aus language ausgegeben
DoubledoubleWie Decimal — Umwandlung über einen Dezimalwert
DateTimedatetimeFormat u, also yyyy-MM-dd HH:mm:ssZ
alle übrigenName des .NET-Typs (z. B. string, boolean, guid)Unveränderte Textdarstellung

Welche Quelltypen ein Zielsystem akzeptiert, steht auf der jeweiligen Zielkonnektor-Seite: D365BC, D365CE, D365NAV.

4.6 - Oracle — Oracle Database

Konnektor für Oracle Database. Preview — derzeit nicht für den produktiven Einsatz geeignet.

Der Oracle-Konnektor ist für das Lesen von Daten aus Oracle-Database-Instanzen vorgesehen.

EigenschaftWert
Lesen⚠️ Preview, nicht produktionsreif
Schreiben— nicht unterstützt
Job-Filter✅ wird als WHERE-Klausel übernommen
StatusPreview

Wie beim MSSQL-Konnektor wird der Inhalt des Feldes Filter am Job — nach Ersetzung des Platzhalters %watermark% — wörtlich hinter WHERE eingesetzt. Ohne Eintrag im Feld Filter entsteht keine WHERE-Klausel und der Job liest bei jedem Lauf die vollständige Tabelle. Der Ausdruck muss gültiges PL/SQL sein; es findet keine Parametrisierung statt. Details zur Filter-Syntax siehe MSSQL — Filter.


Verbindungsparameter

{
  "connectionstring": "<Oracle Connection String>"
}

Beispiel (TNS):

Data Source=<tns-alias>;User Id=<user>;Password=<password>;

Beispiel (Easy Connect):

Data Source=<server>:<port>/<service>;User Id=<user>;Password=<password>;

Optionale Parameter

ParameterStandardwertBeschreibung
commandTimeOut30Zeitlimit für einzelne SQL-Befehle in Sekunden
maxPageSize5000Anzahl Datensätze je Seite
languagede-DEKultur, in der gelesene Zahlen- und Datumswerte formatiert werden
keyVaultUrl(leer)Key-Vault-Secret-URL; ersetzt sämtliche übrigen Parameter

Tabellen- und Feldnamen

EinstellungWert
SourceTableTabellenname, ggf. mit Schema-Präfix (SCHEMA.TABLENAME)
SourceFieldSpaltenname, wie in Oracle definiert (üblicherweise Großbuchstaben)

Änderungserkennung (Watermark)

EigenschaftWert
WatermarkTypedate oder datetime
WatermarkFieldName der Zeitstempel-Spalte

Die Spalte aus WatermarkField wird zusätzlich zu den gemappten Feldern abgefragt und für die Sortierung verwendet.


Datentypen — gelesene Werte

Da Schreiben nicht unterstützt wird, ist allein der Lesepfad relevant. Der Typ ergibt sich aus dem ADO.NET-Spaltentyp; der so ermittelte Typname wird zusammen mit dem formatierten Wert an den Zielkonnektor weitergereicht und dort für die Auswahl der Umwandlung verwendet.

ADO.NET-SpaltentypWeitergegebener TypFormatierung des Wertes
Int32int32Wird als Ganzzahl gelesen und in der Kultur aus language ausgegeben
DecimaldecimalWird als Dezimalzahl gelesen und in der Kultur aus language ausgegeben
DoubledoubleWie Decimal — Umwandlung über einen Dezimalwert
DateTimedatetimeFormat u, also yyyy-MM-dd HH:mm:ssZ
alle übrigenName des .NET-Typs (z. B. string, boolean, guid)Unveränderte Textdarstellung

Welche Quelltypen ein Zielsystem akzeptiert, steht auf der jeweiligen Zielkonnektor-Seite: D365BC, D365CE, D365NAV.

4.7 - PostgreSQL

Konnektor für PostgreSQL. Preview — Lesen mit Filterunterstützung, Schreiben nicht unterstützt.

Der PostgreSQL-Konnektor ermöglicht das Lesen von Daten aus PostgreSQL-Datenbanken und deren Übertragung in andere Systeme.

EigenschaftWert
Lesen⚠️ Preview
Schreiben— nicht unterstützt
Job-Filter✅ wird als WHERE-Klausel übernommen
StatusPreview

Wie beim MSSQL-Konnektor wird der Inhalt des Feldes Filter am Job — nach Ersetzung des Platzhalters %watermark% — wörtlich hinter WHERE eingesetzt. Ohne Eintrag im Feld Filter entsteht keine WHERE-Klausel und der Job liest bei jedem Lauf die vollständige Tabelle. Der Ausdruck muss gültiges PostgreSQL-SQL sein; es findet keine Parametrisierung statt. Details zur Filter-Syntax siehe MSSQL — Filter.


Verbindungsparameter

{
  "connectionstring": "<PostgreSQL Connection String>"
}

Beispiel (Npgsql-Format):

Host=<server>;Database=<db>;Username=<user>;Password=<password>;

Optionale Parameter

ParameterStandardwertBeschreibung
commandTimeOut30Zeitlimit für einzelne SQL-Befehle in Sekunden
maxPageSize5000Anzahl Datensätze je Seite. Steuert LIMIT und OFFSET der erzeugten Abfrage.
languagede-DEKultur, in der gelesene Zahlen- und Datumswerte formatiert werden
keyVaultUrl(leer)Key-Vault-Secret-URL; ersetzt sämtliche übrigen Parameter

Tabellen- und Feldnamen

EinstellungWert
SourceTableTabellenname, ggf. mit Schema-Präfix (z. B. public.customers)
SourceFieldSpaltenname (in PostgreSQL üblicherweise Kleinbuchstaben)

Der Wert aus SourceTable wird unverändert hinter FROM eingesetzt.


Änderungserkennung (Watermark)

EigenschaftWert
WatermarkTypedate oder datetime
WatermarkFieldName der Zeitstempel-Spalte

Die Spalte aus WatermarkField wird zusätzlich zu den gemappten Feldern abgefragt und für die Sortierung (ORDER BY … ASC) verwendet.


Datentypen — gelesene Werte

Da Schreiben nicht unterstützt wird, ist allein der Lesepfad relevant. Der Typ ergibt sich aus dem ADO.NET-Spaltentyp; der so ermittelte Typname wird zusammen mit dem formatierten Wert an den Zielkonnektor weitergereicht und dort für die Auswahl der Umwandlung verwendet.

ADO.NET-SpaltentypWeitergegebener TypFormatierung des Wertes
Int32int32Wird als Ganzzahl gelesen und in der Kultur aus language ausgegeben
DecimaldecimalWird als Dezimalzahl gelesen und in der Kultur aus language ausgegeben
DoubledoubleWie Decimal — Umwandlung über einen Dezimalwert
DateTimedatetimeFormat u, also yyyy-MM-dd HH:mm:ssZ
alle übrigenName des .NET-Typs (z. B. string, boolean, guid)Unveränderte Textdarstellung

Zahlen werden in der Kultur der Quellverbindung formatiert. Steht dort der Vorgabewert de-DE, wird ein Komma als Dezimaltrennzeichen ausgegeben — für Zielumwandlungen, die einen Textwert zwingend als en-US lesen, ist language entsprechend anzupassen.

Welche Quelltypen ein Zielsystem akzeptiert, steht auf der jeweiligen Zielkonnektor-Seite: D365BC, D365CE, D365NAV.

4.8 - OpenAPI — generischer REST-Konnektor

Generischer REST-Konnektor, der Endpunkte, Wrapper-Schlüssel und Datentypen direkt aus einer OpenAPI-Spezifikation ableitet.

Der OpenAPI-Konnektor verbindet die DataBridge mit einer beliebigen REST-API, für die eine OpenAPI-Spezifikation (JSON oder YAML) existiert. Anders als bei den übrigen HTTP- Konnektoren werden Pfade, Wrapper-Schlüssel ({"clients": [...]} vs. {"client": {...}}) und Feldtypen nicht pro Ressource fest verdrahtet, sondern zur Laufzeit aus der Spezifikation aufgelöst. Ein Mapping muss dafür lediglich SourceTable/TargetTable auf den Ressourcennamen setzen (z. B. clients).

EigenschaftWert
Lesen✅ unterstützt
Schreiben (Create/Update)✅ unterstützt
Löschen✅ unterstützt
Aktionsaufrufe (InvokeAction)✅ unterstützt
Job-Filter⚠️ wird als Query-String an die Liste-Anfrage angehängt (kein $filter-Standard, API-abhängig)
StatusIn Entwicklung / Preview

Verbindungsparameter

{
  "baseUrl": "https://api.example.com",
  "openApiSpecUrl": "https://api.example.com/openapi.yaml",
  "clientId": "<Client-ID>",
  "clientSecret": "<Client-Secret>",
  "tenantId": "<Entra Tenant ID>",
  "scope": "<OAuth2-Scope>",
  "tokenUrl": "https://api.example.com/oauth/token",
  "apiKeyHeader": "X-API-Key",
  "apiKeyValue": "<API-Key>",
  "customHeaders": { "X-Custom-Header": "wert" },
  "nextLinkPath": "@odata.nextLink",
  "nextPagePath": "meta.next_page",
  "pageQueryParam": "page",
  "pageSizeQueryParam": "per_page",
  "updateMethod": "PATCH",
  "authenticateSpecRequest": false
}
ParameterPflichtStandardwertBeschreibung
baseUrl✅—Basis-URL der API (ohne abschließenden Schrägstrich; wird bei Bedarf entfernt)
openApiSpecUrl✅—URL zur OpenAPI-Spezifikation (JSON oder YAML)
authenticateSpecRequest—falsetrue = die Spezifikation wird mit denselben Zugangsdaten wie die API abgerufen; false = ohne Authentifizierung
customHeaders—(leer)Zusätzliche statische Header, die bei jeder Anfrage mitgeschickt werden (z. B. Mandanten- oder Staging-Header)
nextLinkPath—(leer)JSON-Pfad zur nächsten Seiten-URL in paginierten Antworten (z. B. @odata.nextLink). Gesetzt ⇒ URL-basierte Paginierung.
nextPagePath—(leer)JSON-Pfad zur nächsten Seitennummer (z. B. meta.next_page). Erfordert zusätzlich pageQueryParam.
pageQueryParam—(leer)Query-Parameter für die Seitennummer (z. B. page), zusammen mit nextPagePath
pageSizeQueryParam—(leer)Query-Parameter für die Seitengröße (z. B. per_page); wenn gesetzt, wird maxPageSize darüber mitgeschickt
updateMethod—PATCHHTTP-Methode für Aktualisierungen. PUT für APIs, die volle Ersetzung statt Teilaktualisierung erwarten.

Zusätzlich stehen die universellen Parameter maxPageSize, language, httpClientTimeoutSeconds, keyVaultUrl, logCreateRequests, logUpdateRequests und logReceivedData zur Verfügung — siehe Verbindungen.


Authentifizierung

Der OpenAPI-Konnektor nutzt dieselbe Auswahllogik wie die übrigen HTTP-Konnektoren, erweitert um API-Key-Authentifizierung:

MethodeAktivierungParameter
Basic AuthenticationuserName + password gesetztuserName, password
OAuth2 Client CredentialsclientId + clientSecret + tenantId gesetzt (Vorgabe, wenn nichts anderes zutrifft)clientId, clientSecret, tenantId, scope, optional tokenUrl
API KeyapiKeyHeader gesetztapiKeyHeader, apiKeyValue
{
  "apiKeyHeader": "X-Qonto-Staging-Token",
  "apiKeyValue": "<Token>"
}

Zusätzliche statische Header (z. B. mandantenspezifische Tokens, die parallel zu OAuth2 oder API-Key benötigt werden) lassen sich unabhängig von der Authentifizierungsmethode über customHeaders mitgeben.


Tabellen- und Feldnamen

  • SourceTable / TargetTable: kurzer Ressourcenname, wie er in der OpenAPI-Spezifikation als letztes Pfadsegment vorkommt (z. B. clients für /v2/clients). Der Konnektor sucht in der Spezifikation nach einem Pfad, der auf /<Ressourcenname> endet und keine Parameter enthält; ein zugehöriger Einzeldatensatz-Pfad (/v2/clients/{id}) wird automatisch mitaufgelöst.
  • Alternativ kann auch der vollständige Pfad aus der Spezifikation eingetragen werden (Rückwärtskompatibilität).
  • SourceField / TargetField: Feldname aus dem JSON-Schema der Ressource. Verschachtelte Felder werden mit Punktnotation adressiert (z. B. phone.country_code).

Liste- und Einzeldatensatz-Antworten müssen nicht manuell konfiguriert werden: Wrapper- Schlüssel wie {"clients": [...]} oder {"client": {...}} werden aus der Spezifikation erkannt. Fehlen entsprechende Hinweise in der Spezifikation, erkennt der Konnektor ersatzweise die gebräuchlichen Muster value, data und results als Array-Wrapper.


Unterstützte TargetKey-Typen

TypBeschreibung
PrimaryKey / IdSchlüsselwert wird direkt aus dem TargetKey.KeyName-Attribut der Nachricht gelesen und unverändert in die Item-URL eingesetzt
CustomKeyEin oder mehrere Feld-Wert-Paare werden als Query-String (key=wert&key2=wert2) an die Liste-Anfrage angehängt; der Datensatz wird darüber gesucht
FilterWie CustomKey, zusätzlich wird id als besonderer keyName-Wert erkannt und dafür message.Id verwendet. Für Update und Löschen wird der Schlüssel des gefundenen Datensatzes verwendet: KeyAttributeFromExisting, sonst der Primärschlüssel der Spezifikation

Wird bei CustomKey/Filter mehr als ein Datensatz gefunden, bricht die Verarbeitung mit einem Fehler ab (Found more than 1 record).


Schreiben (Upsert)

Der Konnektor prüft vor jedem Schreibvorgang, ob bereits ein Zieldatensatz existiert (GetSingleRequest bei PrimaryKey/Id/CustomKey, ein gefilterter Liste-Abruf bei Filter). Existiert er, wird nur bei tatsächlicher Werteänderung ein Update ausgelöst — identische Werte führen zu keiner Anfrage.

  • Anlegen: POST auf den Collection-Pfad

  • Aktualisieren: PATCH (Vorgabe) oder PUT (updateMethod) auf den Item-Pfad

  • Anfrage-Body wird bei Bedarf automatisch in einen aus der Spezifikation abgeleiteten Wrapper-Schlüssel eingebettet (z. B. {"client": {...}}). Ein Job kann das über SpecialSettings gezielt abschalten:

    { "DoNotWrapRequestBody": true }
    
  • Ein 409 Conflict bei Anlegen oder Aktualisieren wird als transiente Ausnahme behandelt und über die Transitive-Queue erneut verarbeitet (siehe Architektur → Retry- und Fehlerverhalten).


Löschen

Anders als die SQL-Konnektoren unterstützt der OpenAPI-Konnektor Löschungen: Der Schlüssel wird wie beim Lookup aufgelöst, anschließend folgt ein DELETE auf den Item-Pfad. Bei TargetKey vom Typ Filter wird der Datensatz zuerst gesucht. Wird keiner gefunden, schreibt der Konnektor eine Warnung ins Log und sendet kein DELETE.


InvokeAction — generische Aktionsaufrufe

Neben dem klassischen CRUD-Zyklus unterstützt die DataBridge seit dieser Version Aktions-Jobs: Ein Job mit Action = InvokeAction (technisch: jeder Wert, der nicht auf einen bekannten JobActionType passt) ruft einen benannten, nicht-CRUD-Endpunkt auf, statt einen Datensatz anzulegen oder zu ändern — z. B. POST /v2/client_invoices/{id}/send_by_einvoice.

Konfiguration:

FeldBedeutung
Job.ActionName der Aktion (z. B. der OpenAPI-operationId oder ein sprechender Bezeichner) — beliebig, solange er nicht NewUpdate, Delete, TimeStamp oder DeleteOnUpdate entspricht
TableMapping.TargetPath + TargetTableErgeben zusammen den Aktionspfad, so wie er in der Spezifikation steht (z. B. TargetPath = "v2/client_invoices/{id}", TargetTable = "send_by_einvoice")
FieldMapping mit gesetztem TargetFieldOptional; ergibt den JSON-Request-Body der Aktion

Ablauf:

  1. Der Aktionspfad wird in der Spezifikation nachgeschlagen; die HTTP-Methode wird daraus übernommen (bevorzugt POST, sonst PUT/PATCH/DELETE — die erste im Pfad gefundene, nicht-GET-Methode).
  2. Pfadparameter ({id} etc.) werden aus message.Id (Parametername id) bzw. aus message.Attributes aufgelöst; ein nicht auflösbarer Parameter erzeugt lediglich eine Warnung und bleibt im Pfad stehen.
  3. Ist mindestens ein Zielfeld gemappt, wird daraus ein JSON-Body gebaut und mitgeschickt.
  4. Bei einem ResponseJob wird die Antwort — über den aus der Spezifikation abgeleiteten Wrapper-Schlüssel entpackt — als neue Queue-Nachricht mit ActionCode = NewUpdate weitergereicht.

Ein fehlender Aktionspfad oder eine Antwort ohne 2xx-Statuscode brechen den Job mit einer Ausnahme ab.


Job-eigene Source-/Target-Pfade (statt nur Mapping-Tabelle)

(neu, alle Konnektoren betroffen — beim OpenAPI-Konnektor besonders relevant)

Ein Job kann eigene Felder Source und Target tragen, die den SourceTable/TargetTable der referenzierten Mapping-Zeilen überschreiben. Damit lässt sich ein und dasselbe Mapping für mehrere Ressourcen bzw. Aktionspfade wiederverwenden. Details: Jobs → Source und Target.


Änderungserkennung (Watermark)

EigenschaftWert
Empfohlener WatermarkTypedatetime
WatermarkFieldBeliebiger JSON-Pfad im Antwortobjekt (z. B. updated_at), der bei jedem gelesenen Datensatz zu einem gültigen Zeitstempel ausgewertet werden kann

Der Watermark-Wert wird über job.WatermarkField als SelectToken-Pfad auf jedes gelesene JSON-Objekt angewendet. Der Filterausdruck (Job.Filter) wird — anders als bei D365BC/NAV — nicht in eine standardisierte Query-Syntax übersetzt, sondern unverändert als Query-String an die Liste-Anfrage angehängt; Aufbau und Platzhalter sind daher von der jeweiligen API abhängig.


EmbedChildren — eingebettete Kind-Datensätze

(Quell-Feature ist derzeit nur im D365CE-Connector implementiert, das Zielverhalten dazu liegt im OpenAPI-Konnektor)

Verweist ein Feld-Mapping über die SourceOperation EmbedChildren auf einen anderen Job, werden dessen Zeilen bereits beim Lesen in die Nachricht des Elterndatensatzes eingebettet. Beim Schreiben in eine OpenAPI-Zielressource baut der Konnektor daraus ein verschachteltes JSON-Array (z. B. Rechnungspositionen unter lines) und schreibt es als Teil der Elternanfrage — ohne Feld-für-Feld-Abgleich mit einem eventuell vorhandenen Array im Zieldatensatz. Details zur Quellseite: Architektur und D365CE.


Caching

OpenAPI-Dokument und aufgelöste Ressourcen-Metadaten (Pfade, Wrapper-Schlüssel, Schema) werden pro Prozess zwischengespeichert. Ein Neuladen der Spezifikation erfordert einen Neustart der Function App bzw. ein erneutes Kaltstart-Init.

5 - MCP-Server

Die DataBridge unterstützt das Model Context Protocol: ein eingebauter, read-only Server für Jobs, Mappings, Service-Bus-Queues und echte Connector-Schemas für KI-Assistenten wie Claude Code, GitHub Copilot oder Microsoft 365 Copilot.

Die dsyr.DataBridge unterstützt das Model Context Protocol (MCP) — den offenen Standard, über den KI-Assistenten strukturiert auf externe Systeme zugreifen. Ein eingebauter, read-only MCP-Server macht dieselben Daten, die auch über die HTTP-APIs (GetJobByRowKey, GetMappingByRowKey) verfügbar sind, sowie die Service-Bus-Queue-Diagnose zusätzlich als MCP-Tools ansprechbar — sodass ein KI-Assistent (Claude Code, GitHub Copilot, Microsoft 365 Copilot, …) direkt gegen eine laufende DataBridge-Instanz Fragen stellen kann, statt Tabellen manuell in Azure Storage Explorer oder per REST-Aufruf durchzusehen.

Auf dieser Seite

  • Funktionsweise — Endpunkt, Transport, Authentifizierung und wie die Function intern aufgebaut ist.
  • Operationen — die zehn verfügbaren Tools im Detail: was jedes einzelne liest, und was es nicht liest.
  • Nutzung — Einrichtung mit Claude Code, GitHub Copilot (VS Code) und Microsoft 365 Copilot.

Kurzüberblick

FunctionMcp (DataBridge/Functions/McpApi.cs)
RoutePOST /api/mcp (JSON-RPC-Austausch über Streamable HTTP)
Auth-LevelFunction (Function Key als ?code=-Query-Parameter)
Tools10 — Jobs & Mappings, Service-Bus-Diagnose, Connector-Schema (siehe Operationen)
SchreibzugriffKeiner — alle Tools sind read-only, siehe Operationen

Der Server ist damit direkt an einer laufenden Function App (lokal oder in Azure) ansprechbar, ohne eigenen Prozess oder zusätzliche Infrastruktur.

5.1 - Funktionsweise

Wie der MCP-Server technisch aufgebaut ist: Endpunkt, Transport, Authentifizierung, interner Aufbau und eine bekannte Einschränkung.

Endpunkt

FunctionMcp (DataBridge/Functions/McpApi.cs)
RoutePOST /api/mcp (JSON-RPC-Austausch); GET /api/mcp liefert bewusst 405 — siehe Bekannte Einschränkung
Auth-LevelFunction (Function Key als ?code=-Query-Parameter)
ProtokollMCP über Streamable HTTP, stateless — jede POST-Anfrage ist ein eigenständiger JSON-RPC-Austausch, es gibt keine langlebige Server-Session

Der Server ist damit direkt an einer laufenden Function App (lokal oder in Azure) ansprechbar, ohne eigenen Prozess oder zusätzliche Infrastruktur. Da die Authentifizierung über den normalen Azure-Functions-Mechanismus läuft (Function Key), lässt sich der Server ohne zusätzliche Identity-Provider-Konfiguration einrichten — der Key kann als Query-Parameter oder als Header übergeben werden, je nachdem, was der jeweilige Client unterstützt (siehe Nutzung).


Interner Aufbau

Der Mcp-Function-Trigger baut pro HTTP-Anfrage einen eigenen StreamableHttpServerTransport auf und delegiert die eigentliche Tool-Logik an den IMcpToolService (DataBridge.Core/Services/Mcp/), der wiederum dieselben Service-Interfaces (IJobService, IMappingService, IServiceBusService, IConnectionService) aufruft wie die klassischen HTTP-Funktionen — es gibt keine internen Selbstaufrufe zwischen den Functions. McpApi.cs bleibt dadurch auf die reine Protokoll-Anbindung beschränkt (Tool-Registrierung, Transport), analog zum dünnen Zuschnitt von JobApi/MappingApi. Die eigentliche Tool-Logik (Init-Guard, Job-/Mapping-Lesezugriff, Service-Bus-Delegation, Connector-Auflösung) liegt in IMcpToolService, testbar unabhängig vom Protokoll-Layer. Details zur Service-Schicht: Architektur.

Die [Description]-Attribute und Standardwerte für die Tool-Parameter liegen bewusst auf den Methoden von IMcpToolService selbst (reines System.ComponentModel) — McpApi.cs bindet die Tools direkt auf diese Service-Methoden, ohne zusätzliche Wrapper-Methoden. Der Transport-Teil (ModelContextProtocol.AspNetCore) bleibt im Host; DataBridge.Core referenziert nur ModelContextProtocol.Core, und das ausschließlich für die McpException (siehe unten).

Fehlerweitergabe an den Client

Der MCP-C#-SDK ersetzt jede unbehandelte Exception eines Tools durch den generischen Text „An error occurred invoking ‘…’.“ und hängt nur bei einer McpException deren Message an. Alle anderen Exceptions landen zwar im Log, der Client sieht aber nicht, warum der Aufruf fehlgeschlagen ist. Ein CallToolFilter hilft hier nicht, weil der Catch im SDK innerhalb des Tool-Aufrufs sitzt. McpToolService wirft deshalb bei den beiden Connector-Tools (list_connector_tables, get_connector_table_schema) Connector-Fehler und unbekannte Connection-Namen als McpException weiter (SurfaceConnectorErrors), sodass z. B. die angefragte $metadata-URL und der HTTP-Status beim Client ankommen. Der Test McpApiTests.ToolsCall_ListConnectorTables_ConnectorFails_ErrorResultCarriesTheCause sichert dieses Verhalten durch die echte SDK-Pipeline ab.


Bekannte Einschränkung: GET auf /api/mcp

Manche MCP-Clients (typischerweise Remote-Clients, die den Endpunkt über eine echte Azure-Deployment-URL statt lokal ansprechen) versuchen nach dem initialize-POST zusätzlich einen GET auf denselben Endpunkt, um einen optionalen Server-zu-Client-SSE-Stream zu öffnen (Teil der Streamable-HTTP-Spezifikation).

Da dieser Server stateless ist (keine Session, kein Server-Push), unterstützt er diesen Stream bewusst nicht. Die Route ist deshalb zusätzlich zum POST-Handler mit einem eigenen GET-Handler registriert, der spezifikationskonform mit 405 Method Not Allowed (statt einem nicht aussagekräftigen, von Azure Functions automatisch erzeugten 404) antwortet:

[Function("McpGet")]
public IActionResult HandleMcpGetRequest(
    [HttpTrigger(AuthorizationLevel.Function, "get", Route = "mcp")] HttpRequest req)
{
    req.HttpContext.Response.Headers.Append("Allow", "POST");
    return new StatusCodeResult(StatusCodes.Status405MethodNotAllowed);
}

Manche Clients interpretieren 405 als „kein SSE-Support, aber Server ist erreichbar" und fallen automatisch auf reines POST zurück; ein 404 liest sich dagegen wie „Endpunkt existiert nicht" und lässt die Verbindung fehlschlagen. Ohne diesen Handler tritt das Problem nur bei Remote-/Azure-Deployments zuverlässig auf — lokal (Claude Code über .mcp.json) sendet der Client i. d. R. keinen GET-Probe-Request, daher blieb es dort unbemerkt.

5.2 - Operationen

Die zehn MCP-Tools im Detail: was jedes einzelne liest — und was bewusst nicht zurückgegeben wird.

Alle zehn Tools sind als ReadOnly = true, Idempotent = true und Destructive = false markiert (MCP-Tool-Annotationen, die dem aufrufenden Client mitgeben, dass wiederholte Aufrufe risikolos sind). Kein Tool schreibt, löscht oder sendet — weder in der jobs-/mappings-Tabelle, noch in den Service-Bus-Queues, noch in einem Quell-/Zielsystem.


Jobs & Mappings (Table Storage)

Lesen ausschließlich aus den beiden Tabellen jobs/mappings, über dieselben In-Memory-Caches wie JobApi/MappingApi (IJobService.Jobs/IMappingService.Mappings — reine Query<T>()-Reads, kein Upsert/Delete im Codepfad).

ToolLiestLiest nicht
list_jobsAlle konfigurierten Jobs: Quelle/Ziel-Connection, Richtung, Priorität, Zeitplan, Mapping-Name, aus der jobs-TabelleKeine Connection-Credentials (nur der Name der Connection, nicht ihre Zugangsdaten)
get_jobEin einzelner Job anhand seines RowKey (Job-Name) — dieselben Felder wie list_jobsdito
list_mappingsAlle Feld-Mappings zwischen Quell- und Zielsystemen aus der mappings-TabelleKeine tatsächlichen Datensatz-Inhalte — nur die Mapping-Konfiguration (welches Feld auf welches Feld abgebildet wird), nicht produzierte/synchronisierte Daten
get_mappingEin einzelnes Mapping anhand seines Namens (der Mapping-Name, auf den ein Job verweist und unter dem list_mappings es auflistet)dito

Warum keine Credentials möglich sind: IConnectionService.GetConnector(name) liefert nur die IConnector-Instanz zurück, nicht das zugrunde liegende ConnectionSetting-Objekt. Felder wie ClientSecret, Password oder ApiKeyValue sind private Felder in den konkreten Connectoren und nicht Teil des IConnector-Interfaces — es gibt schlicht keinen Rückgabewert, über den sie herauskommen könnten.


Service Bus (Diagnose)

Reine Peek-/Status-Operationen über IServiceBusService. Die beiden Peek-Tools erzeugen den Empfänger ohne PeekLock und rufen ausschließlich receiver.PeekMessagesAsync(...) auf — es gibt im Codepfad kein ReceiveMessagesAsync, CompleteMessageAsync, DeadLetterMessageAsync oder AbandonMessageAsync. Auch SendMessageAsync (existiert auf IServiceBusService für GetChanges) ist von keinem MCP-Tool aus erreichbar.

ToolLiestLiest nicht
list_queue_statusesLaufzeit-Status (aktive/dead-letter/geplante Nachrichtenanzahl) aller Export-QueuesKeine einzelnen Nachrichten — nur Zähler
get_queue_statusLaufzeit-Status einer einzelnen Queue für eine Job-Prioritätdito
peek_queue_messagesSequenznummer, JobName, SourceKey, Enqueue-Zeitpunkt, Zustellversuche und BodySize (Größe des Nachrichteninhalts in Bytes) je NachrichtDen Nachrichteninhalt selbst (Body) nicht — siehe Kasten unten
peek_dead_letter_queue_messagesZusätzlich zu den obigen Feldern: DeadLetterReason, DeadLetterErrorDescription, ErrorMessage, ErrorTimeStamp, ErrorStackTrace, OperationIddito — auch hier nur BodySize, nie Body

Connector-Metadaten (echtes Ziel-/Quellsystem-Schema)

ToolLiestLiest nicht
list_connector_tablesTabellen-/Entitätsnamen einer Connection direkt aus den Metadaten des Zielsystems — zum Abgleich mit den in Jobs/Mappings konfigurierten NamenKeine tatsächlichen Datensätze aus der Tabelle — nur ihren Namen
get_connector_table_schemaSpalten-Metadaten (Typ, Nullable, maximale Länge, bei SQL-Dezimalspalten Precision/Scale, Primary Key) einer Tabelle/Entität — zum Validieren eines Mappings gegen das tatsächliche Schema, statt sich auf die Mapping-Konfiguration allein zu verlassenKeine tatsächlichen Datensätze/Zeilenwerte — nur die Spalten-Struktur

Beide Tools nehmen einen connectionName entgegen (den RowKey aus der connections-Tabelle — siehe Verbindungen, z. B. BC_Test oder CRM_Test) sowie einen tableName/Ressourcennamen. Für BC/NAV gibt es zusätzlich den apiPath (derselbe Wert wie TableMapping.SourcePath/TargetPath im Mapping), da beide Connectoren ihr Schema über einen versionierten OData-$metadata-Endpunkt beziehen. Bei BC ist er optional: leer bedeutet v2.0 (die Standard-API); Custom-APIs (z. B. publisher/gruppe/v1.0) brauchen ihren Pfad weiterhin. Bei den SQL-Connectoren hat apiPath eine andere Bedeutung, siehe unten.

Diese beiden Tools sind die einzigen mit OpenWorld = true — sie greifen tatsächlich auf das externe Quell-/Zielsystem der jeweiligen Connection zu, nicht nur auf die eigene DataBridge-Infrastruktur. Ungültige Connection-Namen liefern eine klare Meldung („Connection ‘…’ is not configured or not initialized") statt einer rohen KeyNotFoundException — es lässt sich also nicht durch Ausprobieren beliebiger Namen etwas über das System herausfinden, das über list_jobs/get_job nicht sowieso schon sichtbar wäre. Schlägt der Zugriff auf das Zielsystem fehl (falscher apiPath, unbekannte Tabelle, Host nicht erreichbar), enthält das Fehlerergebnis die Ursache, z. B. GET …/api/nope/v1.0/$metadata returned 404 Not Found — technisch als McpException, weil der MCP-SDK nur deren Message an den Client durchreicht (siehe Funktionsweise).

SQL-Connectoren: Schema über apiPath

Bei den SQL-Connectoren gibt apiPath das Schema an, in dem die Tabelle liegt — bei MySQL die Datenbank. Ohne apiPath wird das Standard-Schema der Verbindung verwendet (MSSQL SCHEMA_NAME(), MySQL DATABASE(), PostgreSQL current_schema(), Oracle der angemeldete Benutzer). Damit werden gleichnamige Tabellen in unterschiedlichen Schemas (z. B. dbo.Customers und archive.Customers) nicht vermischt.

ConnectorMetadatenquelleapiPath
MSSQLINFORMATION_SCHEMASchema, z. B. dbo
PostgreSQLINFORMATION_SCHEMASchema, z. B. public
MySQLINFORMATION_SCHEMADatenbank
OracleUSER_TAB_COLUMNS/USER_CONSTRAINTS, mit apiPath ALL_*-ViewsOwner/Schema, z. B. HR

Tabellen- und Schemaname werden groß-/kleinschreibungsunabhängig verglichen und als Parameter an die Datenbank übergeben. Die Metadaten werden je Connector-Instanz zwischengespeichert — Schemaänderungen an der Datenbank werden erst nach einem Neustart der Function App sichtbar.

SQL-Connectoren: Datentypen

Die Datentypen aus dem Datenbank-Katalog werden auf .NET-Typen abgebildet. Dabei werden Precision und Scale sowie bei MySQL der vollständige Spaltentyp (COLUMN_TYPE) berücksichtigt. Unbekannte Typen (z. B. json, xml, Array-Typen) erscheinen als String.

.NET-TypMSSQLPostgreSQLMySQLOracle
Guiduniqueidentifieruuid——
Booleanbitbooleantinyint(1)/boolean, bit(1)boolean (23ai)
Bytetinyint—tinyint unsigned—
Int16smallintsmallinttinyint, smallint, yearnumber(1–4,0)
Int32intintegerint, mediumint, smallint unsignednumber(5–9,0)
Int64bigintbigintbigint, int unsigned, bit(n>1)number(10–18,0)
Decimaldecimal, numeric, moneynumericdecimal, bigint unsignednumber mit Nachkommastellen, ab 19 Stellen oder ohne Angabe
Singlereal, float(1–24)realfloatbinary_float
Doublefloatdouble precisiondoublebinary_double, float
DateOnlydatedatedate—
DateTimedatetime, datetime2, smalldatetimetimestampdatetime, timestampdate, timestamp, timestamp with local time zone
DateTimeOffsetdatetimeoffsettimestamp with time zone—timestamp with time zone
TimeSpantimetime, intervaltimeinterval
Byte[]binary, varbinary, image, timestamp/rowversionbyteabinary, varbinary, blob-Typenblob, raw
StringZeichentypen, xmlZeichentypen, json/jsonbZeichentypen, jsonZeichentypen, clob, json

Besonderheiten:

  • MSSQL timestamp ist der alte Name von rowversion, eine binäre Zeilenversion und kein Datum — daher Byte[].
  • MySQL speichert BOOLEAN als tinyint(1); erst der vollständige Spaltentyp macht daraus Boolean. Vorzeichenlose Ganzzahlen erhalten den nächstgrößeren Typ, damit der Wertebereich passt. timestamp hat keinen Offset und wird daher DateTime.
  • Oracle kennt keine eigenen Ganzzahltypen; NUMBER(p,0) wird nach der Anzahl der Stellen auf Int16/Int32/Int64 abgebildet. DATE enthält immer eine Uhrzeit und wird daher DateTime, nicht DateOnly.
  • GUIDs, die als Text gespeichert sind (MySQL char(36), Oracle varchar2(36)), erscheinen als String.

Zusammenfassung: was über MCP grundsätzlich nicht möglich ist

  • Keine Schreib-, Lösch- oder Sende-Operation auf jobs/mappings/Service-Bus-Queues oder einem Ziel-/Quellsystem — es gibt für keines der zehn Tools einen Codepfad zu Upsert, Delete, SendMessageAsync, CompleteMessageAsync o. Ä.
  • Keine Connection-Credentials (Passwörter, Client-Secrets, API-Keys) — die sind strukturell nie Teil eines Rückgabewerts.
  • Kein tatsächlicher Nachrichteninhalt aus einer Service-Bus-Queue — nur dessen Byte-Größe.
  • Keine tatsächlichen Datensatz-Werte aus einem Ziel-/Quellsystem über die Connector-Tools — nur Tabellennamen und Spalten-Struktur, keine Zeilen.

5.3 - Nutzung

Den MCP-Server mit Claude Code, GitHub Copilot (VS Code) oder Microsoft 365 Copilot verbinden.

Der Server spricht Standard-MCP über Streamable HTTP — grundsätzlich verbindet sich jeder MCP-fähige Client über dieselbe URL (.../api/mcp?code=<Function-Key>). Die folgenden Abschnitte zeigen die konkrete Einrichtung für die gängigsten Clients.


Claude Code

Dieses Repository bringt bereits eine passende .mcp.json mit:

{
  "mcpServers": {
    "databridge": {
      "type": "http",
      "url": "${DATABRIDGE_MCP_URL:-http://localhost:7062}/api/mcp?code=${DATABRIDGE_FUNCTION_KEY:-local}"
    }
  }
}

Lokal (Core Tools / Debugger): Der lokale Function-Host prüft den Auth-Level Function nicht — der Platzhalterwert local in der URL reicht aus. Voraussetzung ist nur, dass die Function App tatsächlich auf Port 7062 läuft (nicht der func-Standard 7071!):

  • Visual Studio / dotnet run: Startprofil DataBridge aus src/DataBridge/Properties/launchSettings.json (setzt bereits --port 7062).
  • VS Code: Task func: host start aus .vscode/tasks.json (ebenfalls fest auf --port 7062).
  • Rider: die in der IDE angezeigte „Azure Functions Project"-Run-Configuration DataBridge — im Feld „Function host arguments" muss host start --pause-on-error --port 7062 stehen.

Danach lädt Claude Code den databridge-Server automatisch; die Tools erscheinen mit dem Präfix mcp__databridge__....

Gegen eine Azure-Deployment-Instanz: Die URL enthält zwei Umgebungsvariablen, die ohne eigene Werte auf den lokalen Host zeigen. Für ein Deployment setzt man sie in der nicht eingecheckten .claude/settings.local.json des Repositorys:

{
  "env": {
    "DATABRIDGE_MCP_URL": "https://<function-app-name>.azurewebsites.net",
    "DATABRIDGE_FUNCTION_KEY": "<Function Key>"
  }
}

Den Function Key gibt es im Azure Portal → Function App → Funktionen → Mcp → Funktionsschlüssel. Niemals einen echten Key in .mcp.json committen. Geänderte Werte greifen erst beim Start einer neuen Claude-Code-Session.

Eine eigene Datei wie .mcp.local.json wertet Claude Code nicht aus. Wer zusätzlich einen persönlichen Server-Eintrag braucht (etwa für eine zweite Instanz), legt ihn über claude mcp add --scope local --transport http <name> "<url>" an.

Beispiel-Interaktion

Ein typischer Anwendungsfall: prüfen, ob ein Mapping-Feld wirklich im Zielsystem existiert.

Prompt: „Prüfe, ob das Mapping BCAccounts gültige Zielfelder hat — vergleiche mit den echten Metadaten der Connection."

Claude ruft dafür typischerweise nacheinander auf:

  1. get_mapping mit rowKey: "BCAccounts" → liefert die konfigurierten Feld-Mappings inkl. TargetTable/TargetPath.
  2. get_connector_table_schema mit der targetConnection aus dem zugehörigen Job sowie tableName/apiPath aus dem Mapping → liefert das tatsächliche Schema.
  3. Vergleicht beide Ergebnisse und meldet fehlende Felder oder Typ-Abweichungen.

Damit lassen sich Mapping-Fehler bereits vor dem ersten produktiven Lauf eines Jobs erkennen, statt sie erst über eine fehlgeschlagene Dead-Letter-Nachricht zu bemerken (siehe peek_dead_letter_queue_messages, Operationen).


GitHub Copilot (VS Code)

VS Code (ab Version 1.99, Agent-/Chat-Modus) unterstützt MCP-Server nativ über eine mcp.json — entweder projektweit unter .vscode/mcp.json oder als Nutzer-Konfiguration über die Command Palette (MCP: Open User Configuration). Anders als Claude Codes .mcp.json liegt der Server hier unter dem Schlüssel servers, nicht mcpServers:

{
  "servers": {
    "databridge": {
      "type": "http",
      "url": "https://<function-app-name>.azurewebsites.net/api/mcp?code=${input:databridgeFunctionKey}"
    }
  },
  "inputs": [
    {
      "id": "databridgeFunctionKey",
      "type": "promptString",
      "description": "DataBridge MCP Function Key",
      "password": true
    }
  ]
}

Der inputs-Block sorgt dafür, dass VS Code beim ersten Verbindungsversuch einmalig nach dem Function Key fragt (maskierte Eingabe) statt ihn im Klartext in der Datei zu speichern — dieselbe Absicht wie die ${DATABRIDGE_FUNCTION_KEY:-local}-Umgebungsvariable im Claude-Code-Beispiel oben, nur über den VS-Code-eigenen Mechanismus. Lokal (Function-Host ohne Auth-Prüfung) reicht auch hier ein beliebiger Platzhalterwert. Details zu Input-Variablen: VS Code MCP-Konfigurationsreferenz.

Nach dem Speichern zeigt VS Code den Server im Chat-/Agent-Modus an; die Tools lassen sich dort wie jedes andere MCP-Tool auswählen und aufrufen.


Microsoft 365 Copilot

Microsoft 365 Copilot bindet externe MCP-Server nicht direkt per URL im Chat ein, sondern über Copilot Studio — dort lässt sich ein bestehender MCP-Server einem Agenten als Tool hinzufügen, und dieser Agent dann in Microsoft 365 Copilot veröffentlichen. Die Einrichtung erfolgt über den MCP-Onboarding-Wizard in Copilot Studio:

  1. Im Agenten zur Seite Tools wechseln → Add a tool → New tool → Model Context Protocol.
  2. Server-URL eintragen: https://<function-app-name>.azurewebsites.net/api/mcp.
  3. Als Authentifizierungsart API key wählen (nicht OAuth 2.0 — unser Server nutzt den Azure-Functions-eigenen Function-Key-Mechanismus, kein OAuth).
  4. Als Type Query wählen und als Parametername code eintragen — das entspricht genau dem ?code=-Query-Parameter, den auch Claude Code und VS Code verwenden.
  5. Create, dann den Function Key als API-Key-Wert für die Verbindung hinterlegen.

Copilot Studio unterstützt für MCP ausschließlich den Streamable-HTTP-Transport (SSE ist seit August 2025 deprecatet) — genau das Protokoll, das McpApi.cs implementiert, es sind also keine serverseitigen Anpassungen nötig.

Alternativ lässt sich der Server auch als benutzerdefinierter Connector über eine OpenAPI-Beschreibung in Power Apps einbinden (x-ms-agentic-protocol: mcp-streamable-1.0) — das ist der Weg, wenn mehr Kontrolle über die Connector-Konfiguration nötig ist als der Wizard bietet. Details dazu: Microsoft-Dokumentation zu MCP in Copilot Studio.

6 - Betrieb & Wartung

Logging, Monitoring, Alerts und Updates für den laufenden Betrieb der DataBridge.

Eine laufende DataBridge-Instanz braucht Beobachtung. Dieses Kapitel erklärt, wie man den Betrieb nachvollzieht, Fehler diagnostiziert, Warnungen automatisiert und die Instanz auf neue Versionen aktualisiert.

SeiteInhalt
LoggingWie Logs entstehen, Sampling von Traces und Abhängigkeiten, Custom Metrics, KQL-Abfragen
Monitoring & AlertsAzure Alert Rules, Action Groups, empfohlene Schwellwerte
Grafana-DashboardAutomatisch ausgeliefertes Betriebsdashboard in Application Insights
Updates & UpgradesUpgrade-Pfade zwischen Versionen, Breaking Changes

6.1 - Logging & Telemetrie

Wie die DataBridge Logs erzeugt, welche Telemetrie sie sendet und wie Sampling die Kosten steuert.

Die DataBridge steuert ihr Telemetrie-Volumen über zwei voneinander unabhängige Mechanismen:

  • Gepuffertes Logging — Log-Einträge werden pro Funktionsausführung gesammelt und erst am Ende des Laufs, abhängig vom Ergebnis, selektiv gesendet.
  • Sampling von Abhängigkeiten — ausgehende Aufrufe an Quell- und Zielsysteme werden nur anteilig gesendet, sofern sie erfolgreich und schnell waren.

Der zweite Mechanismus ist der eigentliche Kostenhebel: In einer Volumenanalyse über 90 Tage entfielen rund 78 % des gesamten Datenvolumens auf Abhängigkeiten und nur ein Bruchteil auf Log-Einträge.

SeiteInhalt
MetrikenCustom Metrics — die einzige nicht gesampelte Mengenangabe
Performance & LaufzeitenKQL-Abfragen für Laufzeiten, Fehler, Abhängigkeiten und Datenvolumen

Gepuffertes Logging — drei Lauf-Ergebnisse

ErgebnisKriteriumGesendete Log-Level
Normaler LaufKeine Fehler, keine WarnungenNur Warning + Error + ~20 % der Information-Einträge (konfigurierbar)
Lauf mit WarnungenMindestens eine Warning, kein FehlerAlle Information + Warning + Error
FehlerlaufMindestens ein unbehandelter FehlerAlle Debug + Information + Warning + Error

Der Anteil der Information-Einträge im Normalbetrieb wird über TELEMETRY_INFO_SAMPLE_RATE gesteuert (Standard: 20 = 20 %). Die Stichprobe wird pro Eintrag gezogen — es entstehen also vereinzelte Traces aus allen Läufen, keine vollständigen Ketten. Lückenlose Ketten gibt es nur bei Warning- und Fehler-Läufen.

Trace-Level erreicht Application Insights nie


Sampling von Abhängigkeiten

Ausgehende Aufrufe an Quell- und Zielsysteme (D365 OData, SQL, Table Storage, Service Bus) werden von Application Insights automatisch als Dependencies erfasst. Ohne Filterung verursachen sie bei kurzem Polling-Intervall ein Vielfaches aller Log-Einträge zusammen.

AufrufWird gesendet
FehlgeschlagenImmer vollständig
Langsam (≥ TELEMETRY_DEPENDENCY_SLOW_MS)Immer vollständig
Erfolgreich und schnellNur zum Anteil TELEMETRY_DEPENDENCY_SAMPLE_RATE (Standard 10 %)

Steuerung über TELEMETRY_CONTROL

Die Einstellung wirkt auf beide Mechanismen — Traces und Abhängigkeiten:

WertWirkung
TRACE_SAMPLE (Standard)Selektives Sampling wie oben beschrieben
TRACE_ALLSendet alles, deaktiviert jedes Sampling (für aktive Fehlersuche)
TRACE_NONEUnterdrückt Traces und Abhängigkeiten vollständig

Eine leere Variable entspricht TRACE_SAMPLE. Details zu allen Telemetrie-Einstellungen stehen unter Umgebungsvariablen.


Auswirkung auf bestehende Auswertungen

Nicht betroffen sind:

  • Fehleranzahlen und Fehlerursachen — fehlgeschlagene Aufrufe werden nie verworfen
  • Auswertungen langsamer Aufrufe — ab dem Schwellwert immer vollständig
  • Requests, Exceptions, Traces und Custom Metrics — unverändert

Wichtige Log-Felder in Application Insights

FeldBeschreibung
operation_IdKorrelations-ID — verknüpft alle Einträge einer einzigen Funktionsausführung
operation_ParentIdSpan-ID des Requests — sorgt für die korrekte Einordnung in der End-to-End-Transaktionsansicht
operation_NameName der ausgeführten Function (z. B. GetChanges, ProcessStandardQueueItems)
timestampZeitpunkt des ursprünglichen Log-Aufrufs, nicht des Sendens am Ende der Ausführung
messageLog-Text
severityLevel0 = Verbose, 1 = Information, 2 = Warning, 3 = Error, 4 = Critical
customDimensions.Category.NET-Logger-Kategorie
customDimensions.ExceptionVollständiger Stack Trace (nur bei Exceptions)

Automatisch erfasste Telemetrie

Neben den eigenen Log-Einträgen erfasst Application Insights selbstständig:

  • Requests — je Funktionsausführung, mit Laufzeit und Erfolgskennzeichen
  • Exceptions — jede aufgetretene Ausnahme mit Stack Trace
  • Dependencies — jeder ausgehende Aufruf (unterliegt dem Sampling, siehe oben)

Auswertung im Azure Portal

Schnelle Übersicht — Performance Blade

Application Insights → Untersuchen → Leistung

Zeigt alle Funktionsausführungen mit Durchschnittslaufzeit und P50/P95/P99-Perzentilen. Ein hoher P95-Wert deutet auf gelegentliche Verzögerungen hin (langsames Zielsystem, Queue-Stau).

Fehlerdiagnose — Failures Blade

Application Insights → Untersuchen → Fehler

Fehlgeschlagene Aufrufe, gruppiert nach Fehlertyp, mit direktem Zugriff auf Stack Traces.

Application Insights → Untersuchen → Transaktionssuche

Filter nach operation_Id, um alle Einträge einer einzigen Ausführung zu sehen.


Tipps für die Fehlersuche

  1. operation_Id finden: Im Failures Blade auf einen fehlgeschlagenen Request klicken → End-to-End Transaction Details → operation_Id ist in der URL bzw. in den Details sichtbar.

  2. Komplette Traces aktivieren: TELEMETRY_CONTROL = TRACE_ALL setzen, den problematischen Job über ExecuteJob manuell auslösen, anschließend zurücksetzen.

  3. Zwischen Funktionen korrelieren: Schlägt eine der Process…QueueItems-Funktionen fehl, ist die operation_Id der ursprünglichen GetChanges-Ausführung in der Nachricht gespeichert — so lässt sich der vollständige Pfad eines Datensatzes nachvollziehen. Beachten Sie dabei die Einschränkung beim Dependency-Sampling (siehe oben).

6.1.1 - Metriken

Custom Metrics der DataBridge — vorab aggregiert und ohne Sampling.

Neben Traces und Requests sendet die dsyr.DataBridge eigene Custom Metrics an Application Insights. Metriken sind numerische Zeitreihen mit Dimensionen — sie eignen sich für Dashboards, Trend-Analysen und Metric Alerts, ohne dass dafür Log-Einträge durchsucht werden müssen.

Die Metriken werden über TelemetryClient.GetMetric() vorab aggregiert: Das SDK sammelt die Werte lokal in 1-Minuten-Intervallen und sendet pro Intervall und Dimensionskombination einen aggregierten Datenpunkt (Summe, Anzahl, Min, Max).

Verfügbare Metriken

Alle Metriken verwenden den Namespace DataBridge.

MetrikDimensionenQuelleBedeutung
ChangesDetectedJobNameGetChanges → ProcessJobsForRetrievalInOrderServiceAnzahl der erkannten geänderten Datensätze pro Job und Lauf (kumuliert pro Lauf, inkl. 0)
MessagesProcessedJobName, Outcome (Success/Failed/Transitive)ProcessStandardQueueItems/ProcessLowPriorityQueueItems/ProcessHighPriorityQueueItems → ProcessQueueMessageServiceEine verarbeitete Queue-Nachricht mit ihrem Ausgang. Queue-übergreifend: alle drei Prioritäts-Queues schreiben in dieselbe Metrik, unterscheidbar nur über den Ausgang, nicht über die Queue. Transitive heißt: die Nachricht wurde nicht endgültig verworfen, sondern in eine Retry-Queue umgeleitet.
ProcessingDurationMsJobNameProcessStandardQueueItems/ProcessLowPriorityQueueItems/ProcessHighPriorityQueueItems → ProcessQueueMessageServiceVerarbeitungsdauer einer einzelnen Queue-Nachricht in Millisekunden, unabhängig vom Ausgang.

Anzeige in Azure

Metrics Explorer

Application Insights → Metriken → Namespace DataBridge auswählen → Metrik ChangesDetected. Als Aggregation eignet sich Summe (Anzahl Datensätze) oder Anzahl (Anzahl Läufe).

KQL (Logs)

Jeder aggregierte Datenpunkt landet zusätzlich in der Tabelle customMetrics:

customMetrics
| where name == "ChangesDetected"
| extend job = tostring(customDimensions["JobName"])
| summarize sum(valueSum) by bin(timestamp, 2m), job
| render timechart

Host-generierte Metriken (Functions-Runtime)

Zusätzlich zu den oben aufgeführten, selbst erzeugten Metriken schreibt die Azure-Functions-Runtime automatisch pro Funktion aggregierte Kennzahlen ebenfalls in die Tabelle customMetrics — ohne dass die DataBridge dafür Code enthält:

<FunctionName> Successes
<FunctionName> Failures
<FunctionName> SuccessRate
<FunctionName> AvgDurationMs
<FunctionName> MinDurationMs
<FunctionName> MaxDurationMs

<FunctionName> ist z. B. GetChanges, ProcessStandardQueueItems, ProcessLowPriorityQueueItems oder ProcessHighPriorityQueueItems. Diese Metriken sind Standardverhalten des Functions-Hosts (WebJobs SDK Function Result Aggregator) und daher in jeder Azure-Functions-App vorhanden, unabhängig von der DataBridge selbst. Das Grafana-Dashboard nutzt sie für die Kopfzeilen-Tachos und die Ausführungen/Laufzeiten-Panels der Zeile „GetChanges".

Limits

LimitWertAnmerkung
Dimensionen pro Metrik10Azure-Monitor-Limit
Zeitreihen pro Metrik (SDK-Default)1.000seriesCountLimit der SDK-Pre-Aggregation
Distinct-Werte pro Dimension (SDK-Default)100valuesPerDimensionLimit

Konsequenz: Dimensionswerte müssen niedrig-kardinal sein — Job-Namen, Queue-Namen und Status-Codes sind geeignet; IDs, Zeitstempel oder Datensatz-Schlüssel nicht.

6.1.2 - Performance & Laufzeiten

KQL-Abfragen für Laufzeiten, Fehlerquoten, Abhängigkeiten und Datenvolumen.

Die folgenden Abfragen können direkt im Logs-Bereich von Application Insights (Kusto Query Language) ausgeführt werden.


Laufzeiten und Fehler

Laufzeiten aller Funktionen (letzte 24 Stunden)

requests
| where timestamp > ago(24h)
| summarize
    Aufrufe        = count(),
    DurchschnittMs = avg(duration),
    P95Ms          = percentile(duration, 95),
    Fehler         = countif(success == false)
  by name
| order by DurchschnittMs desc

Verlauf einer einzelnen Funktion (letzte 7 Tage)

requests
| where timestamp > ago(7d)
| where name == "GetChanges"
| summarize
    P50Ms  = percentile(duration, 50),
    P95Ms  = percentile(duration, 95),
    Fehler = countif(success == false),
    Läufe  = count()
  by bin(timestamp, 1h)
| order by timestamp asc

Durchschnittliche Laufzeit der letzten 30 Tage (täglich)

requests
| where timestamp > ago(30d)
| summarize
    DurchschnittMs = avg(duration),
    P95Ms          = percentile(duration, 95)
  by name, bin(timestamp, 1d)
| order by name asc, timestamp asc

Fehlerquote je Funktion (letzte 7 Tage)

requests
| where timestamp > ago(7d)
| summarize
    Gesamt    = count(),
    Fehler    = countif(success == false),
    FehlerPct = round(100.0 * countif(success == false) / count(), 1)
  by name
| order by FehlerPct desc

Warnungen und Fehler im Zeitverlauf

traces
| where timestamp > ago(7d)
| where severityLevel >= 2  // 2 = Warning, 3 = Error
| summarize Anzahl = count() by severityLevel, bin(timestamp, 1h), operation_Name
| order by timestamp asc

Top-10 häufigste Fehlermeldungen

exceptions
| where timestamp > ago(7d)
| summarize Anzahl = count() by outerMessage
| top 10 by Anzahl desc

Einzelne Ausführung nachvollziehen

Alle Einträge einer Ausführung (per operation_Id)

union requests, traces, exceptions
| where operation_Id == "<operation_Id hier einfügen>"
| project timestamp, itemType, severityLevel, message, outerMessage, operation_Name
| order by timestamp asc

Fehlerläufe mit allen zugehörigen Traces (letzte 24 Stunden)

let fehlgeschlagene =
    requests
    | where timestamp > ago(24h)
    | where success == false
    | project operation_Id, FunktionsName = name, Startzeit = timestamp;
traces
| where timestamp > ago(24h)
| join kind=inner fehlgeschlagene on operation_Id
| project Startzeit, FunktionsName, severityLevel, message
| order by Startzeit desc, severityLevel desc

Abhängigkeiten

Langsamste Abhängigkeiten (letzte 24 Stunden)

Zeigt die ausgehenden Aufrufe an Quell- und Zielsysteme mit den höchsten Laufzeiten:

dependencies
| where timestamp > ago(24h)
| summarize
    Aufrufe        = count(),
    DurchschnittMs = avg(duration),
    P95Ms          = percentile(duration, 95),
    Fehler         = countif(success == false)
  by type, target, name
| order by P95Ms desc
| take 20

Fehlgeschlagene Abhängigkeiten nach Zielsystem (letzte 7 Tage)

dependencies
| where timestamp > ago(7d)
| where success == false
| summarize Fehler = count() by type, target, resultCode
| order by Fehler desc

Datenvolumen und Kosten

Datenvolumen pro Tabelle

Wenn das tägliche Datenvolumen die konfigurierte Obergrenze (Daily Cap) erreicht, zeigt diese Abfrage, welche Tabelle den Verbrauch verursacht:

union withsource = Tabelle *
| where TimeGenerated > ago(7d)
| summarize VolumenMB = round(sum(_BilledSize) / 1024.0 / 1024.0, 1) by Tabelle, bin(TimeGenerated, 1d)
| order by TimeGenerated desc, VolumenMB desc

Hinweise zur Interpretation

Warum fehlen manche Traces bei erfolgreichen Läufen?

Das Buffering sendet bei erfolgreichen Läufen nur einen Teil der Information-Einträge und keine Debug-Einträge. Die Stichprobe wird pro Eintrag gezogen: Jede einzelne Information-Zeile hat unabhängig eine Chance von 20 % (konfigurierbar via TELEMETRY_INFO_SAMPLE_RATE), gesendet zu werden. Das führt zu einem gleichmäßigen Strom vereinzelter Traces — vollständige, lückenlose Ketten gibt es nur bei Warning- und Fehler-Läufen.

Trace-Einträge erreichen Application Insights grundsätzlich nie — sie sind nur live in der Konsole bzw. im Log Stream sichtbar.

Warum fehlen manche Abhängigkeiten?

Fehlgeschlagene und langsame Aufrufe kommen immer vollständig an, erfolgreiche und schnelle nur zu einem Anteil von 10 % (konfigurierbar via TELEMETRY_DEPENDENCY_SAMPLE_RATE).

Anders als bei den Log-Einträgen wird diese Entscheidung pro Aufruf getroffen — in dem Moment, in dem der Aufruf endet, also bevor das Ergebnis der Ausführung bekannt ist. In der End-to-End-Transaktionsansicht sind daher auch bei einem fehlgeschlagenen Lauf nicht zwangsläufig alle ausgehenden Aufrufe zu sehen. Für eine lückenlose Kette ist temporär TELEMETRY_CONTROL=TRACE_ALL zu setzen.

Requests vs. Traces vs. Dependencies

Telemetrie-TypWird immer erfasst?Enthält Laufzeit?
RequestJa — jede InvocationJa
ExceptionJa — jede ExceptionNein
TraceNein — abhängig vom OutcomeNein
DependencyNein — garantiert nur, wenn der Aufruf selbst fehlschlägt oder den Langsam-Schwellwert überschreitetJa
Custom MetricJa — vorab aggregiert, kein SamplingNein

Für Laufzeit-Analysen der Funktionen immer die requests-Tabelle verwenden, für Laufzeiten einzelner Fremdsystem-Aufrufe die dependencies-Tabelle, für inhaltliche Diagnose die traces-Tabelle. Für belastbare Mengenangaben ausschließlich die Custom Metrics, da diese nicht gesampelt werden.

Korrelation über operation_Id

Jede Funktionsausführung erhält eine eindeutige operation_Id. Alle Traces, Requests und Exceptions dieser Invocation tragen dieselbe ID. Das erlaubt es, in KQL alle zusammengehörenden Einträge mit einem einzigen Filter zu finden.

6.2 - Monitoring & Alerts

Azure Alert Rules und Action Groups für die automatische Überwachung der DataBridge.

Ein funktionierendes Monitoring erkennt Probleme, bevor sie auffallen. Die DataBridge kann über Azure Monitor Alert Rules so konfiguriert werden, dass bei Fehlern, Queue-Staus oder Dead-Letter-Einträgen automatisch Benachrichtigungen ausgelöst werden.


Aufbau des Alert-Systems

Das Azure-Alert-System besteht aus zwei Komponenten:

Alert Rule                    Action Group
──────────────────            ──────────────────────────
Bedingung definieren    →     Wer wird benachrichtigt?
Scope: Ressource              E-Mail, SMS, Webhook, Azure Role
Condition: KQL / Metric
Schwellwert festlegen

Empfohlene Reihenfolge:

  1. Zuerst die Action Group anlegen (Empfänger)
  2. Dann die Alert Rules darauf zeigen lassen

Action Group anlegen

Azure Portal → Monitor → Warnungen → Action Groups → Erstellen

EinstellungEmpfehlung
Namez. B. databridge-ops-team
Anzeigenamekurz (max. 12 Zeichen), erscheint in Alert-E-Mails
AktionstypE-Mail/SMS/Push/Voice oder Azure-Rolle (z. B. alle „Contributor")

Mitgeliefertes Dashboard

Unabhängig von Alerts liefert jede Installation ein vorkonfiguriertes Grafana-Dashboard mit — es zeigt Erfolgsraten je Funktion, Queue-Stände (aktive und Dead-Letter-Nachrichten, siehe unten) über alle drei Prioritäts-Queues, Laufzeiten, Ausführungen und erkannte Änderungen je Job sowie Exceptions. Es lässt sich zusätzlich nach Job und Zeitraster filtern. Es wird automatisch angelegt und erfordert keine Einrichtung.


Vorkonfigurierte Alerts (per Bicep)

Das Deployment kann Alert Rules für die Dead-Letter-Queue und die Queue-Länge mit anlegen. Sie sind standardmäßig deaktiviert:

Das Deployment erzeugt dabei auch die zugehörige Action Group (ag-<name>, Kurzname DLQAlert) — eine handangelegte Gruppe ist dafür nicht nötig.

TriggerStandard-SchwellwertParameterPrüfintervallSeverity
DLQ ≥ 11 NachrichtdlqLowThreshold5 Minuten3 — Informational
DLQ > 5050 NachrichtendlqMediumThreshold15 Minuten2 — Warning
DLQ > 200200 NachrichtendlqHighThreshold30 Minuten1 — Error
DLQ > 500500 NachrichtendlqCriticalThreshold5 Minuten0 — Critical
Queue-Länge > 300.000300.000 Nachrichten—15 Minuten2 — Warning

Ausgewertet werden die Service-Bus-Metriken DeadletteredMessages bzw. ActiveMessages über die Dimension EntityName = *, also je Queue einzeln. Alle Schwellwerte sind beim Deployment als Parameter überschreibbar.


Eigene Alert Rule anlegen

Azure Portal → Monitor → Warnungen → Warnungsregel erstellen

Beispiel: Fehler in GetChanges-Funktion

Scope: Application Insights-Ressource der DataBridge

Condition: Custom log search (KQL)

requests
| where operation_Name == "GetChanges"
| where success == false
| summarize FailedCalls = count() by bin(timestamp, 1h)

Alert-Logik:

EinstellungWert
MessgrößeFailedCalls
AggregationstypMaximum
Granularität1 Stunde
OperatorGrößer oder gleich
Schwellwert1 (sofort bei erstem Fehler) oder 5 (bei mehreren Fehlern)
Prüfhäufigkeit1 Stunde

Action Group: hier die zuvor angelegte Gruppe auswählen.


Empfohlene Alert Rules

RuleScopeBedingungSchwellwertPriorität
DLQ-Nachrichten vorhandenService BusDLQ Count > 01🔴 Kritisch
Fehler in GetChangesApp InsightsFehlgeschlagene Requests1/h🔴 Kritisch
GetChanges arbeitet nicht mehrApp InsightsAusführungen pro Stunde kleiner als 11🔴 Kritisch
Fehler in ProcessStandardQueueItemsApp InsightsFehlgeschlagene Requests5/h🟡 Warnung
Queue wächst unkontrolliertService BusQueue Length10.000🟡 Warnung
Hohe LaufzeitApp InsightsP95 > 30.000 ms30 Sekunden🔵 Info

Der Schwellwert für die Queue-Länge ist hier bewusst strenger gewählt als der Deployment-Standard (300.000) — er schlägt an, lange bevor sich ein Rückstau aufbaut.


Budget-Überwachung (empfohlen)

Zusätzlich zum operativen Monitoring empfiehlt sich eine Budget-Warnung auf Abonnement-Ebene:

Azure Portal → Cost Management → Budgets → Erstellen

  • Budget für die Ressourcengruppe der DataBridge festlegen
  • Alert bei 80 % und 100 % des Budgets
  • Empfänger: gleiche Action Group wie operative Alerts

So werden unerwartete Kostensteigerungen (z. B. durch Queue-Stau und erhöhte Service Bus-Operationen) frühzeitig erkannt.

6.3 - Grafana-Dashboard

Das mitgelieferte Betriebsdashboard in Application Insights — Erfolgsraten, Queue-Stände, Laufzeiten, Ausführungen und Fehler je Job, mit Job- und Zeitraster-Filter.

Seit v2.1 liefert jede Installation ein vorkonfiguriertes Grafana-Dashboard mit. Es zeigt auf einen Blick, ob die DataBridge läuft, wie schnell sie arbeitet und wo Fehler auftreten — ohne dass dafür KQL-Abfragen geschrieben werden müssen.

Wo finde ich es?

Azure Portal → Application Insights-Ressource der DataBridge → Bereich „Dashboards with Grafana". Das Dashboard ist außerdem direkt von der Function App aus verlinkt.

Die Dashboard-Ressource selbst liegt als dash-<name> in derselben Ressourcengruppe. Es handelt sich nicht um eine eigene Azure-Managed-Grafana-Instanz, sondern um das in Application Insights integrierte Grafana-Feature.

Aufbau

Das Dashboard heißt DataBridge Dashboard und zeigt standardmäßig die letzten 7 Tage. Ein automatischer Refresh ist nicht eingestellt — die Aktualisierung erfolgt über den Refresh-Knopf bzw. beim Neuladen.

Filter (Dashboard-Variablen)

Oben im Dashboard stehen zwei Variablen zur Verfügung, die für (fast) alle Panels gelten:

VariableTypBedeutung
Bin Size (bin_size)Auswahl: 5m, 10m, 15m, 30m, 1h, 2h, 4h, 12h, 1d (Standard: 10m)Größe der Zeit-Fenster, in die die Zeitreihen-Panels ihre Werte gruppieren. Kleinerer Wert = feinere, aber unruhigere Kurven; größerer Wert = glattere Kurven über einen langen Zeitraum.
Job Namen (jobName)Mehrfachauswahl aus den tatsächlich aktiven Jobs, inkl. „All"Schränkt alle job-bezogenen Panels auf die gewählten Jobs ein. Die Liste wird live aus den letzten Metrik-Daten gezogen (Jobs, die aktuell nur 0 liefern, tauchen nicht auf — siehe unten).

Kopfzeile: Erfolgsraten und Queue-Stände

Immer sichtbar, sieben Kacheln:

KachelTypZeigt
GetChanges SuccessRateTacho (0–100 %)Erfolgsquote der Änderungserkennung
ProcessQueueItems SuccessRateTacho (0–100 %)Erfolgsquote der Verarbeitung, über alle drei Queues (Low/Standard/High) zusammengefasst
Active MessagesTachoAktuell wartende Nachrichten, summiert über alle drei Export-Queues (sbqexport, sbqhighexport, sbqlowexport)
DeadletterTachoNachrichten in den Dead-Letter-Queues, summiert über alle drei Export-Queues
LowTacho (0–100 %)Erfolgsquote der Verarbeitung niedriger Priorität
StandardTacho (0–100 %)Erfolgsquote der Verarbeitung mit Standard-Priorität
HighTacho (0–100 %)Erfolgsquote der Verarbeitung hoher Priorität

Farbschwellen der vier SuccessRate-Tachos: rot ab 0 %, gelb ab 80 %, grün ab 95 %. Die beiden Service-Bus-Tachos (Active Messages, Deadletter) färben relativ zum aktuellen Wertebereich (grün → orange ab 70 % → rot ab 85 %), nicht anhand fester Nachrichtenzahlen — bei sehr wenigen Nachrichten insgesamt kann die Einfärbung daher empfindlicher wirken als bei einer großen Queue.

Alle sieben Kopfzeilen-Kacheln werten den im Dashboard gewählten Zeitraum aus (die SuccessRate-Kacheln über dashboardTime, die Service-Bus-Kacheln, weil Azure-Monitor- Platform-Metriken grundsätzlich immer an den Zeitraum gebunden sind).

Zeile „GetChanges"

Aufklappbar, drei Panels — Datenquelle sind hier weiterhin die von der Functions-Runtime automatisch erzeugten Host-Metriken (GetChanges Successes/Failures/AvgDurationMs/…), da GetChanges als Ganzes noch keine Job-granulare Erfolg/Fehler-Metrik im eigenen Code erzeugt (siehe Metriken):

PanelTypInhalt
AusführungenBalkenErfolgreiche gegen fehlgeschlagene Läufe, gruppiert nach Bin Size
LaufzeitenVerlaufDurchschnittliche, minimale und maximale Laufzeit in Millisekunden, gruppiert nach Bin Size
GetChanges per JobsVerlaufErkannte Änderungen je Job — basiert auf der Custom Metric ChangesDetected mit der Dimension JobName; reagiert auf Job Namen

Das Panel GetChanges per Jobs ist im Alltag das aussagekräftigste: Es zeigt pro Job, wie viele Änderungen erkannt wurden. Da auch Läufe ohne Änderungen den Wert 0 schreiben, bedeutet eine Lücke in der Kurve, dass der Job gar nicht gelaufen ist — nicht, dass es nichts zu tun gab.

Zeile „ProcessQueueItems"

Aufklappbar, vier Panels. Anders als bei GetChanges gibt es hier bereits eine job-granulare Custom Metric (MessagesProcessed, siehe Metriken), die zudem queue-übergreifend ist — alle drei Queues (Low/Standard/High) laufen durch denselben Verarbeitungscode und tauchen deshalb gemeinsam in einer Sicht auf, statt wie früher pro Queue getrennt zu sein:

PanelTypInhalt
AusführungenBalkenMessagesProcessed nach Ausgang (Success/Failed/Transitive), gruppiert nach Bin Size, gefiltert nach Job Namen
LaufzeitenVerlaufDurchschnittliche, minimale und maximale Verarbeitungsdauer (ProcessingDurationMs) über alle gefilterten Jobs zusammengefasst, gruppiert nach Bin Size
ProcessItems Success per JobVerlaufErfolgreich verarbeitete Nachrichten je Job (Outcome = Success), gruppiert nach Bin Size, gefiltert nach Job Namen
ProcessItems Errors per JobVerlaufFehlgeschlagene (Failed) und in eine Retry-Queue umgeleitete (Transitive) Nachrichten je Job, als getrennte Linien pro Job und Ausgang, gefiltert nach Job Namen

Transitive bedeutet: die Nachricht wurde nicht endgültig verworfen, sondern in eine Retry-Queue (TransitiveLow/TransitiveStandard/TransitiveHigh) umgeleitet, weil eine TransitiveException aufgetreten ist — kein finaler Fehler, aber auch kein abgeschlossener Erfolg.

Zeile „Fehler-Überblick"

Aufklappbar, ein Panel: Heatmap — Anzahl der Ausnahmen (exceptions-Tabelle) je Zeitfenster (Bin Size), aufgeschlüsselt nach Exception-Typ.

Was das Dashboard nicht abdeckt

Damit klar ist, wofür weiterhin andere Werkzeuge nötig sind:

  • Keine Job-genaue Fehlerursache für GetChanges — nur Timer-Lauf-Ebene, siehe Warnung oben. Für Details bleibt das Log (traces/exceptions) nötig.
  • Keine Fehlerquoten über requests oder dependencies — hierfür die KQL-Abfragen verwenden.
  • Keine Alarmierung — das Dashboard ist eine Ansicht, kein Alerting. Dafür sind die Alerts zuständig.

Eigene Anpassungen

Wer eine rein persönliche Ansicht braucht, legt sich im Portal ein eigenes, zusätzliches Dashboard an — dieses bleibt vom Deployment unberührt.

6.4 - Updates & Upgrades

Wie die DataBridge aktualisiert wird und welche Breaking Changes bei Versionssprüngen zu beachten sind.

Diese Seite beschreibt das Vorgehen bei einem Update. Die vollständigen Änderungen und Installationshinweise je Release stehen in der Versionshistorie.

Vorgehen bei einem Update

Die Konfiguration in Table Storage bleibt bei einem Update unverändert — es sei denn, ein Breaking Change erfordert Anpassungen.

Schritte

  1. Release Notes prüfen — Breaking Changes und Installationshinweise des Zielrelease und aller dazwischenliegenden Versionen lesen (Versionshistorie)
  2. Wartungsfenster einplanen — GetChanges über ein App-Setting deaktivieren:
    AzureWebJobs.GetChanges.Disabled = true
    
    Bei Bedarf lassen sich die Verarbeiter analog abschalten (AzureWebJobs.ProcessStandardQueueItems.Disabled usw.)
  3. Warten, bis alle Queue-Nachrichten verarbeitet wurden (Queue-Länge im Azure Portal prüfen)
  4. Neue Version einspielen — je nach Betriebsart:
    • Container: Image-Tag auf die neue Version setzen
    • Marketplace: Assistenten erneut durchlaufen (bei unveränderten Standardwerten ergibt sich keine Änderung an bestehenden Einstellungen)
    • Bicep / Pipeline: Deployment erneut ausführen
    • Zip-Upload: Paket über die Pipeline oder manuell einspielen
  5. Konfigurationsanpassungen aus den Installationshinweisen vornehmen
  6. GetChanges wieder aktivieren:
    AzureWebJobs.GetChanges.Disabled = false (oder Eintrag löschen)
    
  7. Ersten Lauf beobachten — z. B. über die Custom Metric ChangesDetected je Job oder die Fehlerquoten-Abfrage unter Performance & Laufzeiten

Breaking Changes je Version

Die vollständigen Breaking Changes und Installationshinweise stehen ausschließlich in den Release Notes der jeweiligen Version — hier nur der Überblick, ob ein Übergang überhaupt einen Breaking Change enthält:

ÜbergangBreaking ChangeDetails
→ v2.3 (aktuell)✅Release Notes v2.3
→ v2.2✅Release Notes v2.2
→ v2.1✅Release Notes v2.1
→ v2.0❗ .NET 10 RuntimeRelease Notes v2.0
1.0 → 1.1, 1.1 → 2.0❗ je ÜbergangÄltere Releases

Beim Überspringen von Versionen sind die Release Notes aller dazwischenliegenden Versionen zu beachten, nicht nur die des Zielrelease.

7 - Versionshistorie

Release-Dokumentation aller Versionen der dsyr.DataBridge — Änderungen, neue Funktionen und Installationshinweise.

Hier findet sich die Release-Dokumentation für alle Versionen der dsyr.DataBridge.

Jede Version beschreibt die wesentlichen Änderungen aus Anwender-Sicht sowie Installationshinweise für Administratoren, die ein bestehendes System aktualisieren. Die Details zu etwaigen Breaking Changes stehen jeweils im Abschnitt Installationshinweise der verlinkten Release-Seite.

ReleaseSchwerpunktBreaking Change
v2.3Tabellen- und Spalten-Metadaten der SQL-Connectoren (MSSQL, MySQL, Oracle, PostgreSQL) über den MCP-Server, Mapping-Abfrage per Name, schlankeres NAV-Logging, Managed Identity (System/User-assigned) für D365BC und D365CE mit neuem Verbindungsfeld authmethod; für Entwickler: lokale Podman-Testumgebung mit HTTP-Mocks, Service-Bus-Emulator und Debug-Modus✅
v2.2Neuer OpenAPI-Connector, eingebauter MCP-Server für KI-Assistenten, Action-Jobs, System-assigned Managed Identity für die Function App, Verbesserungen am Grafana-Dashboard✅
v2.1Erweiterte Lookup-Funktionen, Sampling von Abhängigkeiten, Grafana-Dashboard, Watermark-Korrekturen✅
v2.0Umstellung auf .NET 10, Containerisierung, gepuffertes Logging, Custom Metrics❗
Ältere ReleasesMigrationshinweise für die Übergänge 1.0 → 1.1 und 1.1 → 2.0❗

7.1 - Release v2.3

Managed Identity (system- und benutzer-zugewiesen) für D365BC und D365CE, Tabellen- und Spalten-Metadaten der SQL-Connectoren (MSSQL, MySQL, Oracle, PostgreSQL) über den MCP-Server, genauere Datentypen, Mapping-Abfrage per Name, schlankeres NAV-Logging, wieder funktionierendes Lesen aus PostgreSQL, MySQL und Oracle, behobene Abbrüche beim Schreiben von SQL-Datums-, Ganzzahl- und Fließkommafeldern nach BC/NAV/CE/OpenAPI und eine lokale Podman-Testumgebung mit Datenbanken, HTTP-Mocks und einem Service-Bus-Emulator für BC, NAV und OpenAPI.

Was ist neu

Neue Funktionen

  • Metadaten-Abfrage für die SQL-Connectoren: Die MCP-Tools list_connector_tables und get_connector_table_schema funktionieren jetzt auch für MSSQL, MySQL, Oracle und PostgreSQL. Bisher war das nur für BC, NAV, CE (Dataverse) und OpenAPI möglich, bei SQL-Verbindungen brach der Aufruf mit einer Fehlermeldung ab. Geliefert werden Spaltenname, Typ, Nullable, maximale Länge, bei Dezimalspalten Precision und Scale sowie der Primary Key — direkt aus dem Datenbank-Katalog (INFORMATION_SCHEMA, bei Oracle USER_*/ALL_*-Views).

    Über apiPath wird das Schema gewählt (MySQL: die Datenbank, Oracle: der Owner). Ohne Angabe gilt das Standard-Schema der Verbindung, sodass gleichnamige Tabellen in verschiedenen Schemas (z. B. dbo.Customers / archive.Customers) nicht vermischt werden. Die Metadaten werden zwischengespeichert; Schemaänderungen an der Datenbank werden erst nach einem Neustart der Function App sichtbar. Details: MCP-Server → Operationen.

  • Genaue .NET-Datentypen je Datenbank: Die Spaltentypen werden je Datenbank passend abgebildet — z. B. date als DateOnly, time als TimeSpan, Spalten mit Zeitzone als DateTimeOffset, Binärspalten als Byte[], MySQL BOOLEAN (tinyint(1)) als Boolean und Oracle NUMBER(p,0) je nach Stellenzahl als Int16/Int32/Int64. Die vollständige Übersicht steht unter MCP-Server → Operationen.

  • Managed Identity für D365CE (Dataverse): Die Verbindung kann statt über einen Connection-String mit Secret direkt über die Managed Identity der Function App aufgebaut werden. Dazu url (und optional clientid für eine benutzer-zugewiesene Identity) in der Connection angeben. Ein vorhandener connectionstring hat weiterhin Vorrang. Siehe D365CE.

  • Managed Identity für D365BC (Cloud): Neue Authentifizierungsart ManagedIdentity für HTTP-Konnektoren. Ohne clientId wird die system-zugewiesene, mit clientId die benutzer-zugewiesene Identity genutzt. Der Token wird automatisch erneuert, bevor er abläuft, und mit dem scope der Connection angefordert. Siehe D365BC.

  • Neuer Verbindungsparameter authmethod: Erlaubt es, die Authentifizierungsmethode ausdrücklich festzulegen (BasicAuthentication, OAuth2, APIKey, ManagedIdentity). Ohne Angabe bleibt das bisherige Verhalten unverändert. ManagedIdentity wird nie automatisch aus fehlenden Zugangsdaten abgeleitet, damit eine unvollständige Connection weiterhin sofort mit einem klaren Fehler abbricht. Siehe Verbindungen.

Fehlerbehebungen & Verbesserungen

Allgemein

  • Felder fehlten gelegentlich in der Verarbeitung: Wurden die Mappings neu geladen, während gleichzeitig Änderungen gelesen oder Nachrichten verarbeitet wurden, konnte ein Job auf ein noch unvollständiges Mapping treffen. Einzelne Felder, z. B. die Kontaktnummer einer Firma, wurden dann nicht übertragen. Die Mappings werden jetzt vollständig aufgebaut und erst danach aktiv geschaltet.
  • Service Bus: Skalierung ohne Managed Identity: Im schlüsselbasierten Modus (Standard, enableManagedIdentity nicht gesetzt) setzt das Bicep-Deployment jetzt TARGET_BASED_SCALING_ENABLED=0. Der Host skaliert dann direkt über den Metrics Monitor, und die bisherige Warnung zum Target-based Scaling im Log entfällt. Siehe Umgebungsvariablen.
  • Mapping-Abfrage über den Namen: Das MCP-Tool get_mapping und der HTTP-Endpunkt GET /mapping/{name} finden ein Mapping jetzt über seinen Namen — also den Namen, auf den ein Job verweist und unter dem list_mappings es auflistet. Bisher wurde nur der RowKey des ersten Feldeintrags verglichen, sodass ein Mapping über seinen Namen meist nicht gefunden wurde. Die bisherige Suche über den RowKey funktioniert weiterhin.
  • get_connector_table_schema akzeptiert jetzt auch den kombinierten Tabellennamen: Bei BC/NAV/CE/OpenAPI wird der API-Pfad in Jobs/Mappings üblicherweise zusammen mit der Tabelle geschrieben (z. B. v2.0/customers). Wurde dieser kombinierte Wert unverändert als tableName übergeben und apiPath weggelassen, schlug die Metadaten-Abfrage mit einer schwer deutbaren Exception fehl (Could not read Metadata for path). Das Tool trennt den API-Pfad jetzt selbst ab, wenn apiPath leer bleibt und tableName einen / enthält — Aufrufe mit getrennten Parametern funktionieren unverändert.
  • Nachvollziehbare MCP-Aufrufe im Log: Jeder MCP-Aufruf schreibt eine Logzeile mit dem Namen der Operation und — wo vorhanden — dem gelesenen Job, Mapping bzw. der Connection und Tabelle, z. B. MCP get_connector_table_schema: connection=oracle-dev, table=Account. Datensatzinhalte oder Zugangsdaten werden nicht geloggt.
  • Aktualisierte Abhängigkeiten (u. a. Azure.Data.Tables, Azure.Messaging.ServiceBus, Azure.Security.KeyVault.Secrets 4.11.2, Microsoft.IdentityModel.Tokens, Microsoft.OData.Core/Microsoft.OData.Edm auf 9.0.0, Microsoft.Data.SqlClient 7.1.1, MySql.Data 26.10.0, Oracle.ManagedDataAccess.Core 23.26.400, Microsoft.OpenApi 3.11.0, ModelContextProtocol 2.3.0, Durable-Functions-Extension 1.20.1 — rein intern, keine Verhaltensänderung). System.Security.Cryptography.Xml steht auf 10.0.14 und schließt damit mehrere als „hoch“ eingestufte Sicherheitslücken der Vorversion.

D365CE (Dataverse)

  • Fehlender oder inaktiver Alternativschlüssel wird nicht mehr als „Datensatz nicht gefunden“ gewertet: Ist der in TargetKey verwendete Schlüssel in Dataverse nicht definiert oder nicht aktiv, bricht die Verarbeitung jetzt mit einem Fehler ab. Die Nachricht wird wiederholt und landet anschließend in der Dead-Letter-Queue. Bisher wurde der Datensatz in diesem Fall als nicht vorhanden behandelt und neu angelegt, was zu Dubletten führen konnte. Siehe D365CE.
  • Fehler beim Nachschlagen eines Datensatzes führten still zu falschen Daten: Schlug das Lesen des bestehenden Zieldatensatzes fehl, etwa durch Throttling (Service-Protection-Limits), Timeout, Netzwerk- oder Rechtefehler, wurde das bisher wie „Datensatz existiert nicht“ behandelt. Die Folgen blieben unbemerkt: Löschungen wurden übersprungen und die Nachricht als erledigt abgeschlossen, Felder mit leerem Quellwert wurden im Ziel nicht geleert, und Lookups mit DoNotCreate konnten eine bestehende Referenz leeren. Jetzt gilt nur noch die Dataverse-Meldung „Datensatz nicht gefunden“ als solcher; jeder andere Fehler wird mit Details geloggt, die Nachricht wiederholt und notfalls in die Dead-Letter-Queue gelegt.
  • Nachgeschlagene Werte wurden nicht aktualisiert, wenn sie beim ersten Lesen noch fehlten: Lookups mit UseCache (z. B. die BC-Kontaktnummer der Firma auf Kontakten und Verkaufschancen) speicherten auch ein leeres Ergebnis und behielten einen einmal gelesenen Wert bis zum Neustart der Function App. War die Nummer beim ersten Lesen noch nicht aus BC zurückgeschrieben, wurde das Feld deshalb auch danach leer übertragen. Leere Ergebnisse werden jetzt nicht mehr gespeichert, und beim Lesen der Änderungen ersetzt ein frisch gelesener Wert immer den gespeicherten. Nicht mehr vorhandene Datensätze werden aus dem Zwischenspeicher entfernt.
  • Boolean-Felder aus Ganzzahlen befüllen: Ja/Nein-Felder akzeptieren jetzt auch die Quelltypen byte (z. B. SQL tinyint) und int32 (0 = Nein, sonst Ja). Statusfelder (statecode) akzeptieren zusätzlich byte.

BC, NAV & OpenAPI

  • HTTP-Konnektoren (BC, NAV, OpenAPI) konnten nach dem Start dauerhaft ausfallen: Verarbeiteten mehrere Queue-Nachrichten gleichzeitig ihren ersten Aufruf an dasselbe Zielsystem, richteten sie den gemeinsamen HTTP-Client parallel ein. Das führte zu Fehlern wie Object reference not set to an instance of an object oder This instance has already started one or more requests. Danach blieb der Client unvollständig eingerichtet (z. B. ohne Authentifizierung), und alle weiteren Nachrichten an dieses System schlugen bis zum Neustart der Function App fehl. Die Einrichtung ist jetzt gegen parallelen Zugriff geschützt, und eine fehlgeschlagene Einrichtung wird beim nächsten Aufruf wiederholt.
  • Update nach BC oder NAV schlug fehl, wenn ein Zielfeld dort bereits leer war: War ein Feld im bestehenden BC- oder NAV-Zieldatensatz null (z. B. ein noch nicht gesetztes Abschlussdatum) und hatte sich irgendein anderes Feld des Datensatzes geändert, brach die Aktualisierung mit einer NullReferenceException ab. Der interne Änderungsvergleich ging fälschlich davon aus, dass ein vorhandenes Feld nie leer sein kann. Behoben.
  • BC/NAV: Update mit TargetKey vom Typ Filter schlug bei Guid- oder Zahl-Schlüsseln fehl: Der Schlüssel aus KeyAttributeFromExisting wurde immer in Hochkommas gesetzt (z. B. customers('1111…')). Bei Schlüsselfeldern vom Typ Guid oder Zahl, etwa dem BC-Feld id, ergab das eine ungültige Anfrage. Der Schlüssel wird jetzt passend zum Feldtyp aus den Metadaten geschrieben, und Hochkommas im Wert werden maskiert. Fehlt das Feld im bestehenden Datensatz, bricht die Verarbeitung mit einer klaren Fehlermeldung ab statt mit einer NullReferenceException.
  • BC: Tabellen-Metadaten wurden zwischen API-Pfaden vermischt und nicht gefundene Tabellen dauerhaft zwischengespeichert: Der Metadaten-Cache unterschied Tabellen nur nach ihrem Namen, nicht nach dem API-Pfad. Gab es eine Tabelle (z. B. customers) unter einem API-Pfad nicht, unter einem anderen aber schon, konnte das Ergebnis des einen Pfads für den anderen verwendet werden. War eine Tabelle beim ersten Zugriff noch nicht in $metadata enthalten (z. B. weil eine Extension erst später veröffentlicht wurde), blieb dieser Fehlschlag bis zum Neustart der Function App bestehen. Der Cache unterscheidet jetzt nach API-Pfad und Tabelle. Nicht gefundene Tabellen werden nicht mehr gespeichert, beim nächsten Aufruf wird $metadata neu gelesen.
  • NAV: Metadaten nicht mehr im Log, klare Fehlermeldung: Der NAV-Konnektor hat beim Laden der Metadaten die komplette $metadata-Antwort auf Level Information geloggt. Bei echten NAV-Installationen sind das oft mehrere hundert KB bis einige MB je Neustart. Das erzeugte unnötiges Log-Volumen in Application Insights und wurde dort ohnehin abgeschnitten. Der Konnektor verhält sich jetzt wie der BC-Konnektor und loggt nur noch die Anzahl der gefundenen EntitySets. Außerdem prüft er den Statuscode. Antwortet der Server nicht mit Erfolg (z. B. 401 oder 500), erscheint Could not read Metadata from <URL> (StatusCode: …) statt eines schwer deutbaren Parse-Fehlers. Die Metadaten werden weiterhin tolerant gelesen.
  • NAV: Später veröffentlichte Tabellen wurden erst nach einem Neustart erkannt: Fehlte eine Tabelle beim ersten Laden der Metadaten, blieb das veraltete Modell bis zum Neustart der Function App aktiv. Jetzt werden die Metadaten beim nächsten Aufruf neu gelesen. Außerdem wird $metadata nicht mehr doppelt abgerufen, wenn sowohl die Tabellenliste als auch die Tabellen-Metadaten benötigt werden.
  • Aktualisieren eines bestehenden Datensatzes in einer OpenAPI-Zielressource schlug grundsätzlich fehl: Jeder Schreibvorgang, der einen Datensatz aktualisiert statt neu anzulegen (PATCH/PUT), brach mit einer NullReferenceException ab — betroffen war der reguläre Verarbeitungspfad ohne Antwort-Job, also der Normalfall. Ursache war eine fehlende Null-Prüfung beim Zusammenbauen des Request-Bodys. Neuanlagen (POST) waren nicht betroffen. Behoben.
  • OpenAPI: Aktualisieren und Löschen mit TargetKey vom Typ Filter gingen an eine falsche URL: Der Konnektor setzte den Suchfilter selbst als Schlüssel in die Item-URL ein. Jetzt wird zuerst der passende Datensatz gesucht und dessen Schlüssel verwendet: das Feld aus KeyAttributeFromExisting, sonst der Primärschlüssel aus der Spezifikation (id, uuid …). Findet der Konnektor beim Löschen keinen Datensatz, schreibt er eine Warnung ins Log und sendet kein DELETE. Der Fehler bestand seit v2.2.
  • OpenAPI: Gleichnamige Ressourcen verschiedener Connections wurden vermischt: Boten zwei OpenAPI-Verbindungen eine Ressource mit demselben Namen an (z. B. clients), verwendete die zweite das Schema der ersten. Der Fehler bestand seit v2.2. Jetzt wird das Schema je Spezifikation zwischengespeichert.
  • Doku OpenAPI-Konnektor korrigiert: Der Hinweis, dass der Konnektor die Spezifikation aus einer festen lokalen Datei lädt, war veraltet. Die Spezifikation wird über openApiSpecUrl geladen. Neu dokumentiert sind zwei bekannte Einschränkungen (siehe unten).

SQL-Konnektoren (MSSQL, PostgreSQL, MySQL, Oracle)

  • PostgreSQL, MySQL und Oracle als Quelle: Lesen schlug grundsätzlich fehl: Der Konnektor erzeugte eine ungültige Abfrage, weil zwischen der letzten Spalte und FROM das Leerzeichen fehlte (z. B. …,RevenueFROM Account). Jeder Job mit einer dieser Datenbanken als Quelle brach mit einem Datenbankfehler ab (PostgreSQL: column … does not exist, MySQL: Unknown column, Oracle: ORA-00904). Der Fehler bestand seit v2.2. MSSQL war nicht betroffen. Behoben.
  • MSSQL: Spaltennamen werden immer in eckige Klammern gesetzt: Alle Spalten aus dem Mapping und das WatermarkField werden jetzt in [...] eingeschlossen, nicht mehr nur Namen mit Leerzeichen. Damit funktionieren auch reservierte Wörter wie Order oder Key als Spaltenname. Für Mappings mit reinen Spaltennamen ändert sich nichts. Ausdrücke oder Aliasse als SourceField wurden schon vorher nicht unterstützt. Siehe MSSQL.
  • Datumsfelder aus SQL-Quellen (DATE-Spalte) nach BC, NAV, Dataverse (CE) oder OpenAPI schlugen fehl: Stammte ein Datumswert aus einer MSSQL-, PostgreSQL-, MySQL- oder Oracle-Spalte vom Typ DATE (intern als DateOnly geführt) und das Zielfeld lag bei BC, NAV, Dataverse oder einer OpenAPI-Zielressource, brach die Verarbeitung mit Source Type not yet implemented ab. Die Datums- und Datum/Zeit-Resolver kannten nur ihren eigenen internen Typnamen „Date“, nicht „DateOnly“. Betroffen waren ausschließlich Jobs mit einer SQL-Datenbank als Quelle. Behoben, indem alle Datums-Resolver jetzt beide Typnamen akzeptieren.
  • Ganzzahl- und Fließkommafelder aus SQL-Quellen mit TINYINT/SMALLINT/BIGINT/REAL schlugen fehl: Aus demselben Grund brach die Verarbeitung mit Source Type not yet implemented ab, wenn eine SQL-Spalte vom Typ TINYINT, SMALLINT, BIGINT oder einem kleinpräzisen FLOAT/REAL in ein Ganzzahl-, Dezimal- oder Fließkomma-Zielfeld bei BC, NAV oder Dataverse geschrieben wurde. Auch hier waren nur Jobs mit SQL als Quelle betroffen. Behoben.
  • MySQL: vorzeichenbehaftete TINYINT-Spalten in Ganzzahlfelder: Der MySQL-Treiber liefert diese Spalten als SByte. Beim Schreiben in ein Ganzzahlfeld bei BC, NAV, Dataverse oder OpenAPI brach die Verarbeitung mit Source Type not yet implemented ab. Behoben.

Entwickler-Features

Diese Neuerungen betreffen nur die Entwicklung der DataBridge im Repository, nicht den Betrieb einer Instanz.

  • Lokale Podman-Testumgebung: dev/up.sh bzw. dev/up.ps1 startet die DataBridge aus dem aktuellen Quellcode zusammen mit Azurite und wahlweise MSSQL, PostgreSQL, MySQL und Oracle (dev/up.sh mssql oracle, dev/up.sh all). In jeder Datenbank werden dieselben Beispieltabellen Account, Opportunity und OpportunityProduct mit Testdaten angelegt, die möglichst viele Datentypen abdecken. Connections, Jobs für jede Datenbank-Kombination und Mappings werden automatisch erzeugt. Details: Entwicklung → Podman-Testumgebung.
  • HTTP-Mocks für Business Central, NAV und OpenAPI: Die Testumgebung kann zusätzlich einen WireMock-Container starten, der BC (OAuth), NAV (Basic) und eine OpenAPI-Beispiel-API (API-Key) nachbildet. Die Mocks sind einzeln wählbar und lassen sich mit Datenbanken mischen (dev/up.sh mssql bc openapi, all für alles). WireMock startet nur, wenn mindestens ein Mock gewählt ist. Er lädt nur die Regeln der gewählten Mocks. Die Regeln decken den ganzen HTTP-Ablauf der Konnektoren ab: Verbindungstest, $metadata bzw. Spezifikation, Lesen mit Paging, Upsert mit Lookup, PATCH oder POST, sowie Löschen. Jeder Mock bildet jetzt alle drei Beispieltabellen ab (Account, Opportunity, OpportunityProduct — zuvor nur Account); Connections, Jobs und Mappings SQL ↔ Mock werden für alle drei automatisch angelegt. Was die DataBridge gesendet hat, zeigt das Anfrage-Protokoll unter localhost:8089/__admin/requests. Details: Entwicklung → Podman-Testumgebung.
  • MCP-Server databridge-dev: Die .mcp.json enthält einen zweiten Server, der fest auf die lokale Testumgebung zeigt. So stehen in Claude Code Cloud-Instanz (databridge) und Testumgebung (databridge-dev) nebeneinander zur Verfügung. Details: Entwicklung → MCP für die Testumgebung.
  • Neuer Dokumentationsbereich „Entwicklung“ mit der Beschreibung beider Werkzeuge.
  • Architektur im Detail: Die bisher nur intern gepflegte technische Detaildokumentation (Systemkontext, Bicep-Module, DI und host.json, HTTP- und Nachrichten-Referenz, Sequenzdiagramme, Abhängigkeiten, Fähigkeiten-Katalog) ist überarbeitet, auf den Stand 2.3 gebracht und unter Entwicklung → Architektur im Detail veröffentlicht. Die Übersicht unter Überblick → Architektur verweist darauf. Nebenbei korrigiert: „Wie es funktioniert“ nannte einen Timer-Takt von zwei Minuten (tatsächlich 15 Sekunden) und beschrieb die Transitive-Queues als automatische Wiederholungs-Queue.
  • Service-Bus-Emulator, schaltbare Verarbeitung, Debug-Modus: Die Testumgebung hat jetzt einen Service-Bus-Emulator (Profil servicebus), damit Jobs auch lokal von Anfang bis Ende laufen können. GetChanges (Timer) und Process*QueueItems lassen sich einzeln zuschalten (dev/up.sh mssql process), und dev/up.sh --host (bzw. dev\up.ps1 -HostMode) startet die Umgebung ohne den databridge-Container, damit sich ein lokal laufender Prozess mit dem Debugger daran hängen lässt. Details: Entwicklung → Podman-Testumgebung und Entwicklung → Debuggen.
  • Web-Oberfläche für den Service-Bus-Emulator: Läuft automatisch mit, sobald das Profil servicebus aktiv ist (http://localhost:8090, Port über SERVICEBUS_EXPLORER_PORT konfigurierbar) — zeigt Queues, Nachrichtenzahlen und einzelne Nachrichten grafisch an. Community-Tool Service-Bus-Emulator-Explorer, nicht von Microsoft. Der klassische Service Bus Explorer funktioniert mit dem Emulator nicht. Details: Entwicklung → Debuggen → Gegen Podman.
  • Fix: dev/down.sh/dev/down.ps1 ließen Service-Bus- und databridge-Container zurück. Die beiden Skripte kannten nur die Datenbank- und wiremock-Profile, nicht die seit dieser Version existierenden Profile servicebus und databridge — ein Reset (down -v) räumte deshalb servicebus, servicebus-db, servicebus-explorer und den databridge-Container nicht mit auf. Behoben, indem beide Profile in die feste Profil-Liste aufgenommen wurden.
  • Azurite-Connection-String direkt aus up.sh/up.ps1: Die Start-Zusammenfassung gibt jetzt den vollständigen, einsatzbereiten Connection-String für den Azure Storage Explorer aus, statt nur UseDevelopmentStorage=true. Details zum Verbinden: Entwicklung → Podman-Testumgebung → Azurite einsehen.
  • Fix: docker build aus einem frischen Checkout schlug fehl. Das Dockerfile kopierte Directory.Packages.props nicht vor dem dotnet restore-Schritt, wodurch das zentrale Paket-Versionsmanagement (ManagePackageVersionsCentrally) ins Leere lief (NU1015). Betraf nur den lokalen Container-Build (dev/up.sh/dev/up.ps1, src/compose.yml); die produktive Auslieferung baut nicht über dieses Dockerfile. Behoben.
  • Fix: dev\up.ps1/dev\down.ps1 unter Windows. Nach einem podman machine stop blieb eine Socket-Datei liegen, an der beim nächsten Start win-sshproxy scheiterte. Ohne die Docker-API-Pipe brach danach jeder podman compose-Aufruf ab. Beide Skripte räumen die Datei jetzt vor dem VM-Start weg und warten, bis Compose die VM erreicht. down.ps1 -Reset startet eine gestoppte VM selbst, down.ps1 ohne -Reset endet bei gestoppter VM ohne Fehler. Unter Windows PowerShell 5.1 brach up.ps1 außerdem ab, weil Compose seinen Fortschritt auf stderr schreibt, und zeigte Umlaute verstümmelt an. Beides behoben; die Skripte laufen unter PowerShell 7 und 5.1. Details: Entwicklung → Podman-Testumgebung → Fehlersuche.
  • Doku-Website auf Docsy 0.18.0: Das Theme ist mit dem Umzug des Docsy-Projekts unter github.com/docsy/docsy erreichbar; der Hugo-Modulpfad, die Mermaid-Einstellung (jetzt ein Docsy-Plugin) und Dart Sass (1.105.0) sind angepasst. Inhalt und Darstellung der Seiten bleiben gleich.
  • Build bricht bei NuGet-Paketen mit bekannten Sicherheitslücken ab. NuGet Audit prüft jetzt alle Projekte inklusive transitiver Pakete, und Treffer ab Schwere „moderate“ gelten als Fehler statt als Warnung („low“ bleibt eine Warnung) — lokal, im Dockerfile und in der Pipeline. Details: Entwicklung → Architektur → Abhängigkeiten.

Bekannte Einschränkungen

  • OpenAPI – CheckList: Der Diagnose-Endpunkt meldet für OpenAPI-Mappings fälschlich „could not find table“. GetTableNames liefert die Pfade der Spezifikation (/v1/customers) statt der Ressourcennamen (customers). Das Lesen und Schreiben ist nicht betroffen.
  • OpenAPI – Primärschlüssel: get_connector_table_schema weist bei OpenAPI keinen Primärschlüssel aus. Intern erkennt der Konnektor ihn weiterhin am Property-Namen.
  • PostgreSQL/Oracle – CheckList: Der Diagnose-Endpunkt meldet für Mappings mit PostgreSQL- oder Oracle-Tabellen fälschlich „could not find table“. Der Vergleich der Tabellennamen berücksichtigt nicht, dass PostgreSQL Namen kleingeschrieben und Oracle großgeschrieben ablegt. Das Lesen ist nicht betroffen.
  • BC/NAV – Zeitstempel: In den Metadaten werden Edm.DateTimeOffset-Felder wie lastModifiedDateTime intern mit demselben Typnamen geführt wie Edm.Date. Das betrifft nur die interne Typkennzeichnung des Quellwerts; die Uhrzeit geht dabei nachweislich nicht verloren, da die Datums-Resolver den tatsächlichen Wert unabhängig von dieser Kennzeichnung parsen.

Installationshinweise

Update von ZIP-Paket bzw. Container-Image (containered-connector:v2.3.0). Keine neuen Azure-Ressourcen. Das Bicep-Deployment setzt im schlüsselbasierten Modus zusätzlich das App-Setting TARGET_BASED_SCALING_ENABLED=0. Bei einem reinen ZIP- oder Image-Update ohne erneutes Deployment fehlt es nur, dann bleibt die Warnung im Log, die Funktion ist davon nicht betroffen.

Die Metadaten der SQL-Connectoren liefern gegenüber früheren Vorabständen teils andere Typnamen (z. B. DateOnly statt DateTime für date-Spalten). Das betrifft nur die Ausgabe von get_connector_table_schema; das Lesen von Daten ändert sich dadurch nicht.

Breaking Changes & Migrationsschritte

Verhaltensänderung bei CE-Jobs: Ist der in TargetKey verwendete Alternativschlüssel in Dataverse nicht definiert oder nicht aktiv, landen Nachrichten ab 2.3 in der Dead-Letter-Queue, statt dass der Datensatz neu angelegt wird.

  1. Vor dem Update für jeden CE-Zieljob mit CustomKey prüfen, dass der Alternativschlüssel in Dataverse existiert und aktiv ist (Power Apps → Tabelle → Schlüssel, Status „Aktiv“).
  2. Nach dem Update die Dead-Letter-Queues beobachten. Nachrichten mit einem Schlüsselfehler nach dem Aktivieren des Schlüssels erneut einreihen.

Verhaltensänderung bei CE-Zielen unter Last: Fehler beim Nachschlagen eines Zieldatensatzes (z. B. Throttling durch die Dataverse-Service-Protection-Limits) werden nicht mehr verschluckt, sondern lösen eine Wiederholung der Nachricht aus. Bei stark ausgelasteten Umgebungen können deshalb mehr Wiederholungen und, wenn die Limits länger anhalten, Einträge in der Dead-Letter-Queue auftreten — bisher wurden in diesen Fällen still falsche oder unvollständige Daten geschrieben. Diese Nachrichten nach Abklingen der Last erneut einreihen.

Managed Identity nutzen (optional)

Die Identity muss im jeweiligen Zielsystem berechtigt werden, das Bicep-Deployment erledigt das nicht:

  1. D365CE: Identity in der Dataverse-Umgebung als Anwendungsbenutzer anlegen und eine Sicherheitsrolle zuweisen.
  2. D365BC: Identity in Business Central unter Microsoft-Entra-Anwendungen anlegen, aktivieren und Berechtigungssätze zuweisen.
  3. Connection anpassen: für BC "authmethod": "ManagedIdentity" setzen, für CE url statt connectionstring verwenden.

7.2 - Release v2.2

Neuer generischer REST/OpenAPI-Connector, ein eingebauter MCP-Server für KI-Assistenten, Action-Jobs, System-assigned Managed Identity für die Function App sowie tolerantere Behandlung leerer Schlüssel im D365CE-Upsert.

Was ist neu

Neue Funktionen

  • Neuer Connector: generische REST-API über OpenAPI-Spezifikation: Neben BC, CE, NAV und den SQL-Connectoren steht jetzt ein generischer HTTP-Connector zur Verfügung, der ein beliebiges REST-API anhand seiner OpenAPI-/Swagger-Beschreibung anbindet (neuer Verbindungstyp OPENAPI). Damit lassen sich Systeme ohne eigenen Connector über ihr veröffentlichtes API-Schema anbinden, statt eine dedizierte Implementierung zu benötigen. Details, Konfigurationsbeispiel und unterstützte Auth-Methoden siehe OpenAPI-Connector.

  • Neue Authentifizierungsart „API-Key“ für HTTP-Connectoren: Zusätzlich zu Basic-Auth und OAuth2 unterstützen alle HTTP-basierten Connectoren (BC, NAV, OpenAPI) jetzt die Authentifizierung über einen statischen API-Key, der als Header an jede Anfrage angehängt wird (apiKeyHeader / apiKeyValue in der Connection). Zusätzlich kann der OAuth2-Token-Endpunkt über tokenUrl frei konfiguriert werden, statt fest auf den Microsoft-Login-Endpunkt zu zeigen — relevant für Systeme mit eigenem oder abweichendem Token-Server.

  • Action-Jobs (InvokeAction): Jobs können jetzt statt eines CRUD-Datenabgleichs eine einzelne benannte Aktion auf dem Zielsystem auslösen (z. B. eine OpenAPI-Action/operationId oder einen D365CE-OrganizationRequest). Ein solcher Job benötigt kein Mapping in der Mapping-Tabelle — die Aktion und ihre Parameter werden direkt aus der Queue-Nachricht und dem Job selbst gebildet. Siehe Action — welche Änderungsarten gelesen werden.

  • Ressourcenpfad direkt am Job konfigurierbar: Jobs können nun ihre Quell- und Zielressource (Tabelle, Entität oder Pfad) direkt in den neuen Job-Feldern Source/Target tragen, statt sie zwingend über die Mapping-Tabelle zu beziehen. Bestehende, ausschließlich über die Mapping-Tabelle konfigurierte Jobs funktionieren unverändert weiter; ohne Eintrag in Source/Target greift wie bisher die Mapping-Tabelle.

  • Kind-Datensätze einbetten (EmbedChildren, D365CE): Eine neue Source-Operation erlaubt es, die Datensätze eines referenzierten Kind-Jobs (z. B. Angebotspositionen zu einem Angebot) direkt in die Queue-Nachricht des Eltern-Datensatzes einzubetten, sodass beide gemeinsam in einer Anfrage beim Ziel ankommen. Der Filter des Kind-Jobs kann dabei über {{...%parent.Feldname%...}} auf Werte des Eltern-Datensatzes zugreifen.

  • Neue Diagnose-Endpunkte GET /job und GET /mapping: Zwei neue, rein lesende HTTP-Endpunkte (/job, /job/{rowKey}, /mapping, /mapping/{rowKey}) geben die aktuell geladene Job- bzw. Mapping-Konfiguration zurück — praktisch zur Fehlersuche, ohne direkt in der Table-Storage nachsehen zu müssen.

  • Eingebauter MCP-Server für KI-Assistenten (z. B. Claude Code, GitHub Copilot, Microsoft 365 Copilot): Ein neuer, rein lesender Model-Context-Protocol-Endpunkt (POST /api/mcp) macht Jobs, Mappings sowie Service-Bus-Diagnosedaten (Queue-Laufzeit-Status, non-destruktives Peek von Nachrichten und Dead-Letter-Nachrichten) als KI-Tools ansprechbar — dediziert dafür gibt es keinen eigenen HTTP-Endpunkt. Zwei der zehn Tools gehen noch einen Schritt weiter: list_connector_tables und get_connector_table_schema lesen die echten Tabellen-/Feld-Metadaten (Typ, Nullable, maximale Länge, Primary Key) direkt aus dem Quell-/Zielsystem einer Connection — für BC, NAV, CE (Dataverse) und OpenAPI —, sodass sich ein Mapping gegen das tatsächliche Schema validieren lässt, statt sich auf die Mapping-Konfiguration allein zu verlassen. Für die SQL-Connectoren (MSSQL, MySQL, Oracle, PostgreSQL) ist das noch nicht implementiert.

    Die beiden Peek-Tools geben bewusst niemals den Nachrichteninhalt zurück, nur dessen Größe in Bytes (BodySize) — der Body kann fachliche Kunden-/Geschäftsdaten des jeweiligen Jobs enthalten, die Kopf-/Fehlerdaten (JobName, ErrorMessage, Stacktrace, Zustellversuche, …) reichen für die Diagnose. Die Tool-Beschreibungen selbst sind auf Englisch verfasst, da sie vom aufrufenden KI-Modell gelesen werden, nicht von einem Menschen. Details, Einrichtung (inkl. GitHub Copilot/VS Code und Microsoft 365 Copilot Studio), die vollständige Tool-Liste mit Lese-Umfang je Tool sowie die Funktionsweise: MCP-Server.

  • System-assigned Managed Identity für die Function App: Die Azure Function App erhält beim Deployment automatisch eine system-zugewiesene Managed Identity. Ihre Objekt-ID (principalId) steht als Deployment-Output (functionPrincipalId) für nachgelagerte Rollenzuweisungen zur Verfügung. Die Identity wird immer angelegt.

  • Optional (Preview): Storage & Service Bus über Managed Identity: Über den neuen Schalter enableManagedIdentity (Standard: aus) lassen sich die Verbindungen zu Storage und Service Bus von Schlüsseln auf identitätsbasierten Zugriff umstellen. Ist der Schalter aktiv, weist das Bicep-Deployment der Identity die nötigen Rollen zu (Storage Table/Blob/Queue, Service Bus Sender/Receiver/Data Owner — Data Owner wird für die Queue-Status-Abfrage des MCP-Tools list_queue_statuses benötigt) und die App-Settings verwenden AzureWebJobsStorage__accountName bzw. …__fullyQualifiedNamespace statt der Schlüssel-Verbindungszeichenfolgen.

    Standardmäßig bleibt alles beim Schlüssel-basierten Zugriff — ein reines ZIP-/Image-Update ändert daran nichts. Die Umstellung erfolgt nur, wenn das Bicep-Script mit aktiviertem Schalter neu deployt wird (siehe Installationshinweise).

  • Leere Quell-Schlüssel gezielt ignorieren (IgnoreEmptySourceKey, D365CE): Ist das Quellfeld eines Ziel-Schlüssels leer, brach die Verarbeitung der Nachricht bisher immer mit einem Fehler ab — auch dann, wenn ein weiterer Schlüssel den Datensatz noch hätte finden können. Mit dem neuen Feld IgnoreEmptySourceKey am TargetKey (Standard: aus) lässt sich das je Schlüssel abschalten: Der betroffene Schlüssel wird übersprungen und die Suche läuft mit dem nächsten Schlüssel nach KeyRank weiter.

    Das ist vor allem im Multikey-Szenario relevant, wenn ein optionales Feld wie eine externe ID nicht in jedem Quelldatensatz gefüllt ist, ein nachrangiger Schlüssel den Zieldatensatz aber sicher findet.

    Findet kein Schlüssel einen Datensatz und waren alle leer gebliebenen Schlüssel entsprechend markiert, wird ein neuer Datensatz angelegt statt ein Fehler gemeldet. War auch nur einer der leeren Schlüssel nicht markiert, bleibt es beim Fehler. Details siehe IgnoreEmptySourceKey.

    Bestehende Mappings sind nicht betroffen — ohne gesetztes Feld bleibt das bisherige Verhalten unverändert.

Fehlerbehebungen & Verbesserungen

Allgemein

  • sample.local.settings.json war kein gültiges JSON: Eine überzählige schließende geschweifte Klammer am Dateiende hätte beim Kopieren als Vorlage für local.settings.json zu einem sofortigen JSON-Parse-Fehler geführt. Behoben.

Das in v2.1 eingeführte Grafana-Dashboard wurde zweimal überarbeitet, zunächst:

  • Direkt aus der Azure Function erreichbar: Das Dashboard ist jetzt zusätzlich direkt von der Function App aus verlinkt und nicht mehr nur über die Ressourcengruppe auffindbar.
  • Erfolgsraten berücksichtigen den Zeitraum: Die vier Erfolgsraten-Anzeigen (GetChanges sowie die Verarbeitung nach Priorität) werten jetzt den im Dashboard gewählten Zeitraum aus, statt einen festen Standardzeitraum zu verwenden.
  • Neue Auswertung „GetChanges per Jobs": Eine zusätzliche Zeitreihe zeigt die erkannten Änderungen je Job im Zeitverlauf.

und in einem zweiten Durchgang deutlich erweitert:

  • Zwei neue Custom Metrics: MessagesProcessed (Dimensionen JobName, Outcome mit den Werten Success/Failed/Transitive) und ProcessingDurationMs (Dimension JobName) werden jetzt beim Verarbeiten jeder Queue-Nachricht getrackt — queue-übergreifend, da alle drei Prioritäts-Queues durch denselben Verarbeitungscode laufen. Details siehe Metriken.
  • Service-Bus-Anbindung: Zwei neue Kopfzeilen-Kacheln (Active Messages, Deadletter) zeigen die aktuell wartenden bzw. dead-letterten Nachrichten, summiert über alle drei Export-Queues — direkt aus den Azure-Monitor-Platform-Metriken des Service-Bus-Namespace, ohne eigene KQL-Abfrage.
  • Kombinierte Ansicht statt drei getrennter Queue-Zeilen: Ausführungen, Laufzeiten und Job-Aufschlüsselung der Queue-Verarbeitung (ProcessLowPriorityQueueItems/ProcessStandardQueueItems/ProcessHighPriorityQueueItems) laufen jetzt in einer gemeinsamen Zeile „ProcessQueueItems" statt in getrennten, queue-spezifischen Panels — inklusive einer neuen kombinierten Erfolgsraten-Kachel in der Kopfzeile.
  • Erfolg und Fehler je Job für die Queue-Verarbeitung: Zwei neue Zeitreihen-Panels („ProcessItems Success per Job", „ProcessItems Errors per Job") zeigen erfolgreiche bzw. fehlgeschlagene bzw. in eine Retry-Queue umgeleitete (Transitive) Nachrichten aufgeschlüsselt nach Job.
  • Zwei neue Dashboard-Filter: Die Variable Bin Size (5 Minuten bis 1 Tag, Standard 10 Minuten) steuert die Gruppierung aller Zeitreihen-Panels; die Variable Job Namen (Mehrfachauswahl, live aus den vorhandenen Jobs ermittelt) schränkt alle job-bezogenen Panels auf die gewählten Jobs ein. Beide gelten nicht für die Kopfzeilen-Kacheln und die Exceptions-Heatmap (siehe Grafana-Dashboard für die genaue Wirkung je Panel).
  • Fehler-Überblick als Heatmap: Das Exceptions-Panel wurde von einem Balkendiagramm auf eine Heatmap umgestellt.

Vollständig neu dokumentiert, inklusive der bekannten Grenzen (z. B. keine job-genaue Erfolgsquote für GetChanges, keine Job-Filterung auf Exceptions): siehe Grafana-Dashboard.

Das aktualisierte Dashboard wird beim nächsten Bicep-Deployment automatisch übernommen — es sind keine manuellen Schritte nötig. Neu ist dafür ein zusätzlicher Bicep-Parameter, der den Namen des Service-Bus-Namespace an das Dashboard-Modul durchreicht (serviceBusNamespaceName in modules/dashboard.bicep) — bei Deployment über die vorhandene main.bicep ist das bereits verdrahtet und erfordert keine Anpassung durch Administratoren.

D365CE (Dataverse)

  • Ausweichen auf den nächsten Schlüssel funktioniert bei leeren Schlüsseln: War bei mehreren Ziel-Schlüsseln der erste in der Quelle leer, lief die anschliessende Suche im Zielsystem auf einen unbehandelten Fehler — die nachrangigen Schlüssel kamen gar nicht mehr zum Zug, obwohl sie den Datensatz gefunden hätten. Die Schlüsselsuche behandelt diesen Fall jetzt sauber und arbeitet die Schlüssel wie vorgesehen der Reihe nach ab.

SQL-Konnektoren (MSSQL, PostgreSQL, MySQL, Oracle)

  • Oracle- und MySQL-Konnektor: Paging funktionierte nicht: Beide Konnektoren erzeugten für jede Seite ab der zweiten eine ungültige SQL-Abfrage und brachen mit einem Datenbankfehler ab. Betroffen waren ausschließlich Tabellen, die mehr Datensätze als die konfigurierte maxPageSize je Lauf lieferten. Beide Konnektoren erzeugen jetzt dialektkorrektes Paging (Oracle: OFFSET…FETCH NEXT, MySQL: LIMIT…OFFSET).

  • PostgreSQL-Konnektor: Job-Filter (und damit die Watermark) wurde beim Lesen ignoriert: Der Konnektor las unabhängig vom im Job hinterlegten Filter bei jedem Lauf die komplette Tabelle neu, statt nur die seit der letzten Watermark geänderten Datensätze. Der Filter wird jetzt wie bei den übrigen SQL-Konnektoren als WHERE-Klausel übernommen.

  • Abbruch bei Watermark-Spalten, die kein Datum sind: War die als WatermarkField konfigurierte Spalte kein DateTime-Typ (z. B. eine fortlaufende ID oder ein rowversion-Wert), brach der Lesevorgang beim ersten Datensatz ab. Der Wert wird jetzt auch bei anderen Spaltentypen sicher verarbeitet; als Watermark-Spalte empfohlen bleibt weiterhin ein echtes Datum, da davon auch die Sortierung der Änderungen abhängt.

Installationshinweise

Dieser Abschnitt richtet sich an Administratoren, die ein bestehendes System aktualisieren.

Ein reines ZIP-/Image-Update genügt — es sind keine manuellen Konfigurationsschritte erforderlich. Die Managed Identity wird beim nächsten Bicep-Deployment automatisch angelegt.

Der neue OpenAPI-Connector, Action-Jobs, die job-eigenen Source/Target-Felder sowie EmbedChildren sind rein additiv: Bestehende Connections, Jobs und Mappings sind nicht betroffen und laufen ohne Anpassung unverändert weiter. Es sind keine neuen App-Settings und keine Bicep-Änderungen erforderlich, um die neuen Funktionen bereitzustellen — sie werden erst durch entsprechende Einträge in den Konfigurationstabellen (connections, jobs, mappings) aktiv genutzt.

Das neue Mapping-Feld IgnoreEmptySourceKey ist optional: Ohne Anpassung der Mappings bleibt das Verhalten wie bisher. Soll es genutzt werden, wird es im TargetKey-JSON der betroffenen Mapping-Zeile ergänzt ("IgnoreEmptySourceKey": true) — ein Neustart der Function App ist dafür nicht nötig, die Konfiguration wird aus dem Table Storage gelesen.

Neue Azure-Ressourcen (Bicep)

  • System-assigned Managed Identity (Microsoft.Web/sites → identity): Wird beim Deployment der Function App aktiviert (identity.type = 'SystemAssigned'). Ihre Objekt-ID wird als Deployment-Output functionPrincipalId (aus dem Modul-Output principalId) ausgegeben und dient als Grundlage für nachgelagerte Rollenzuweisungen. Es entstehen keine zusätzlichen Kosten.

Managed Identity für Storage & Service Bus aktivieren (Preview)

Optional. Nur relevant, wenn Storage und Service Bus ohne Schlüssel betrieben werden sollen.

  • Schalter enableManagedIdentity (im Marketplace-Assistenten unter Product Settings → Preview, bzw. Bicep-Parameter) auf Ja/true setzen und das Bicep-Script neu deployen.
  • Owner oder User Access Administrator auf der Ressourcengruppe ist dafür erforderlich, da das Deployment Rollenzuweisungen anlegt.
  • Beim Deployment mit aktiviertem Schalter muss die Function-Version zur Bicep-Version passen (die neue Function-Version unterstützt beide Zugriffsarten; ältere Versionen erwarten die Schlüssel-Verbindungszeichenfolgen).
  • Der WEBSITE_CONTENTAZUREFILECONNECTIONSTRING-Content-Share bleibt weiterhin schlüsselbasiert (Azure Files unterstützt hier keine Identität).
  • Kurz nach der Aktivierung können durch die RBAC-Propagation vereinzelt transiente Authentifizierungsfehler auftreten; diese fangen sich beim nächsten GetChanges-Lauf automatisch.

Container-Image

Falls die DataBridge containerisiert betrieben wird, ist das Image auf den neuen Tag zu aktualisieren:

acrwysatest.azurecr.io/containered-connector:v2.2.0

Die .NET-Basis bleibt unverändert (.NET 10, Isolated Worker) — es handelt sich lediglich um einen neuen Versions-Tag, kein Wechsel des Basis-Images.

7.3 - Release v2.1

Statische Schlüsselwerte, mehrere Ziel-Schlüssel, Sampling von Abhängigkeiten und ein mitgeliefertes Grafana-Dashboard.

Was ist neu

Neue Funktionen

  • Statische Schlüsselwerte für Lookups: Ein Lookup kann jetzt gegen einen fest hinterlegten Wert aufgelöst werden, ohne dass dafür ein Quellfeld existieren muss. Dazu wird im Schlüssel (Keyvalue) statt eines attributeName ein StaticValue angegeben. Nützlich, wenn ein Lookup immer auf denselben Zieldatensatz zeigen soll (z.B. eine feste Kategorie oder ein Standard-Datensatz).

  • Mehrere Ziel-Schlüssel je Mapping mit Rangfolge: Ein Table-Mapping bzw. ein Lookup kann nun mehrere Ziel-Schlüssel (TargetKeys) besitzen. Die Schlüssel werden anhand des neuen Felds KeyRank in aufsteigender Reihenfolge geprüft; sobald ein Schlüssel im Zielsystem einen Datensatz findet, wird dieser verwendet und die weiteren Schlüssel werden übersprungen. Damit lassen sich unterschiedliche Schlüssel gegen das CRM zulassen (z.B. zuerst eine externe ID, danach ersatzweise eine Nummer). Bestehende Mappings mit einem einzelnen TargetKey funktionieren unverändert weiter.

  • Automatische Anlage bei Lookups abschaltbar: Über die neue Option DoNotCreate am Lookup lässt sich steuern, dass ein nicht gefundener Ziel-Datensatz nicht automatisch angelegt wird. Ist die Option gesetzt, wird der Lookup nur aufgelöst, wenn der Datensatz bereits existiert — andernfalls bleibt das Feld leer, statt einen neuen Datensatz zu erzeugen.

  • String-zu-Decimal-Konvertierung: Der Decimal-Resolver akzeptiert jetzt auch Quellwerte vom Typ string und wandelt sie in einen Dezimalwert um (Interpretation im Format en-US, d.h. Punkt als Dezimaltrennzeichen). Leere Strings werden zu null, nicht konvertierbare Werte werfen wie bisher einen Fehler.

  • Vorab-Zwischenspeicherung von Lookups (Pre-Caching): Beim Verarbeiten eines Batches werden benötigte Lookup-Werte vorab gebündelt aufgelöst und zwischengespeichert. Dadurch werden wiederholte Einzelabfragen gegen das Zielsystem vermieden, was die Verarbeitung großer Änderungsmengen beschleunigt.

  • Sampling von Abhängigkeiten (Dependencies): Ausgehende Aufrufe an Quell- und Zielsysteme (D365 OData, SQL, Table Storage, Service Bus) werden von Application Insights automatisch als Abhängigkeiten erfasst und verursachten bisher den Großteil des Datenvolumens — bei minutengenauem Polling ein Vielfaches aller Log-Einträge zusammen. Diese Aufrufe werden nun gefiltert: Fehlgeschlagene sowie langsame Aufrufe werden immer vollständig gesendet, erfolgreiche und schnelle nur zu einem konfigurierbaren Anteil (Standard: 10 %). Das reduziert das tägliche Datenvolumen erheblich; Exceptions, Requests und alle Log-Einträge eines Fehler-Laufs bleiben unverändert vollständig erhalten. Steuerbar über die neuen Einstellungen TELEMETRY_DEPENDENCY_SAMPLE_RATE und TELEMETRY_DEPENDENCY_SLOW_MS; Details unter Betrieb → Logging.

Die Entscheidung fällt pro Aufruf in dem Moment, in dem der Aufruf endet — nicht pro Funktionsausführung wie beim Logging. Ein fehlgeschlagener Lauf enthält daher zwar garantiert den fehlgeschlagenen Aufruf, nicht aber zwangsläufig die erfolgreichen Aufrufe davor. Wird dieser Verlauf für eine Analyse benötigt, ist vorübergehend TELEMETRY_CONTROL=TRACE_ALL zu setzen.

  • Grafana-Dashboard für Application Insights: Jede Installation legt jetzt automatisch ein vorkonfiguriertes Dashboard unter „Dashboards with Grafana" im Application-Insights-Blade an — mit Erfolgsraten je Funktion (GetChanges, Verarbeitung nach Priorität), Ausführungszahlen, Laufzeiten je Job sowie einer Übersicht der Exceptions nach Zeit und Typ. Das Dashboard wird ohne separate Aktivierung mit ausgeliefert.

  • Weitere Einstellungen bereits beim Deployment konfigurierbar: Der Cron-Ausdruck des GetChanges-Timers sowie alle vier Telemetrie-Sampling-Einstellungen (TELEMETRY_DEPENDENCY_SAMPLE_RATE, TELEMETRY_DEPENDENCY_SLOW_MS, TELEMETRY_INFO_SAMPLE_RATE, TELEMETRY_CONTROL) lassen sich jetzt direkt als Bicep-Parameter bzw. im Marketplace-Installationsassistenten setzen, statt sie nachträglich manuell in den App-Settings der Function App zu pflegen. Im Marketplace-Assistenten sind sie unter Product Settings in die Bereiche Function Settings, Telemetry, Alert und Preview gruppiert.

Fehlerbehebungen & Verbesserungen

Allgemein

  • Watermark wurde bei abweichend geschriebenem WatermarkType gelöscht: Der Typvergleich war zeichengenau. Ein in der Tabelle jobs von Hand eingetragenes DateTime statt datetime (oder ein versehentliches Leerzeichen) führte dazu, dass die gespeicherte Watermark des Jobs geleert wurde. Da die DataBridge bei leerer Watermark den Filter-Block vollständig entfernt, las der betroffene Job anschließend das komplette Quellsystem — und zwar bei jedem Lauf erneut, ohne Fehlermeldung oder Warnung. Der Typ wird nun ohne Beachtung von Groß-/Kleinschreibung und ohne umgebende Leerzeichen erkannt; ein tatsächlich unbekannter Typ lässt die gespeicherte Watermark unangetastet und erzeugt eine Warnung im Log. Siehe Installationshinweise — betroffene Jobs sollten nach dem Update geprüft werden.

  • Deutlich weniger Zugriffe auf die Watermark-Tabelle: Die Prüfung, welche Jobs laut Zeitplan fällig sind, holte für jeden aktiven Job eine eigene Abfrage aus der Tabelle watermark, obwohl dafür nur ein Zeitstempel benötigt wird. Diese Einzelabfragen sind durch eine gebündelte Abfrage ersetzt. Bei rund 220 aktiven Jobs sinkt die Zahl der Lesezugriffe von ~7.400 auf ~810 pro Stunde. Die Anzahl der Schreibzugriffe bleibt unverändert. Das entlastet Azure Table Storage und reduziert zusätzlich das Datenvolumen in Application Insights.

  • Watermark-Vergleich robuster: Drei Detailprobleme in der Watermark-Auswertung wurden behoben. Zeitstempel werden nun zeitzonenunabhängig verglichen — bisher wurde die Zeitangabe in die lokale Zeit des Servers umgerechnet, was beim Typ date über eine Tagesgrenze springen und das Vergleichsergebnis je nach Betriebsstandort verändern konnte. Fehlt ein Änderungsmarker, bleibt die Watermark unverändert stehen statt einen Abbruch auszulösen. Und eine fehlende Watermark-Spalte in der Tabelle führt nicht mehr zu einem leeren Rückschreibewert.

  • Validate-Funktion auf async umgestellt: Die Diagnose-Funktion Validate verwendete synchrone I/O (WriteString) sowie ein blockierendes .Wait()/.Result beim Warten auf die Initialisierung — beides ist mit dem ASP.NET Core-Modus nicht kompatibel. Sie ist jetzt vollständig auf async/await umgestellt.

  • Aussagekräftigeres Logging bei ExecuteHttpRequest: Bei ad-hoc HTTP-Anfragen werden jetzt der HTTP-Statuscode und der Antwortinhalt getrennt protokolliert, was die Fehlersuche bei fehlgeschlagenen Requests erleichtert.

  • Erweiterte Auswertungs-Abfragen für Abhängigkeiten und Kosten: Die Dokumentation enthält neue KQL-Abfragen für die langsamsten Abhängigkeiten, fehlgeschlagene Aufrufe nach Zielsystem sowie eine Volumenanalyse pro Tabelle zur Ursachensuche bei erreichter Daten-Obergrenze (Daily Cap). Siehe Betrieb → Logging.

BC & NAV

  • Statische Schlüsselwerte im BcConnector-Filter: Die Schlüssel- und Filtererzeugung des Business-Central-Connectors berücksichtigt nun ebenfalls statische Schlüsselwerte (StaticValue), sodass die neuen Lookup-Funktionen auch gegen D365 BC funktionieren. Zusätzlich wurde ein Fehler behoben, durch den Schlüsselwerte vom Typ string (z.B. Nummernfelder) ohne umschließende Anführungszeichen in den OData-Filter geschrieben wurden — ein mehrteiliger Schlüssel mit einem String-Anteil führte dadurch zu einer ungültigen Filterabfrage gegen BC. Der Filter wird nun für string-Werte korrekt in Anführungszeichen gesetzt.

  • Korrekte Fehlermeldung im NavConnector: Eine Fehlermeldung im NAV-Connector verwies fälschlich auf „BC-Communication" — sie lautet jetzt korrekt „NAV-Communication".

Installationshinweise

Ein Update auf v2.1 erfordert keine zwingenden Konfigurationsänderungen. Die neuen Mapping-Funktionen (statische Schlüsselwerte, mehrere Ziel-Schlüssel, DoNotCreate) werden ausschließlich über die Mapping-Konfiguration in Azure Table Storage aktiviert und sind für bestehende Mappings optional — ohne Konfigurationsänderung bleibt das bisherige Verhalten erhalten.

Empfohlen wird jedoch eine einmalige Sichtprüfung der Tabelle jobs (siehe unten) — sie deckt eine Fehlkonfiguration auf, die bisher unbemerkt zu Vollabzügen geführt haben kann.

Neue / geänderte App-Settings (Azure Function)

Beide Einstellungen sind optional. Ohne Konfiguration gelten die angegebenen Standardwerte, das Sampling von Abhängigkeiten ist also ab dem Update automatisch aktiv.

EinstellungTypStandardBeschreibungPflicht
TELEMETRY_DEPENDENCY_SAMPLE_RATEInteger (0–100)10Prozentualer Anteil der erfolgreichen und schnellen Abhängigkeits-Aufrufe, die an App Insights gesendet werden. Fehlgeschlagene und langsame Aufrufe sind davon nicht betroffen.Nein
TELEMETRY_DEPENDENCY_SLOW_MSInteger (ms)2000Schwellwert, ab dem ein Aufruf als „langsam" gilt und immer gesendet wird — auch bei Erfolg.Nein

Die bestehende Einstellung TELEMETRY_CONTROL wirkt nun zusätzlich auf Abhängigkeiten: TRACE_ALL deaktiviert auch deren Sampling, TRACE_NONE unterdrückt sie vollständig.

Diese beiden sowie die bereits bestehenden Einstellungen TELEMETRY_INFO_SAMPLE_RATE, TELEMETRY_CONTROL und GetChangesTimerExpression lassen sich ab dieser Version zusätzlich direkt beim Deployment setzen — als Bicep-Parameter (Beispielwerte in bicep/params/databridge.sample.bicepparam) oder im Marketplace-Assistenten unter Product Settings. Bei einem Update per Marketplace-Assistent führt ein erneuter Durchlauf zu keiner Änderung, solange die Standardwerte übernommen werden.

Neue Azure-Ressourcen (Bicep)

  • Grafana-Dashboard (Microsoft.Dashboard/dashboards + dashboardDefinitions): Vorkonfiguriertes Dashboard in Application Insights („Dashboards with Grafana"). Wird bei jedem Deployment fest mit angelegt, ohne Opt-out-Möglichkeit. Verursacht keine zusätzlichen Kosten; Abfragen laufen gegen die bestehende Application-Insights-Instanz.

Zu prüfen nach dem Update: WatermarkType in der Tabelle jobs

Die Tabelle jobs ist einmalig daraufhin durchzusehen, ob im Feld WatermarkType ein abweichend geschriebener Wert steht — erwartet werden datetime oder string. Abweichungen wie DateTime, Datetime oder ein führendes Leerzeichen haben bis einschließlich der Vorversion dazu geführt, dass der betroffene Job seine Watermark verlor und bei jedem Lauf das komplette Quellsystem las.

Nach dem Update ist der Fall auch ohne Prüfung erkennbar: Ein nicht erkannter Typ erzeugt eine Warnung mit Jobnamen und dem hinterlegten Rohwert im Log.

Was bei einem betroffenen Job zu erwarten ist: Ist die Watermark bereits geleert, führt der erste Lauf nach dem Update noch einmal einen Vollabzug durch. Danach wird die Watermark korrekt fortgeschrieben und der Job arbeitet wieder inkrementell. Es ist also kein manueller Eingriff nötig — lediglich einmalig mit erhöhter Last für diesen Job zu rechnen.

Hinweis zu bestehenden Auswertungen

Verworfene Abhängigkeiten werden vollständig entfernt und nicht über einen Hochrechnungsfaktor (itemCount) kompensiert. Bestehende Dashboards, Alerts oder KQL-Abfragen, die auf absoluten Zählungen oder Durchschnittslaufzeiten der Tabelle dependencies / AppDependencies basieren, liefern nach dem Update zu niedrige Werte und sind zu prüfen.

Besonders zu beachten sind Fehlerquoten über die Tabelle dependencies: Da fehlgeschlagene Aufrufe vollständig erhalten bleiben, erfolgreiche aber gesampelt werden, ist ein Ausdruck der Form countif(success == false) / count() nach dem Update deutlich überhöht. Solche Abfragen und darauf basierende Alerts sind zu entfernen oder auf die requests-Tabelle umzustellen, die keinem Sampling unterliegt.

Nicht betroffen sind:

  • Fehleranzahlen und Fehlerursachen — fehlgeschlagene Aufrufe werden nie verworfen
  • Auswertungen langsamer Aufrufe — diese werden ab dem Schwellwert immer gesendet
  • Requests, Exceptions, Traces und Custom Metrics — unverändert

Für belastbare Mengenangaben sind die vorab aggregierten Custom Metrics zu verwenden, die keinem Sampling unterliegen.

Container-Image

Falls die DataBridge containerisiert betrieben wird, ist das Image auf den neuen Tag zu aktualisieren:

acrwysatest.azurecr.io/containered-connector:v2.1.0

Die .NET-Basis bleibt unverändert (.NET 10, Isolated Worker) — es handelt sich lediglich um einen neuen Versions-Tag, kein Wechsel des Basis-Images.