# 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.

---

Site [llms.txt](/llms.txt)

---

## 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](/docs/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](/docs/mcp-server/operationen/#sql-connectoren-datentypen).

- **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](/docs/konnektoren/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](/docs/konnektoren/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](/docs/konfiguration/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](/docs/einrichtung/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](/docs/konnektoren/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](/docs/konnektoren/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](/entwicklung/lokale-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](/entwicklung/lokale-testumgebung/#http-mocks-wiremock).
- **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](/entwicklung/mcp-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](/entwicklung/architektur/) veröffentlicht. Die Übersicht unter [Überblick → Architektur](/docs/ueberblick/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](/entwicklung/lokale-testumgebung/#service-bus) und [Entwicklung → Debuggen](/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](https://github.com/tombiddulph/Service-Bus-Emulator-Explorer), nicht von Microsoft. Der klassische *Service Bus Explorer* funktioniert mit dem Emulator nicht. Details: [Entwicklung → Debuggen → Gegen Podman](/entwicklung/debuggen/podman/#service-bus-einsehen).
- **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](/entwicklung/lokale-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](/entwicklung/lokale-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](/entwicklung/architektur/abhaengigkeiten/#nuget-pakete).

## 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.
