.NET und Oracle

Das Lieblings-Szenario eines .NET-Entwicklers ist sicherlich, in einem Projekt auf dem SQL Server zugreifen zu können. Hier sind die Fronten relativ klar und man benutzt einfach den eingebauten Provider. Die Realität erfordert allerdings – gerade in größeren Projekten – immer wieder den Zugriff auf Oracle. Und genau hier trennt sich die Spreu vom Weizen. Wir versuchen mal einen Überblick.

Webcast

Für alle Ungeduldigen hier der Screencast mit den praktischen Übungen zum Thema:

Vorbemerkungen

Oracle ist einfach eine Art Industrie-Standard. Mag man darüber denken, was man will. Daraus folgt, dass man als professioneller .NET-Developer früher oder später damit konfrontiert wird. Die Frage ist nun immer wieder die gleiche: „Wie greife ich am besten auf die Oracle zu?“. Die Antwort ist nicht leicht zu geben und hängt von diversen Faktoren ab.

Know-How

Zunächst einmal müssen wir einen kleinen Theorie-Schlenker in die Oracle-Welt unternehmen. Der Server interessiert uns dabei erstmal gar nicht so. Wir konzentrieren uns auf den Client.

Es gibt aus .NET-Sicht 2 unterschiedliche Methoden, um auf Oracle-Datenbanken zuzugreifen. Die Standard-Methode nutzt den sog. Oracle Client.

Der Oracle Client ist grob gesagt, eine Zugriffsschicht auf Oracle-Datenbanken. Der Client muss auf jedem System installiert werden, von dem ein Zugriff auf die Oracle-Datenbank stattfinden soll:

Abb. 1: Schema eines Standard-Zugriffs

Abb. 1 zeigt bereits einige wichtige Elemente. Zum einen zeigt des den Standard-TCP-Port für Oracle-Zugriffe 1521. Das wichtige ist aber die grüne Schicht in der Mitte. Diese Schicht wird durch eine Installation des Oracle Clients auf der Client-Maschine erzeugt. Wichtig hier ist, dass man zum einen einen vollwertigen Client installieren kann (Download). Die andere Variante ist. Einfacher ist das Installieren der Oracle Data Access Components (ODAC) (Download).

Die ODAC beinhalten neben einem sog. Instant Client (eine einfache nicht großartig konfigurierbare Version des Oracle Clients) auch die verfügbaren Zugriffs-Provider für ASP, .NET, Java usw.

Ich empfehle dringend, immer die 32-bit-Versionen zu verwenden, wenn man die eigene Anwendung nicht aus irgendwelchen Gründen zwingend auf 64-bit programmiert. Auf dem Visual-Studio-Rechner funktioniert nur die 32-bit-Version richtig, weil ansonsten der ODP.Net Provider nicht in VS eingebunden werden kann.

Wer nun versucht, eine Verbindung mit einem solchen Provider aufzubauen, ist zunächst ziemlich verwirrt. Man ist ja schließlich daran gewöhnt, eine Server-IP, einen Port, eine Datenbank und dann einen User anzugeben. Verwendet man ODP.NET, ist alles ein wenig anders:

Abb. 2: Verbindung mit ODP.NET

Abb. 2 zeigt den Standard-Provider-Settings-Dialog von .NET. Man kann ihn z.B. aufrufen, indem man im Server-Explorer des Visual Studio auf „Mit Datenbank verbinden“ klickt. Man muss nur aufpassen, dass bei „Datenquelle“ ODP.NET ausgewählt ist.

Abb. 2 zeigt auch, dass es statt der spezifischen Angabe eines Servers eine Dropdown-Box mit dem Namen „Datenquellname“ gibt. Hier kann man nichts eingeben, sondern die angezeigten Einträge stammen aus einer speziellen Datei mit dem Namen „tnsnames.ora“.

tnsnames.ora

Hier beginnt nun die lustige Welt von Oracle! Zunächst einmal ist es Oracle offensichtlich total schnuppe, was Microsoft so über die vernünftige Ablage von Programm-Dateien zu sagen hat. Oracle hat sich überlegt, dass man die *.ora-Datei einfach in den Ordner:

{StammordnerOracle}\{Version}\client_{x}\NETWORK\ADMIN\

legen muss. Alles, was hier in geschweiften Klammern steht, muss man an sein System anpassen. Dazu ein kleines Beispiel. Ich habe bei der Installation des Oracle Client den Ordner „C:\Oracle“ als Ziel angegeben. Meine tnsnames.ora muss also in

C:\Oracle\product\11.2.0\client_1\NETWORK\ADMIN

liegen, weil ich genau einen Oracle Client 11.2.0 installiert habe. Super Idee, Oracle!

