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
| Spalte | Wert | Beschreibung |
|---|
| PartitionKey | connection | Muss immer connection lauten |
| RowKey | frei wählbar | Eindeutiger Name dieser Verbindung. Wird in der jobs-Tabelle als Referenz verwendet. |
| Timestamp | automatisch | Systemseitig gesetzt, nicht bearbeitbar |
| Type | Connector-Typ | Gibt an, um welches System es sich handelt (siehe Tabelle unten) |
| Json | JSON-Objekt | System-spezifische Verbindungsparameter (unterscheidet sich je nach Type) |
PartitionKey ist nicht frei wählbar
Die DataBridge liest die Tabelle ausschließlich mit dem Filter
PartitionKey eq 'connection'. Zeilen mit einem abweichenden PartitionKey werden ohne
Fehlermeldung und ohne Warnung ignoriert — die Verbindung existiert dann für die
DataBridge nicht, und jeder Job, der sie referenziert, wird als unvollständig verworfen.
Unterstützte Connector-Typen
| Type-Wert | System | Lesen | Schreiben |
|---|
D365BC | Dynamics 365 Business Central | ✅ | ✅ |
D365CE | Dynamics 365 / Dataverse | ✅ | ✅ |
D365NAV | Microsoft Dynamics NAV | ✅ | ✅ |
MSSQL | Microsoft SQL Server | ⚠️ Preview | — |
MYSQL | MySQL / MariaDB | ⚠️ Preview | — |
ORACLE | Oracle Database | ⚠️ Preview | — |
PGSQL | PostgreSQL | ⚠️ Preview | — |
OPENAPI | Generische REST-API über OpenAPI-Spezifikation | ✅ | ✅ (inkl. Löschen) |
SQL-Konnektoren sind Preview und nur lesend
Die vier SQL-Konnektoren sind als interne Vorabversion eingestuft. Schreiben wird
nicht unterstützt — ein SQL-System als Ziel führt zur Laufzeit zu einem Fehler.
Auch beim Lesen bestehen Einschränkungen: Nur der MSSQL-Konnektor wertet den Filter des
Jobs aus. Details auf den jeweiligen Konnektor-Seiten.
Universelle JSON-Parameter
Diese Parameter sind für alle Connector-Typen gültig und werden in das Json-Feld
eingetragen. Alle sind optional:
| Parameter | Standardwert | Beschreibung |
|---|
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. |
maxPageSize | 5000 | Maximale Anzahl Datensätze pro Abruf-Seite. Kann bei Performance-Problemen reduziert werden. |
language | de-DE | Kultur für die Interpretation von Zahlen- und Datumsformaten. |
httpClientTimeoutSeconds | 100 | Zeitlimit 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:
| Parameter | Standardwert | Beschreibung |
|---|
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. |
Auswahl der Authentifizierungsmethode
Sind apiKeyHeader und apiKeyValue gesetzt, hat API-Key-Authentifizierung Vorrang vor
Basic Auth und OAuth2. Andernfalls gilt weiterhin: userName + password ⇒ Basic Auth,
sonst clientId + clientSecret + tenantId ⇒ OAuth2 Client Credentials.
Ist authmethod gesetzt, gilt ausschließlich dieser Wert.
Für Managed Identity gilt:
"authmethod": "ManagedIdentity" ohne clientId verwendet die system-zugewiesene Identity der Function App.- Mit
clientId wird die benutzer-zugewiesene Identity mit dieser Client-ID verwendet. clientSecret und tenantId werden nicht benötigt. Der Token wird mit dem scope der Connection angefordert (BC-Standard: https://api.businesscentral.dynamics.com/.default).- Die Identity muss im Zielsystem berechtigt sein (bei BC als Microsoft-Entra-Anwendung in Business Central mit Berechtigungssätzen), siehe D365BC.
Hinweis: Beim D365NAV-Konnektor existiert zusätzlich authenticationMethod (nur NTLM). Das ist ein anderer Parameter als authmethod.
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:
| Parameter | Standardwert | Beschreibung |
|---|
scope | https://api.businesscentral.dynamics.com/.default | OAuth2-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"
}
`url` ist immer erforderlich — auch bei NTLM
baseAddress ersetzt url nicht. Sämtliche Anfrage-Adressen werden aus url
gebildet; fehlt der Parameter, bricht der Konnektor beim ersten Aufruf ab.
baseAddress wird ausschließlich bei authenticationMethod = NTLM für den
Credential-Cache verwendet und ist ansonsten entbehrlich.
Ein fehlender Schrägstrich am Ende von url wird automatisch ergänzt.
Zusätzliche NAV-Parameter:
| Parameter | Standardwert | Beschreibung |
|---|
scope | https://api.businesscentral.dynamics.com/.default | OAuth2-Scope beim Token-Abruf |
baseAddress | (leer) | Basis-Adresse für den NTLM-Credential-Cache. Nur bei authenticationMethod = NTLM relevant. |
authenticationMethod | Basic | Siehe Hinweis unten |
`authenticationMethod` wirkt nur beim Wert `NTLM`
Nur NTLM (Groß-/Kleinschreibung egal) schaltet auf NTLM-Authentifizierung um — und
auch das nur, wenn zusätzlich userName und password gesetzt sind.
Bei jedem anderen Wert — auch beim Vorgabewert Basic — entscheidet die DataBridge
automatisch anhand der vorhandenen Felder: userName + password ergibt Basic Auth,
sonst clientId + clientSecret + tenantId für OAuth2.
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:
| Parameter | Standardwert | Beschreibung |
|---|
commandTimeOut | 30 | Zeitlimit 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:
| Parameter | Beschreibung |
|---|
logCreateRequests | Protokolliert die vollständigen Anfragen beim Anlegen von Datensätzen |
logUpdateRequests | Protokolliert die vollständigen Anfragen beim Aktualisieren von Datensätzen |
logReceivedData | Protokolliert die vollständigen Antwortdaten des Systems |
Nicht dauerhaft aktiviert lassen
Diese Schalter erzeugen erhebliche Datenmengen in Application Insights und können
Geschäftsdaten im Klartext protokollieren. Nur gezielt für die Dauer einer Analyse
aktivieren und danach wieder zurücksetzen.
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.
Key Vault wird nicht mitdeployt
Das Bicep-Deployment legt keinen Key Vault an. Die System Managed Identity der
Function App wird seit v2.2 hingegen automatisch angelegt. Vor der Nutzung des Key Vault
ist daher manuell einzurichten:
- Key Vault anlegen und das Secret hinterlegen
- Der (bereits vorhandenen) System Managed Identity die Berechtigung Get auf die
Key Vault Secrets erteilen
Ohne diese Schritte schlägt die Auflösung der Referenz beim Verbindungsaufbau fehl.
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.
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
| Spalte | Pflicht | Beschreibung |
|---|
| 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. |
PartitionKey ist nicht frei wählbar
Die DataBridge liest die Tabelle ausschließlich mit dem Filter PartitionKey eq 'job'.
Zeilen mit einem abweichenden PartitionKey werden ohne Fehlermeldung und ohne Warnung
ignoriert — der Job existiert dann für die DataBridge schlicht nicht.
Dasselbe gilt für die connections-Tabelle (dort connection). Nur bei mappings ist
der PartitionKey frei wählbar — er dient dort der Gruppierung.
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.
Die Position im Filter ist egal — die Klammerung entscheidet
Es kommt nicht darauf an, dass der Watermark-Vergleich zuerst steht. Ausgewertet wird
allein die {{ }}-Klammerung: Jeder Block, der %watermark% enthält, wird bei leerer
Watermark ersatzlos entfernt. Mehrere Blöcke sind erlaubt.
Daraus folgt eine Falle beim Kombinieren von Bedingungen:
{{lastModifiedDateTime gt %watermark%}} and {{blocked eq false}}
Bei leerer Watermark entsteht daraus " and blocked eq false" — ein ungültiger
Filterausdruck, der die Abfrage gegen das Quellsystem scheitern lässt.
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%}}
| Watermark | Ergebnis |
|---|
| gesetzt | blocked eq false and lastModifiedDateTime gt 2026-08-01T10:00:00.000Z |
| leer | blocked 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 } } ] }
| Operation | Parameter | Wirkung |
|---|
Equals | SourceField, Value | Feldwert entspricht dem angegebenen Wert |
Boolean | SourceField, Value | Boolesches Feld entspricht dem angegebenen Wert |
OptionSetValue | SourceField, Value | Optionsset-Feld entspricht dem angegebenen Wert |
NotNull | SourceField | Feld ist gefüllt |
IsNull | SourceField | Feld ist leer |
StatusCode | Value | Datensatz 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:
| Schedule | Bedeutung |
|---|
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
| Wert | Service-Bus-Queue | Typischer Einsatz |
|---|
High | sbqhighexport | Zeitkritische Stammdaten (Preise, Kundenstatus) |
Standard | sbqexport | Normalbetrieb (Vorgabe, auch bei leerem oder unbekanntem Wert) |
Low | sbqlowexport | Groß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.
Weitere Queues sind rein intern
Der Service Bus enthält vier weitere Queues (sbqignored, sbqtransitivehigh,
sbqtransitivestandart, sbqtransitivelow). Sie sind Ziele der internen
Fehlerbehandlung — für übersprungene bzw. bei Konflikten im Zielsystem zurückgestellte
Nachrichten — und nicht als Wert für Priority gedacht. Details:
Architektur → Queue-Struktur.
Action — welche Änderungsarten gelesen werden
| Wert | Wirkung |
|---|
(leer) / NewUpdate | Nur Neuanlagen und Änderungen (Vorgabe, auch bei unbekanntem Wert) |
Delete | Nur Löschungen |
DeleteOnUpdate | Löschungen, die im Quellsystem als Änderung auftreten |
All | Neuanlagen, Änderungen und Löschungen in einem Job |
| ein beliebiger anderer Wert | Der Job wird als Aktions-Job (InvokeAction) behandelt — siehe unten |
CRUD-Werte wirken nur bei D365CE als Quellsystem
Die Werte Delete, DeleteOnUpdate und All werden ausschließlich vom Dataverse-Connector
ausgewertet. Steht als Quelle ein D365BC-, D365NAV- oder SQL-System, haben sie keine
Wirkung — der Job liest dort unabhängig vom gesetzten Wert immer Neuanlagen und
Änderungen.
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:
- Job A schreibt einen Datensatz von CE nach BC
- BC antwortet mit dem neu erzeugten Datensatz inkl. der neuen BC-ID
- Die DataBridge führt automatisch Job B (den ResponseJob) aus
- 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 - 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
| Spalte | Pflicht | Beschreibung |
|---|
| 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.
Unterschied zwischen PrimaryKey und Id
Bei Business Central und NAV entscheidet die Wahl über die Anführungszeichen im
erzeugten OData-Schlüssel: PrimaryKey setzt den Wert in Anführungszeichen, Id
schreibt ihn unquotiert. Die falsche Wahl führt zu einem Syntaxfehler in der Abfrage.
Dataverse kennt nur PrimaryKey. Ein dort eingetragenes Id fällt in den
Standardzweig, erzeugt lediglich eine Warnung im Log und wird übersprungen —
kein Fehler, aber der Schlüssel greift nicht.
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 }
]
}
| Feld | Bedeutung bei CustomKey |
|---|
KeyName | Zielfeld, das den zusammengesetzten Schlüsselwert enthält |
keyName | Zielfeld-Name für diesen Schlüsselteil |
attributeName | Quellfeld-Name, aus dem der Wert kommt |
StaticValue | Fester Wert statt eines Quellwertes (siehe unten) |
rank | Reihenfolge 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 }
]
}
Bei Dataverse ist die Bedeutung von keyName und attributeName vertauscht
Im D365CE-Connector haben die beiden Felder bei KeyType: Filter die umgekehrte
Bedeutung wie bei CustomKey:
| Feld | Bedeutung bei Filter (D365CE) |
|---|
keyName | Quellfeld — daraus wird der Suchwert gelesen |
attributeName | Zielfeld — dessen Metadaten bestimmen die Typumwandlung |
Bei Business Central gilt dagegen für beide Schlüsseltypen dieselbe Zuordnung
(keyName = Zielfeld).
Stiller Fallstrick bei Filter in Dataverse
Die Bedingung wird nur dann in die Abfrage aufgenommen, wenn im selben Mapping eine
Zeile existiert, deren SourceField dem keyName und deren TargetField dem
attributeName entspricht.
Fehlt diese Zeile, wird die Bedingung kommentarlos weggelassen — die Abfrage
liefert dann zu viele Datensätze, ohne dass ein Fehler auftritt.
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.
Besteht der Schlüssel nur aus statischen Werten
Dann wird die Mapping-Zeile auch dann verarbeitet, wenn das SourceField in der
Nachricht gar nicht vorkommt — die Zeile wird also nicht wie sonst übersprungen.
D365NAV unterstützt keine statischen Schlüsselwerte
Der NAV-Connector greift bei der Schlüssel- und Filtererzeugung unbedingt auf
attributeName zu. Ein dort gesetztes StaticValue bleibt wirkungslos und führt zu
einem Fehler, sobald das Quellfeld fehlt.
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:
| Ebene | Vorgehen |
|---|
| Table-Mapping | Je 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 }
]
}
KeyRank und rank nicht verwechseln
KeyRank bestimmt die Reihenfolge mehrerer Schlüssel zueinander.
rank innerhalb von KeyValues bestimmt die Reihenfolge der Felder innerhalb eines
Schlüssels.
Bestehende Mappings mit einem einzelnen TargetKey funktionieren unverändert — intern
wird ein einzelner Schlüssel wie eine Liste mit einem Eintrag behandelt.
Leere Schlüssel in der Quelle
Liefert die Quelle für einen Schlüssel keinen Wert, bricht die Verarbeitung der Nachricht
standardmäßig mit einem Fehler ab — auch dann, wenn ein weiterer Schlüssel den Datensatz
noch hätte finden können. Mit IgnoreEmptySourceKey lässt sich
das je Schlüssel abschalten.
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:
| Situation | Was passiert | Verhalten ohne dieses Feld |
|---|
| Schlüssel lässt sich nicht aufbauen — das Quellfeld ist leer, es gibt gar keinen Wert zum Suchen | Es wird nicht gesucht | Fehler, die Nachricht schlägt fehl |
| Schlüssel findet nichts — es wurde gesucht, im Zielsystem existiert der Datensatz noch nicht | Suche läuft, bleibt ergebnislos | Datensatz 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.
Warum ein leerer Schlüssel überhaupt ein Fehler ist
Bei einem leeren Schlüssel konnte nicht geprüft werden, ob der Zieldatensatz existiert.
Ihn trotzdem anzulegen, könnte ein Duplikat erzeugen. Deshalb bricht die DataBridge im
Zweifel ab — es sei denn, Sie erklären mit diesem Feld, dass der Schlüssel erwartbar
leer sein kann.
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:
| Schritt | Ergebnis |
|---|
| Schlüssel 1 | leer, wird übersprungen — gemerkt: „hier war ein Fehler" |
| Schlüssel 2 | leer, markiert — wird übersprungen |
| Schlüssel 3 | findet den Datensatz → Suche endet |
| Ergebnis | Update. 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.
Das Feld bewusst setzen
Für die betroffenen Fälle verschiebt sich das Verhalten von „Nachricht abweisen" zu
„Datensatz anlegen". Setzen Sie es nur dort, wo ein leerer Quellwert fachlich erwartbar
ist und ein weiterer Schlüssel den Datensatz zuverlässig findet.
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.
SourceOperations greifen ausschliesslich bei virtuellen Feldern
Eine SourceOperation wird nur ausgeführt, wenn das SourceField der Zeile mit _
beginnt. Steht dort ein normaler Feldname, wird die Operation stillschweigend ignoriert —
das gilt für LookupValue, ActivityParty, LinkToRecord und ReadFromMessage
gleichermaßen.
Umgekehrt wird ein _-präfixiertes Feld nicht in die Select-Liste der Quellabfrage
aufgenommen — stattdessen wird das unter Parameters.SourceField genannte Feld
abgefragt.
Der ResponseJob ist dabei nur einer von mehreren Anwendungsfällen:
| Anwendungsfall | Operation |
|---|
| Wert aus einer anderen Tabelle nachschlagen | LookupValue |
| Wert aus einer ActivityParty-Beziehung lesen | ActivityParty |
| Link auf einen Dataverse-Datensatz erzeugen | LinkToRecord |
| Wert aus der Antwortnachricht lesen (ResponseJob) | ReadFromMessage |
| Zeilen eines Kind-Jobs einbetten | EmbedChildren |
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.
Es wird nur die erste Operation ausgeführt
Obwohl Operations ein Array ist, wertet die DataBridge bei SourceOperations
ausschließlich den ersten Eintrag aus. Weitere Einträge werden verworfen — ohne
Fehler und ohne Warnung.
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
}
}
]
}
| Parameter | Beschreibung |
|---|
LookupType | Attribute — liest ein bestimmtes Feld aus dem gefundenen Datensatz. Name — liefert den Anzeigenamen des verknüpften Datensatzes; dann entfallen LookupTable und LookupField. |
SourceField | Feld im Quelldatensatz, das den Suchwert enthält |
LookupTable | API-Pfad der nachzuschlagenden Tabelle |
LookupField | Feld im nachgeschlagenen Datensatz, dessen Wert übertragen wird |
UseCache | true dringend empfohlen — siehe Hinweis |
LookupType Name nur in Dataverse
Der Wert Name ist ausschließlich im D365CE-Connector implementiert. Business Central
und NAV werfen dafür einen Nicht-implementiert-Fehler.
UseCache aktiviert das Pre-Caching
Mit UseCache: true löst der Dataverse-Connector die benötigten Lookup-Werte seit v2.1
pro Batch gebündelt vorab auf — in Blöcken von 1000 Werten — statt für jeden
Datensatz einzeln beim Zielsystem nachzufragen. Das beschleunigt große Änderungsmengen
erheblich.
Ohne UseCache entfällt das Pre-Caching vollständig. Vorausgesetzt wird zusätzlich ein
gesetztes Parameters.SourceField.
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:
| Spalte | Wert |
|---|
| SourceField | _id (Unterstrich-Präfix!) |
| TargetField | wysa_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"
}
}
}
]
}
| Parameter | Beschreibung |
|---|
TypeMask | Teilnehmertyp (siehe Hinweis) |
SourceField | Feld mit der Activity-ID |
TargetField | Feld im verknüpften Datensatz, das gelesen werden soll |
TargetTable | Entität des verknüpften Datensatzes |
Werden mehrere Teilnehmer gefunden, werden ihre Werte komma-separiert zusammengefügt.
TypeMask ist ein Dataverse-Plattformwert
Die DataBridge reicht den Wert unverändert an Dataverse durch. Die Bedeutung der
einzelnen Zahlen (1 = Sender, 2 = To-Empfänger, …) ist in der
Dataverse-Dokumentation von Microsoft
beschrieben und nicht Teil dieser Anwendung.
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:
| Spalte | Wert |
|---|
| SourceField | _lines (Unterstrich-Präfix, beliebiger Name) |
| TargetField | Pfad 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.
TargetOperations werden angewendet, bevor der Wert ins Zielsystem geschrieben wird.
TargetOperations werden über den Namen aufgelöst
Anders als bei SourceOperations dürfen mehrere Einträge im Array stehen. Sie werden
jedoch nicht der Reihe nach ausgeführt, sondern anhand ihres Namens gesucht — die
Reihenfolge im Array hat keine Wirkung.
Es gilt eine feste Rangfolge:
DefaultValue — überschreibt den Quellwert vollständigDefaultValueIfNullOrEmpty — greift, wenn das Ergebnis leer istDefaultValueMap — übersetzt den bis dahin ermittelten Wert zuletzt
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.
| Parameter | Beschreibung |
|---|
TargetTable | Entität des zu suchenden Datensatzes (Pflicht) |
TargetKey | Einzelner Schlüssel, Syntax wie beim TargetKey |
TargetKeys | Array mehrerer Schlüssel mit KeyRank (ab v2.1, siehe oben) |
DoNotCreate | true verhindert das automatische Anlegen (ab v2.1, siehe unten) |
SetNullIfEqualsValueOfField | Setzt das Lookup auf null, wenn der Wert dem angegebenen Feld der Nachricht entspricht — Schutz gegen Selbstreferenzen |
DefaultValue | Fester Wert; überspringt die Auflösung vollständig |
DoNotCreate — automatisches Anlegen abschalten
Standardmäßig legt die DataBridge einen nicht gefundenen Ziel-Datensatz automatisch an.
Mit "DoNotCreate": true unterbleibt das: Der Lookup wird nur aufgelöst, wenn der
Datensatz bereits existiert — andernfalls bleibt das Feld leer.
Die Option wirkt nur auf diesen Fallback. Die vorgelagerte Suche über mehrere
TargetKeys läuft unverändert.
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.
Sonderzeichen in Schlüsseln
In der Praxis liefert Business Central Sonderzeichen in Feldwerten kodiert: ein
Leerzeichen als _x0020_, ein Bindestrich als _x002D_. Diese kodierten Werte sind
dann 1:1 als Key einzutragen.
Die DataBridge vergleicht die Schlüssel lediglich als Zeichenketten — die Kodierung
stammt aus dem Quellsystem, nicht aus dieser Anwendung.
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).
Diese Asymmetrie ist beabsichtigt
Derselbe Zahlenwert wird also unterschiedlich interpretiert, je nachdem, ob er als
Zeichenkette oder als Zahl aus dem Quellsystem kommt. Bei Feldern, die als string
geliefert werden, ist der Punkt als Dezimaltrennzeichen zu verwenden — unabhängig von
der eingestellten language.
Spezialfälle
Feld ohne Quellwert (reiner Standardwert)
Soll ein Zielfeld immer mit einem festen Wert befüllt werden, ohne dass ein
Quellfeld existiert:
| Spalte | Wert |
|---|
| SourceField | (leer) |
| TargetField | wysa_datasource |
| TargetOperation | DefaultValue mit "DefaultValue": "DataBridge" |
Wert aus der Antwortnachricht lesen (ResponseJob)
| Spalte | Wert |
|---|
| SourceField | _id |
| TargetField | wysa_bcid |
| SourceOperation | ReadFromMessage |
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 |
|---|
| SourceOperation | Nur der erste Eintrag wird ausgeführt, alle weiteren verworfen |
| TargetOperation | Mehrere 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.
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
| Spalte | Wert | Beschreibung |
|---|
| PartitionKey | watermark | Fest vorgegeben — immer watermark |
| RowKey | Jobname | Entspricht dem RowKey des zugehörigen Jobs in der jobs-Tabelle |
| Timestamp | automatisch | Systemseitig gesetzt, nicht bearbeitbar |
| Watermark | Letzter Stand | Format abhängig vom WatermarkType des Jobs (siehe unten) |
| LastCronRun | Zeitstempel | Beginn 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
Empfehlung für Dataverse
Für alle D365CE-Jobs WatermarkType = string verwenden. Die Dataverse
Change Tracking API garantiert lückenlose Erkennung aller Änderungen —
auch wenn der Timer während einer Änderung ausgeführt wird.
Schreibweise und unbekannte Typen
Der Typ wird ohne Beachtung von Groß-/Kleinschreibung und ohne umgebende Leerzeichen
erkannt — datetime, DateTime und DATETIME sind gleichwertig.
Ein unbekannter Typ legt die Änderungserkennung still
Steht im Feld WatermarkType ein Wert, der weder date noch datetime noch string
ergibt (z. B. ein Tippfehler), gilt der Typ als unbekannt. Dann passiert Folgendes:
- Die gespeicherte Watermark bleibt unangetastet — sie wird nicht geleert
- Sie wird aber auch nie fortgeschrieben
- Im Log erscheint eine Warnung mit Jobnamen und dem hinterlegten Rohwert
Der Job liest damit bei jedem Lauf dieselbe Menge erneut. Die Warnung im Log ist der
einzige Hinweis darauf — es entsteht kein Fehler.
Bis einschließlich v2.0 war das Verhalten deutlich schlimmer: Der Vergleich war
zeichengenau, ein abweichend geschriebenes DateTime leerte die Watermark, und der
Job las bei jedem Lauf das komplette Quellsystem — ohne jede Meldung.
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:
- Beim Lesen führt die DataBridge den Änderungsmarker (
ChangeTracker) der verarbeiteten
Datensätze mit - 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 - Nach dem Leeren des letzten Batches wird der Endstand in die Tabelle geschrieben
LastCronRun wird auf den Zeitpunkt gesetzt, zu dem der Job begonnen hat
`LastCronRun` wird bei jedem Lauf geschrieben
Auch dann, wenn der Job keine Änderungen gefunden hat oder gar keinen eigenen Schedule
besitzt. Der Wert ist damit ein verlässlicher Indikator dafür, ob ein Job überhaupt
gelaufen ist.
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:
| Typ | Initialwert |
|---|
datetime | 0001-01-01T00:00:00.000Z |
date | 0001-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
Kein Schutz gegen gleichzeitige Änderungen
Die DataBridge schreibt Watermark-Einträge ohne ETag-Prüfung — es gilt
Last-Writer-Wins. Wird ein Wert von Hand geändert, während der zugehörige Job gerade
läuft, überschreibt der Job die manuelle Änderung am Ende seines Laufs kommentarlos.
Vor einem Eingriff daher die betroffene Funktion deaktivieren
(AzureWebJobs.GetChanges.Disabled = true) oder einen Zeitpunkt wählen, zu dem der Job
nachweislich nicht läuft.
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.