Wie aus einem OData-Dienst ein relationales ODBC-Modell wird
graph TD
M["$metadata (CSDL)"] -->|"beim ersten Gebrauch je Verbindung"| R["Modell des Treibers: Mengen, Spalten, Schlüssel, Annotationen"]
R --> K["Katalog für die Anwendung: SQLTables, SQLColumns, SQLStatistics"]
R --> P["Übersetzung einer Anweisung in eine OData-Anfrage"]
P -->|"HTTP"| A["JSON-Antwort"]
A --> E{"Antwort einordnen"}
E -->|"Sammlung value"| Z["Zeilen: Eigenschaft je Name einer Spalte zugeordnet"]
E -->|"@odata.nextLink"| F["nächste Seite"]
E -->|"error"| D["Diagnosesätze"]
E -->|"übrige Annotationen"| I["übergangen"]Abbildung: Der Treiber leitet die Bedeutung jedes Elements aus dem Schemadokument und der Art der Anfrage ab, nicht aus der Form des JSON. Die Einordnung der Antworten beschreibt Wie der Treiber Antworten einordnet und meldet.
OData beschreibt Daten als Entitäten mit Typen, Schlüsseln, verschachtelten Werten und Beziehungen; ODBC kennt nur Tabellen aus Zeilen und Spalten mit einem SQL-Typ. Der Treiber bildet deshalb nur den Teil des OData-Modells ab, der sich ohne Bedeutungsverlust als Tabelle darstellen lässt, und lässt den Rest erkennbar weg. Diese Seite nennt für jede Art von Element, was der Treiber daraus macht. Sie ist die Grundlage für alles unter Unterstütztes SQL: Erst wenn klar ist, welche Spalte welcher Eigenschaft entspricht, lässt sich einem Ergebnis trauen.
Wichtige Funktionen
- Eine Entitätsmenge ist eine Tabelle, eine Eigenschaft mit einfachem Typ eine Spalte, der Schlüssel des Entitätstyps der Primärschlüssel.
- Der Typ kommt aus
$metadata, nicht aus dem JSON: Eine Zahl, die als Text ankommt, bleibt eine Zahl; ein Wert, der nicht zum angekündigten Typ passt, ist ein Fehler, keine stille Umdeutung. - Was sich nicht als Spalte darstellen lässt, fehlt sichtbar: komplexe Werte, Sammlungen und Beziehungen erscheinen nicht, statt flachgeklopft oder als Text angeboten zu werden.
- Steuerinformationen sind keine Daten:
@odata.nextLinksteuert das Blättern, die übrigen Annotationen einer Antwort werden nie zu Spalten.
Grundkonzepte
Was aus dem Schemadokument wird
Der Treiber liest $metadata einmal je Verbindung, beim ersten Gebrauch. Grundlage ist das Format
CSDL XML von OData 4.0/4.01 (siehe Normen und Spezifikationen).
Element in $metadata | Im ODBC-Modell | Begründung |
|---|---|---|
EntitySet | Tabelle, Tabellenart TABLE | eine adressierbare, abfragbare Menge gleichartiger Sätze |
EntitySet mit einem Typ, den das Dokument nicht beschreibt | fehlt | ohne Typ keine Spalten |
Property mit einem Typ Edm.…, den der Treiber abbildet | Spalte; Typ, Länge und Genauigkeit nach Datentypen; Nullable="false" als SQL_NO_NULLS | ein einfacher Wert je Satz |
Property mit einem anderen Edm.…-Typ (Edm.Geography…, Edm.Geometry…, Edm.Duration, Edm.Stream) | fehlt | keine verlustfreie Entsprechung als SQL-Typ |
Property eines komplexen Typs (ComplexType) | fehlt | ein verschachtelter Wert hat keine Spalte; Flachklopfen (Adresse_Ort) erfände Spalten, die der Dienst weder filtern noch schreiben kann |
Property vom Typ Collection(…) | fehlt | mehrere Werte je Satz lassen sich nicht in einer Zelle darstellen, ohne die Bedeutung zu verlieren |
Property eines Aufzählungstyps (EnumType) oder einer TypeDefinition | fehlt | der Treiber liest nur Eigenschaften, deren Typ unmittelbar Edm.… ist |
Key mit PropertyRef | Primärschlüssel und eindeutiger Index (SQLStatistics); Access übernimmt ihn als Primärindex | ohne ihn ist eine Menge nicht bearbeitbar |
Key, der eine fehlende Eigenschaft nennt | kein Schlüssel; die Tabelle ist schreibgeschützt | ein unvollständiger Schlüssel träfe mehrere Sätze |
NavigationProperty | fehlt; SQLForeignKeys antwortet leer | der Lesepfad folgt keiner Beziehung; Verbünde laufen nur über die Durchreiche |
BaseType (Vererbung zwischen Entitätstypen) | nicht ausgewertet: Eigenschaften und Schlüssel des Basistyps fehlen der abgeleiteten Menge | Vererbung wird nicht unterstützt |
OpenType: Eigenschaften, die nur im Satz stehen | fehlen | sie sind in $metadata nicht beschrieben |
Singleton | fehlt | kein Satz einer Menge, keine Zeilen |
FunctionImport, Function, ActionImport | fehlen; SQLProcedures antwortet leer | Aufrufe gehen nur über die Durchreiche |
gebundene Action UpdateByOriginal/DeleteByOriginal | Ändern und Löschen einer Tabelle ohne Schlüssel | Erweiterung, siehe Anforderungen |
andere Action | übergangen | – |
Annotation Core.Computed | Spalte schreibgeschützt (SQL_DESC_UPDATABLE) | – |
Annotation Core.OptimisticConcurrency | Spalte gilt als Zeilenversion | Schreibkonflikt über If-Match |
Annotationen Aggregation.ApplySupported, Aggregation.CustomAggregationMethods | Gruppieren über $apply | – |
Annotationen des Vokabulars Wkl.Store.V1 | Produktname, Schema, Typnamen, Indizes, Statistik, Durchreiche, Transaktionen | Erweiterung dieses Produkts |
| jede andere Annotation | übergangen | – |
Namen. Der Treiber meldet Mengen und Spalten unter den Namen aus $metadata. Einen Namen, den der
Dienst mit _xHHHH_ kodiert hat, entschlüsselt er, soweit Access das Ergebnis verarbeiten kann; siehe
Feldname kodiert oder zu lang.
Sichten. OData unterscheidet eine Sicht nicht von einer Tabelle; jede Menge erscheint als TABLE.
Was aus einer Antwort mit Daten wird
Eine Leseanfrage gibt eine Sammlung zurück: ein JSON-Objekt mit dem Feld value. Der Treiber ordnet
seine Teile so ein:
| Teil der Antwort | Bedeutung | Im ODBC-Modell |
|---|---|---|
value (Array) | die Sätze der Seite | je Element eine Zeile. Fehlt value oder ist es kein Array, ist die Antwort ein Fehler (siehe Antworten einordnen) |
| Eigenschaft eines Satzes, die eine Spalte beschreibt | Wert | Wert der Spalte, umgewandelt nach dem Typ aus $metadata |
Eigenschaft, die $metadata beschreibt, die im Satz aber fehlt | kein Wert geliefert | NULL |
null | kein Wert | NULL |
| Eigenschaft eines Satzes, die keine Spalte beschreibt (komplexer Wert, Sammlung, dynamische Eigenschaft, Navigation) | – | übergangen |
@odata.nextLink | Fortsetzung | der Treiber holt die nächste Seite, wenn die Anwendung weiterliest; nie eine Spalte |
@odata.context, @odata.count, @odata.etag und jede andere Annotation | Steuerinformation | übergangen, nie eine Spalte |
Umwandlung nach dem angekündigten Typ. Ein Wert wird nach seinem Typ in $metadata gelesen, nicht
nach seiner JSON-Form: "12.50" ist für eine Spalte Edm.Decimal die Zahl 12,50, "NaN" für
Edm.Double der Sonderwert. Passt ein Wert nicht zum Typ, bricht das Lesen mit der Meldung „Der Wert
der Spalte … passt nicht zum angekündigten Typ …“ ab; der Treiber rät keinen anderen Typ.
Zählen. COUNT(*) geht an …/$count; die Antwort ist eine Zahl als Text, keine Sammlung.
Was der Treiber beim Schreiben voraussetzt
Beim Ändern und Löschen bildet der Treiber den Wert für If-Match selbst aus der Spalte, die
Core.OptimisticConcurrency nennt und die Access als Bedingung mitschickt: Ein Binärwert wird zu
W/"<Base64>", ein Text, der mit W/ beginnt, bleibt, wie er ist, jeder andere Text wird zu
W/"<Text>". Ein Dienst muss also ein ETag dieser Form annehmen; das @odata.etag einer Antwort
liest der Treiber nicht. Der Datendienst Wkl.Data.Gateway nimmt diese Form an; bei einem
anderen Dienst prüfen Sie das vor dem Einsatz.
Was nicht abgebildet ist
Nicht abgebildet heißt: Der Treiber zeigt das Element nicht an und arbeitet ohne es weiter. Er meldet dabei keinen Fehler und keine Warnung. Eine Anwendung erkennt fehlende Teile nur daran, dass Spalte oder Tabelle nicht im Katalog stehen. Wo ein solcher Teil gebraucht wird, etwa ein Verbund über eine Beziehung oder der Aufruf einer Funktion, führt der Weg über die Durchreiche, sofern der Dienst sie anbietet.
Verwandte Themen
- Wie der Treiber Antworten einordnet und meldet – Erfolg, Fehler, Warnung, unerwartete Antwort
- Wie der Treiber Anweisungen übersetzt – vom SQL zur Anfrage
- Escape-Klauseln und Datentypen – Typabbildung im Einzelnen
- Anforderungen an einen OData-Dienst – was der Dienst anbieten muss