Nicht immer ist die tnsnames.ora übrigens da. Ist keine zu sehen, muss eine neue angelegt werden (einfache Textdatei mit dem Namen reicht). Hier müssen nun Einträge mit folgendem Muster rein:


{NAME}=
  (DESCRIPTION=
    (ADDRESS=
    (PROTOCOL=TCP)
    (HOST={SERVER-ADRESSE})
    (PORT=1521)
  )
  (CONNECT_DATA=
    (SERVICE_NAME={DB-NAME})
  )
)

Die Einträge in den geschweiften Klammern müssen entsprechend den eigenen Einstellungen geändert werden. Speichert man diese Datei dann ab, sollte in Abb. 2 plötzlich ein Element mit dem Namen „{NAME}“ auftauchen. Darüber erkennt der Client dann die Server-Adresse.

Es geht auch anders

Wie bereits oben erwähnt, gibt es 2 Methoden, auf Oracle-Datenbanken zuzugreifen. Die erste haben wir nun gesehen. Die andere nennt sich „Direct Mode“. Vereinfach ausgedrückt, haben sich ein paar schlaue Leute überlegt, dass man die Zugriffe auch so emulieren kann, als wäre ein Client da. Man entschlüsselt einfach das OCI-Protokoll und jagt die Kommandos direkt an den Server.

Im Laufe der Jahre sind 2 Firmen übrig geblieben, die Produkte hier anbieten: DevArt und DataDirect. DataDirect kann für sich verbuchen, den schnellsten Zugriff auf Oracle-Datenbanken überhaupt anzubieten. Dafür hinken die Jungs erheblich bei der Unterstützung für Entwickler hinterher, weswegen sie aus den weiteren Betrachtungen raus fallen. Wir konzentrieren uns auf DevArt.

Gleich das wichtigste vorweg: DevArt bietet mit dem Produkt „dotConnect for Oracle“ eine kostenpflichtige Software an. Dafür bekommt man aber auch einiges geboten (später mehr). Ab 119 $ ist man mit einer Lizenz dabei. In der „Profesional“-Variante für 240 $ (jeweils pro Entwickler) ist dann die volle benötigte Power enthalten.

Hat man dotConnect auf seiner Entwicklungsmaschine installiert, kann man im Server-Explorer einen Verbindungsversuch wagen:

Abb. 3: dotConnect Connection einrichten

Man beachte die Option „Direct“. Dieser Dialog fühlt sich nun schon eher an, wie ein .NET-Datenprovider-Dialog. Das Ergebnis der ganzen Eingaben ist ein Connection-String, der für den DirectMode ungefähr so aussieht:


user id=USER;password=****;server=SERVERIP;<strong>direct=True</strong>;sid=DATENBANK;persist security info=True

Das wars! Kein Oracle Client, keine Deployment-Probleme. Super!!!

Kann es so einfach sein?

Mal abgesehen vom Preis scheint dotConnect also eine super Sache zu sein. Warum nicht gleich so? Keine Ahnung! Ich finde, Microsoft sollte seine Geldbörse auftun und dotConnect einkaufen. Super Sache das. Es ist trotzdem nicht alles Gold, aber besser geht Oracle unter .NET einfach nicht.

Ein paar Detail-Betrachtungen können trotzdem nicht schaden.

Performance

Zunächst einmal zur Performance. Der langsamst mögliche Zugriff auf eine Oracle-Datenbank kann erreicht werden, wenn man den Microsoft-Provider benutzt, den .NET mitbringt. Er ist nicht mehr supported und sollte daher auf keinen Fall verwendet werden!

Einige interne Messungen haben nun folgende Rangfolge der durchschnittlichen Zugriffszeiten (lesend, 120.000 Datesätze, WAN, gemittelt über 5 Versuche, Entity Framework 4) ergeben:

  1. dotConnect for Oracle im Client-Mode: 6,3864 s
  2. dotConnect for Oracle im Direct-Mode: 10,5552 s
  3. ODP.NET (immer Client-Mode): 12,7584 s

Klarer Sieg also für den dotConnect im Client-Mode. Der Direct-Mode kann offensichtlich einige Optimierungen nicht mitnehmen, ist aber immer noch besser, als ODP.NET!

Problemchen

Die beiden Provider bieten EF-Unterstützung an. Allerdings ist Oracle nun einmal Oracle und damit kommen ein paar Problem ins Spiel. Kandidat Nr. 1 ist die vermaledeite Benamungs-Konvention in Oracle. Für alle, die es nicht wussten: Oracle fordert im Jahr 2012 eine Beschränkung aller Objektnamen (Tabellen, SPs, Felder usw.) auf 30 Zeichen (LOL) und ist per Definition case-insensitiv! Ja, richtig gelesen: Für Oracle sind diese beiden Namen gleich: „TBL_LOG“ == „tbl_log“.

Das ist auch der Grund, warum der Unterstrich bei Oracle-DBAs so unheimlich beliebt ist. Es macht einfach keinen Sinn, seine Tabelle „LOGENTRIESBYUSERS“ zu nennen. Klar, im SQL Server könnte man sie „LogEntriesByUsers“ schreiben, nur halt nicht in Oracle. Also wird daraus meist „LOG_ENTRIES_BY_USERS“.

Blöd nur, dass ODP.NET sich EF-seitig komplett auf das ADO.NET Entity Model verlässt. Da dieses nur Plural/Singular kann, landet man bei entsprechenden Objektnamen:

[charp]

using (var ctx = new MyEntities())

{

var amount = ctx.LOG_ENTRIES_BY_USERS.Count();

}

Herzlichen Glückwunsch und „Auf Wiedersehen, liebe Namenskonvention!“.

Zum Glück gibt es ja dotConnect und die Jungs von DevArt haben ihren Markt erkannt. dotConnect hat nämlich einen komplett eigenen Entity Designer inkl. einer eigenen Modell-Sprache mit an Bord:

Abb. 4: Oracle-Model mit DevArt hinzufügen

Wählt man dieses Element unter „Hinzufügen -> Neues Element“ aus, startet ein eigener Assistent. Im Verlauf dieses Assistenten kann man dann in folgendem Dialog alle Namens-Einstellungen anpassen:

Abb. 5: Benamungs-Optionen im devArt-EF-Assistenten

Abb. 5 zeigt gleich mal eine nützliche Einstellungs-Variante. Am unteren Rand habe ich mal meine beiden Namen aus Oracle eingegeben und jeweils darunter sieht man, was die Einstellungen „Capitalize“ und „Remove prefixes“ bewirken. So ergibt das Modell auch Sinn und man fühlt sich als .NET-Entwickler auch im Oracle-Umfeld einigermaßen wohl.

Das verfluchte PrimaryKey-Problem

Ein Problem schleppen sowohl ODP.NET als auch DevArt mit sich rum. DevArt allerdings mehr aus Schlampigkeit, denn aus Logik. Aber eins nach dem anderen.

Fast jede moderne Datenbank bietet einen Mechanismus, um Schlüsselfelder als sog. Autoincrements zu markieren. Das bedeutet, dass der Wert des Schlüsselfeldes durch die Datenbank gesetzt wird, sobald ein INSERT erfolgt. Bei Oracle ist das (natürlich) anders. Hier braucht man eine Kombination aus einer sog. Sequenz (ein eigene Objekt, das Nummern bereit stellt) und einem BEFORE-INSERT-Trigger, um das gleiche Verhalten umzusetzen (siehe codingfreaks-Artikel).

Das wirklich blöde ist nur, dass nun die Entity-Modelle nicht mehr automatisch merken, dass dieser Mechanismus da ist und artig eine „0“ als Wert dieser Felder liefert, was die Trigger wiederum durcheinander bringt. Die Lösung ist, dass man im generierten Entity Model dem jeweiligen Feld in der Eigenschaft „StoreGeneratedPattern“ den Wert „Identity“ gibt:

Abb. 6: StoreGeneratedPattern setzen

Das erste Problem dabei ist, dass man dies bei einem Update des Modells wiederholen muss (dotConnect ab Version 7 soll es schon mitnehmen können). Viel entscheidender aber ist, dass die Designer hier Unsinn treiben und die Einstellung gar nicht im EF-Schema landet. Dort (Rechtsklick auf Modell in Projektmappen-Explorer -> Öffnen mit -> XML-Editor) steht nach der Änderung weiter:

<Property Name="DSID" Type="decimal" Nullable="false" />

Es muss aber folgendes dort stehen:

<Property Name="DSID" Type="decimal" Nullable="false" StoreGeneratedPattern="Identity" />

Vergisst man dies, landet man schnell im gleichen Alptraum, wie ich: stackoverflow-Frage.

Insgesamt ein lästiges Problem, dass gerade Devart so schnell, wie möglich los werden müssen.

Resumé

Letztlich bleibt nur zu empfehlen, sich ganz schnell eine Lizenz von dotConnect oder dem DataDirect-Provider zu besorgen. Die Dinger integrieren sich absolut sauber in .NET, sind schneller, als ODP.NET (als MS for Oracle sowieso) und bieten einen super Support für EF. Anders kann man auf Oracle unter .NET nicht vernünftig arbeiten.

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.

Diese Website verwendet Akismet, um Spam zu reduzieren. Erfahre mehr darüber, wie deine Kommentardaten verarbeitet werden.