Auswertung Restriktiv vs. Robuste Entwicklung

Auf meinen Blogeintrag hin zu "Rebuste vs. Restriktive Programmierung", wo ich die Behauptung aufgestellt habe, dass es eine Frage des Charakters des Entwicklers sei, gab es mittlerweile mehr als 30 Fremdkommentare. Ein spannendes Thema, dass ich nun hier einmal zusammen fassen möchte.

Ausgangsthese war die Betrachtung des eigenen Programmierstils in Bezug auf Robustheit bzw. Restriktivheit von API-Methoden. Am Beispiel der Methode LoadCustomer(string alphaNumber), wobei die Kundennummer alphanumerisch sei, bedeutet Restriktiv (für mich wohlgemerkt), dass eine Methode nur dann einen Kunden findet, wenn die Kundennummer exakt übereinstimmt. Also keine Leerzeichen, kein null, Groß- und Kleinschreibung stimmt, alle Bindestriche und Sonderzeichen sind exakt eingegeben. In allen anderen Fällen wird eine Exception geworfen und der Kunde konnte nicht gefunden werden. Robust bedeutet, dass z.B. einfach mal Leerzeichen Links und rechts abgeschnitten werden, weil so die Chance, mehr Eingaben als gültig zu erkennen höher ist. Bei null gibts natürlich auch da ein Problem, aber Bindestriche könnte man z.B. auch ersetzen oder versuchen zu behandeln, wenn das Format der Nummer klar ist.

Es sei noch kurz erwähnt, dass ich oft mit Client-Entwicklung zu tun habe und deswegen oft von dem Szenario ausgehe, dass unter anderem Benutereingaben (bzw. ungeprüfte Eingaben woher auch immer) direkt an die API übergeben werden. Als Beispiele wurde zum Beispiel genannt, dass Controls.Add(null) oder label1.Text=null einfach nichts macht. Es stürzt nicht ab und wirft keine Exception, eine meiner Meinung nach sehr robuste und gute Entwicklungsweise.

Andere Beispiele für Restriktive Entwicklung wurden genannt, so zum Beispiel "simpler Datentyp".Parse oder auch alles was mit Sicherheit zu tun hat.

Zusammenfassend kann folgendes gesagt werden:

1. Alle beteiligten sind sich einige, dass, egal was auch immer eine Methode macht. Es muss irgendwo stehen und dokumentiert sein.

2. Eine Methode sollte im Namen schon soo viel wie möglich darüber aussagen, was sie auch tut. Als Beispiel hierfür gibt es die ganzen coolen ExtensionMethods, die da z.B. heissen First<>() oder FirstOrDefault<>(), wobei First eine Exception werfen kann (restriktiv) und FirstOrDefault einfach den Defaultwert zu dem Datentyp zurückgibt (robust). Namen wie "LoadCustomerByNumderOrDefaultAndTrimAndCaseSensitiveAndOtherSignsAreHandledToo" (ich übertreibe leicht), sind natürlich ungünstig.

3. Ein grober Nachteil meines Favoriten, nämlich der robusten Entwicklungsmethodik, ist der Fakt, dass unter Umständen Fehler "verloren" gehen könnten. Dabei sind sich auch alle einig, dass dies niemals der Fall sein sollte. Leere catch-Blöcke sind nur in Ausnahmesituationen ok und ansonsten sollte eine Exception, wenn sie schon nicht nach aussen weitergereicht wird, zumindest irgendwie irgendwo protokolliert werden. Andere Entwickler erkennen Fehler schneller und besser, wenn eine Exception geworfen und nach aussen gereicht wird.

4. Oft ist es eine Frage der Umgebung und der Anforderung, wie und wo eine API eingesetzt wird und ob sie sich an dieser Stelle restriktiv und robust verhalten soll. In Sachen Sicherheit sollte eine API immer restriktiv sein. Also hier kann nicht mal eben versucht werden, dem Benutzer unter die Arme zu greifen und seine Eingabe doch noch richtig zu interpretieren. Wenn ein Kennwort nicht exakt stimmt, dann ist es falsch. Das ist klar.

5. Es wurden einige Vorgehensweisen und Situationen genannt, die, wenn sie richtig durchgeführt und eingehalten werden, viele Fragen von vorn herein beantworten, damit nachher niemand da steht und nicht weiss, was er genau machen soll (und so vllt. eine Exception wirft oder auch nicht...). Ein Ansatz war immer vorher gut durchdachte Kontrakte zu erstellen, und wenn diese klipp und klar sind, dann stellen sich auch später keine Fragen wie :"Ja soll ich dem Benutzer nun unter die Arme greifen und Leerzeichen wegmachen bei der Eingabe einer Kundennummer???". Weiterhin wurde gesagt, dass, wenn alle Teammitglieder gut, oft, offen und ordentlich mit einander kommunizieren, es dann auch einfacher ist, Methoden die man geschrieben hat, für andere Entwickler bereitzustellen. Leider ist dieser Perfektionissmuss, der notwendig ist, damit wirklich immer alles glatt läuft, so wie es im Buche steht, oft nur Theorie. Die echte Praxis unterscheided sich aus vielerlei Gründen oft, was wohl jeder bestätigen kann, der schon in unterschiedlichen Projekten mit unterschiedlichen Kunden und unterschiedlichen Anforderungen gearbeitet hat...

6. Es kam heraus, dass die Mehrheit der Entwickler eher zur restriktiven Gruppe zählen würden, als zur robusten. Sie würden viel eher hier und da mal eine Exception mehr werfen, als eine zu wenig. Eine gute Alternative ist es aber, z.B. in seiner API einstellen zu können, ob man lieber ne Exception haben will oder nicht. So verhält sich die API entweder robust oder restriktiv.

7. So, im Endeffekt könnte man da bestimmt noch weiter diskutieren. Ich selbst habe mit meiner robusten Enwicklungsart mehr positive Erfahrungen gemacht als mit der restriktiven Variante und meine Kunden auch (Endbenutzer sowie auch andere Entwickler). Deswegen werde ich wohl dabei bleiben. Andere widerum haben genau andere Erfahrungen gemacht. Deswegen bleiben diese bei ihren Methoden. Wieder Andere sagen, wenn man sich überhaupt zwischen restriktiv und robust entscheiden muss (das macht man meiner Meinung nach aber auch oft im Unterbewusstsein), dann is schon von vorn herein irgendwas falsch.

Also die eine klare Endlösung gibt es nicht. Die Meinungen gehen auseinander aufgrund der Erfahrungen der Entwickler hier. Beide Versionen haben Vor- und Nachteile, wie Alles in der Welt. Wichtig ist nur, dass man sich überhaupt mal bewusst ist, dass es da einen Unterschied gibt und in welche Richtung der eigene Charakter aufgrund von Erfahrungen geprägt wurde...

Restriktiv vs. Robust

Hat sich schon mal jemand von euch Gedanken über Restriktive Programmierung vs. Robuste Programmierung gemacht? Dahinter stecken keine Pattern oder sowas, sondern Charaktere von Entwicklern.

Hinter restriktiver Programmierung steht ein Charakter der aussagt: "Ich will diese schlechte Welt verbessern".

Hinter robuster Programmierung steht ein Charakter der aussagt: "Ich lebe mit dieser schlechten Welt und mache trotz Fehlern weiter".

Wenn ihr also eine Methode entwickelt, die einige Parameter bekommst, zu welcher Art von Entwicklern zählt ihr euch? Seit ihr Entwickler, die bei jeder noch so kleinen Ungereimtheit sofort eine Exception werfen (das wäre die restriktive Variante) oder nehmt ihr die Parameterwerte, analysiert sie und versucht trotzdem, egal was da kommt, das beste draus zu machen (das ist die robuste Variante)?

Natürlich kann dies nie pauschalisiert werden, und natürlich spielen auch die Anforderungen eine Rolle, aber dennoch kann man, so denke ich, seinen Charakter einer dieser beiden Varianten zuordnen. Ich persönlich bevorzuge die Robuste Variante, denn so kann meine Methode von mehreren Entwicklern genutzt werden, und in den meisten Fällen kommt trotzdem das erwartete Ergebnis heraus. Und nur selten ertönt ein Stöhnen und die Worte "Oh man, was is denn nun schon wieder mit der Scheiß Methode? Kann die nich einfach mal funktionieren??” hehe

Ein Paradebeispiel is übrigens ein Parameterwert vom Typ string. Alle meine Methoden, die einen string erhalten, stürzen niemals ab, wenn z.B. ein null kommt (Es sei denn es ist so gewollt oder sowas). Ein null interpretiere ich eigentlich immer einfach als Leerstring. Aber es gibt auch viele Entwickler, die dann ne ArgumentNullException werfen (oder es erst gar nicht prüfen und in eine NullReferenceException laufen)... Das ist in meinen Augen eine falsche Einstellung. So nach dem Motto: "Och nö, ich mag jetzt nicht nachdenken, ich werf einfach mal ne Exception, sollen die anderen mal drüber nachdenken, und wenn ich nich exakt genau das bekomme, was ich haben will, ja dann bin ich halt bockig..." hehe

 

Was meint ihr dazu? Schreibt mal eure Meinung dazu. Ich denke, in dieser Sache einige andere Sichtweisen könnte meine eigene Sichtweise stark erweitern…

.NET Tipps (Teil 1/n)

Nun wo ich endlich meinen neuen Blog habe, möchte ich meine .NET-Tipps-Sammlung erneut aufleben lassen. Selbst wenn man jahrelang mit dem .NET-Framework gearbeitet hat, gibt es immer wieder Ecken, wo man vorher noch nie drin war. Tipps und Tricks, die man im Laufe eines Entwicklerlebens mal so nebenbei herausfindet, sind oft mit die wertvollsten Informationen, weil sie entweder die tägliche Arbeit erleichtern, oder Antworten geben auf Dinge, "die man schon immer mal wissen wollte"...

Ich hab mir mal ein paar Kleinigkeiten herausgesucht, die vllt. noch nicht jeder weiß, und die man einfach wissen sollte. Leider merkt man, wenn man jahrelang als Entwickler arbeitet, manchmal schon gar nicht mehr, was man vllt. so noch nicht wissen könnte.

Zusammenfassung am Anfang

Falls jemand meine ganzen Aus- und Abschweifungen oder Sonstiges nicht lesen mag, für den hier kurz und knackig die Tipps zusammengefasst. Für Erklärungen musste dann aber doch unten lesen :-)

 

System.Reflection.MethodBase.GetCurrentMethod();
newItem = oldItem ?? new ItemClass();
using WCF = System.ServiceModel.Channels;
string.IsNullOrEmpty()
System.Threading.Interlocked
System.Security.SecureString
GC
employee.Work += employee_Work;
System.Math.Max(myIntValue, 1);

 

So, nun mal noch zu den einzelnen Punkten:

Infos zur Klasse MethodBase

Habt ihr gewusst, dass es in der Klasse MethodBase eine statische Methode gibt, die da heißt "GetCurrentMethod()"??? So kann man nicht nur die aktuelle Methode und das Objekt herausfinden, in der oder in dem man sich gerade befindet, das coole an dieser Methode ist, dass sie auch für statische Klassen funktioniert.

Der ??-Operator

Kennt ihr schon den ??-Operator? Dieser Operator prüft den linksstehenden Term auf null. Ist der null, so wird der rechte Term zurückgegeben. Ist in dem kleinen Beispiel hier oldItem = null, dann wird new ITemClass() zurückgegeben, ansonsten, wenn oldItem ein gültiger Wert ist, wird oldItem zurückgegeben...

newItem = oldItem ?? new ItemClass();

DateTime.Now

Das wissen wahrscheinlich die meisten, aber ich sags trotzdem. Wenn nur ein einziger dabei ist, ders noch nicht weiß, hat es sich gelohnt. In DateTime.Now findet man die aktuelle Uhrzeit mit dem aktuellen Datum.. :-)

Using-Alias

Manche Entwickler binden mit Absicht die usings nicht ein, weil “man so ja sofort sieht, woher die Klasse kommt”. Diese Argumentation kann ich teilweise nachvollziehen, deswegen könnte man anstatt immer den gesamten WCF-Namespace auszuschreiben, einfach das hier nutzen: “using WCF = System.ServiceModel.Channels;”

IsNullOrEmpty in System.string

Die Klasse string beinhaltet eine statische Methode namens "IsNullOrEmpty". Viele wissen nicht, dass strings auch null sein können. Wenn man z.B.

string name;

name.Lenght

macht, so schmiert einem sein Progrämmchen ab. Und einfach nur auf "" prüfen is auch nich so dolle. Viel sicherer ist if(string.IsNullOrEmpty(name)), dann... erst weitermachen. Im übrigen gibt es auch string.Empty. Das ist ein Leerstring, vergleichbar mit "", aber sauberer ist es, string.Empty zu nutzen.

Atomare Methoden der Interlocked-Klasse

Wer viel mit Threads programmiert, sollte die Interlocked-Klasse kennen. Sie beinhaltet atomare Methoden, also Methoden, die nicht unterbrochen werden können. Es ist ja so, dass so ein simples "i++" keine atomare Methode ist. Es sind mehrere Registerumräumungen nötig (sichern, inkrementieren, zurückschreiben). Wenn dabei ein anderer Thread dazwischenhaut, arbeitet der vllt. auf einem falschen Wert. Deswegen lieber das Inkrement der Interlocked-Klasse nutzen, damit passiert das nicht.

SecureString für sichere Strings im Speicher

Ihr wollt eine Anwendung schreiben, wo man auch mal ein Passwort im Speicher halten möchte? Um es möglichen Angreifern wenigstens ein wenig schwerer zu machen, gibt es die Klasse "SecureString" im Security-Namespace. Damit wird der String verschlüsselt im Speicher abgelegt... Kann manchmal bestimmt nicht schaden.

Ein wenig Garbage Collection 

Mit "GC" kann man einfach mal eben so auf den GarbageCollector zugreifen. Der sammelt die alten toten Objekte ein. Hier kann man das Einsammeln manuell starten, oder auch festlegen, dass Objekte nicht eingesammelt werden sollen.

1.0 == 0.9Periode9 = true... ???.   <-- Hää?

Zu guter letzt. Habt ihr gewusst, dass die beiden folgenden Ausdrücke genau das Gleiche sind?
"eployee.Work += new EventHandler( employee_Work );"
und
"employee.Work += employee_Work;"
Ja, doll. 1 ist ja auch exakt das gleiche wie 0,9Periode9... aber wir wollen ja nicht mit Mathematik anfangen...

Na wenn wa eh gerade bei Mathe sind

Schaut euch mal um in der Klasse “System.Math”. Die beinhaltet sehr viele statische Methoden, die auch nützlich sein können, wenn man keine mathematische Anwendung schreiben will. Zum Beispiel habe ich eine int-Property, die aufgrund von Konventionen immer mindestens 1 zurückgeben muss. Also return System.Math.Max(myIntValue, 1);

Runden muss man können

Habt ihr gewusst, dass Math.Round nicht nach dem Standard-Schulverfahren rundet, wie man es aus der Grundschule kennt, sondern nach dem Standard IEEE 754, Abschnitt 4? Das ist das sogenannte „Banker’s Rounding“ und funktioniert so, dass bei Werten die genau in der Mitte stehen (z.B. 1,5), die Werte nicht generell aufgerundet werden, sondern nur dann aufgerundet werden, wenn der Wert davor ungerade ist. Bei geraden Werten wird der Rest einfach abgeschnitten und somit abgerundet.
Beispiel:


Math.Round(1.5,0); // = 2
Math.Round(2.5,0); // = 2


Somit kann der allgemeine Rundungsfehler, der sich normalerweise immer nur nach oben ausdehnt, fast komplett ausgeglichen werden. Ich muss zugeben, dass dieses Verhalten etwas gewöhnungsbedürftig ist. Wenn man das Verhalten so haben möchte, wie man es aus der Grundschule kennt, dann gibt es einen kleinen weiteren Parameter in der Round-Methode, die dann wieder ordentlich rundet.


Math.Round(1.5,0, MidpointRounding.AwayFromZero) //=2
Math.Round(2.5,0, MidpointRounding.AwayFromZero) //=3

C++ sinnvoll mit C# verbinden

Ich habe mich früher sehr viel mit C und C++ beschäftigt (ja mit ASM auch, aber das is egal :-)) und obwohl ich .NET unglaublich toll finde, braucht man trotzdem hier und da immer mal wieder das gute alte c++, denn das große .NET kann leider auch noch nicht alles (einfach SmartCards auslesen zum Beispiel.. hehe). Deswegen wollt ich euch mal kurz im Allgemeinen zeigen, wie man sich schnell und einfach ein C++-Projekt baut, und das auch in .NET benutzen kann.

Zuerst in Visual Studio eurer .NET-Solution ein leeres C++-Projekt hinzufügen ("Leeres Projekt") und dann meinetwegen eine Klasse hinzufügen ("Hinzufügen/C++-Klasse") (Es gibt auch nen Assistenten, aber das is zu einfach... Wir wollen ja mal unter die Haube gucken und das richtig machen... hehe).

Danach gehen wir mal mit einem Rechtsklick auf das Projekt und Eigenschaften in die gesamten Eigenschaften. Unter Allgemein stellen wir diese beiden Dinge ein: Konfigurationstyp auf Dynamische dll und Common Language Runtime-Unterstützung aud Common Language Runtime-Unterstützung (/clr)).

Eigenschaften

Wenn man nun einen Verweis in seinem C#-Projekt hinzufügt, kann man unter Projekte direkt das C++-Projekt auswählen und hinzufügen. Und kompilieren kann man dann auch, denn es kommt kein Fehler mehr wie: "Fehler 2 error C3381: test": Assemblyzugriffsspezifizierer sind nur in Code verfügbar, der mit einer /clr-Option kompiliert wurde. d:\leer\test.h 4 leer".

Ok, in meinem Beispielchen heisst meine C++-Klasse einfach nur Test. und jetzt kann man schon kompilieren und auf die c++-Klasse zugreifen von C# aus. Da wir keinen namespace in c++ vergeben haben, kann man direkt in C# schreiben "Test t = new Test():".

Ok, aber wir sind noch nicht fertig, denn das Problem liegt ja wie immer im Detail. Und da ich kein Evangelist bin, wollen wir nochmal ein wenig weitermachen und schnell eine kleine Methode einfügen in unsere C++-Klasse. Ich füge nun eine Methode "int Add(int a, int b) { return a + b;}" hinzu. In der Headerdatei wird se deklariert und in der cpp ausprogrammiert. Tja, und in C#, wenn ich nun mache t.... erscheint einfach meine Add-Methode nich... Unglaublich.

 Code

So, also was macht man da? Ja wir versehen unsere C++-Klasse mit dem Schlüsselwörtchen "ref" in "public ref class Test"...

Und siehe da, schon können wir kompilieren. So, und ich hör aber immer noch nicht auf. Solltet ihr nun zufällig wenn ihr drauf zugreift, eine BadImageFormatException erhalten mit dem dummen Text "Die Datei oder Assembly "xxx, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null" oder eine Abhängigkeit davon wurde nicht gefunden. Es wurde versucht, eine Datei mit einem falschen Format zu laden.", dann prüft mal, ob euer C#-Projekt zufällig 64 Bit is (oder auch AnyCPU) und euer C++-Projekt zufällig 32 Bit. Wenn ja, vereinheitlicht das, denn das zu mischen is wohl nich so einfach (Unter den C++-Einstellungen unter Linker/Erweitert/Zielcomputer und unter C# unter Projekteigenschaften/Ertellen/Zielplattform) (siehe Bild unten). Nachdem beide Projekte die gleiche Zielplattform haben, gehts auf einmal...

Um strings mit der C++-Welt austauschen zu können, wird das Zeichen Accent Zirkumflex (^) benötigt. Die hier dargestellte C++-Methode liefert für C# einen String zurück. Mich erreichte weiterhin eine Anfrage, wie man denn das mit Arrays machen könnte. Siehe dazu die Methode GetPoints. Sie bekommt einen double-Pointer, und auf der .NET-Seite habe ich ein double-Array. Leider musste ich hierfür mit dem “unsafe”-Schlüsselwort arbeiten. Wenn jemand weiss, wie man Arrays übergeben kann ohne dieses Schlüsselwort, kann er das ja gern mal als Kommentar hinterlassen”. Hier nochmal der Code ganz übersichtlich:

Test.h

   1:  
   2: public ref class Test
   3: {
   4:     public:
   5:         Test(void);
   6:         int Add(int a, int b);
   7:         System::String^ GetName();
   8:         void GetPoints(double* myData);
   9:         ~Test(void);
  10: };

Test.cpp

   1: #include "Test.h"  
   2:   Test::Test(void)   
   3:   {}  
   4:  
   5:   int Test::Add(int a, int b)   
   6:   {
   7:     return a + b;   
   8:   }  
   9:  
  10:   System::String^ Test::GetName()
  11:   {
  12:     char str[80] = "";
  13:     System::String ^realStr;
  14:  
  15:     CoolCppFunction(str);        // Im Array str befindet sich nun unser gewünschter String
  16:     realStr=gcnew System::String(str);
  17:     return realStr;
  18:   }
  19:  
  20:   void  Test::GetPoints(double* myData)
  21:   {
  22:       myData[0]=1;
  23:       myData[1]=2;
  24:       myData[2]=3;
  25:       myData[3]=4;
  26:   }
  27:  
  28:   Test::~Test(void)   
  29:   {}

Program.cs

   1: Test t = new Test();
   2: t.Add(5, 6);
   3:  
   4: unsafe
   5: {
   6:     double[] data = new double[4];
   7:  
   8:     fixed (double* pArray = data)
   9:         t.GetPoints(pArray);
  10: }

Einige Ergänzungen zum Unsafe-Schlüsselwort. Das sagt euch zwar auch der Compiler, aber die Option “Unsicheren Code zulassen” muss in den Projekteinstellungen aktiviert werden, sonst funktioniert das unsafe-Schlüsselwort nicht und somit auch die Pointer nicht. Das soll verhindern, dass gewiefte C++-Entwickler “mal ausversehen” alles doch wieder mit Pointern machen… :-)

(Die Zielplattform hab ich hier auch mal gelb gemacht, weil die vorhin erwähnt wurde in Bezug auf die BadImageFormatException)

image

Einfach großartig diese Zusammenarbeit dieser beiden Supermächte. C++ is wie ein alter weiser Greis und .NET wie ein fast fertig ausgebildeter Meister. Der Greis isn bissl alt und schwerfällig, kann aber dafür alles. Und .NET is modern, dynamisch, flexibel, leicht zu erlernen, einfach geil, kann aber in manchen Situationen noch nich so viel wie C++... Ein perfektes Team. Spätestens damit geht einfach alles!

DB2 NHibernate - HibernateException

Ich beschäftige mich seit geraumer Zeit mit DB2 und NHibernate und habe nun mal wieder eine dumme überflüssige Exception bekommen, nämlich die folgende:

NHibernate.HibernateException ist aufgetreten.
  Message="ERROR [42727] [IBM][DB2/NT] SQL0286N  Es konnte kein Standardtabellenbereich mit einer Seitengröße von mindestens \"8192\" gefunden werden, für den die Berechtigungs-ID \"DBUSER\" eine Nutzungsberechtigung hat.  SQLSTATE=42727\r\n"
  Source="NHibernate"
  StackTrace:
       bei NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Action`1 scriptAction, Boolean export, Boolean justDrop)
       bei NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Boolean script, Boolean export, Boolean justDrop)
       bei NHibernate.Tool.hbm2ddl.SchemaExport.Create(Boolean script, Boolean export)
       bei CreateDatabase.NHibernateDBHelper.BuildSchema(Configuration config) in D:\CreateDatabase\NHibernateDBHelper.cs:Zeile 56.
  InnerException: IBM.Data.DB2.DB2Exception
       Message="ERROR [42727] [IBM][DB2/NT] SQL0286N  Es konnte kein Standardtabellenbereich mit einer Seitengröße von mindestens \"8192\" gefunden werden, für den die Berechtigungs-ID \"MWAREDBUSER\" eine Nutzungsberechtigung hat.  SQLSTATE=42727\r\n"
       Source="IBM.Data.DB2"
       ErrorCode=-2147467259
       StackTrace:
            bei IBM.Data.DB2.DB2Connection.HandleError(IntPtr hHandle, SQL_HANDLE hType, RETCODE retcode)
            bei IBM.Data.DB2.DB2Command.g()
            bei IBM.Data.DB2.DB2Command.ExecuteNonQuery()
            bei NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Action`1 scriptAction, Boolean export, Boolean throwOnError, TextWriter exportOutput, IDbCommand statement, String sql)
            bei NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Action`1 scriptAction, Boolean export, Boolean justDrop, IDbConnection connection, TextWriter exportOutput)
            bei NHibernate.Tool.hbm2ddl.SchemaExport.Execute(Action`1 scriptAction, Boolean export, Boolean justDrop)
       InnerException:

So, nach stundenlangem herumärgern ist es nun nämlich so, wenn man bei dieser komische DB2-Steuerzentrale eine neue Datenbank anlegt, dann kann man dort eine "Standardgröße für Pufferpol und Tabellenbereichsseite" angeben. Der Defaultwert liegt bei 4 KB. Tja, nun bin ich bei meiner User-Tabelle, die aus 37 Spalten besteht, schon da an die Grenze gestoßen, und die einzige Möglichkeit, die ich gesehen habe, war die Datenbank neu zu erstellen, mit einem größeren "Tabellenbereichsseite"... Solltet ihr auch mit DB2 arbeiten, freiwillig oder unfreiwillig, dann nehmt da von Anfang an lieber einen größeren Bereich beim erstellen, sonst könnt ihr mit NHibernate vllt. irgendwann einfach kein Schema mehr erstellen... (Mit DB2 gibts einen riesigen Haufen Probleme, den man mit MS SQL nicht hätte...)

Sollte jemand einen besseren Weg kennen, die Tabellengröße da einzustellen, dann kann er mir gerne Schreiben (Kommentare funzen leider noch nicht, aber ich arbeite dran)...

DB2, NHibernate, 64 Bit und nen Haufen Fehler

So, nach vielen Stunden lesen, probieren, recherchieren, probieren, fluchen und wieder probieren hab ichs nun und schreib das nun hier mal für alle auf. Um mittels NHibernate auf eine DB2-Datenbank zuzugreifen, muss man die folgenden Dinge beachten:

  • Referenz auf die IBM.Data.DB2.dll
  • Diese Referenz muss als lokale Kopie vorliegen, sonst findet er da irgendwas nich. Wenn man das nicht macht, erhält man folgende wunderbar klar strukturierte Exception:
    • (An invalid or incomplete configuration was used while creating a SessionFactory. Check PotentialReasons collection, and InnerException for more detail.  * Database was not configured through Database method.
      )
    • In der InnerException steht dann, dass er den DB2 Driver nich laden konnte, weil irgendein Interface fehlt
  • Die Zielplattform muss x86 sein. Bei mir funktioniert nämlich der 64 Bit Driver von IBM nich (war ja auch nich zu erwarten). Deswegen hab ich 32 Bit installiert, der funktioniert halbwegs. Deswegen muss die Zielplattform auch 32 Bit sein, sonst geht das alles nich...
Mysteriöse TimeoutException bei NHibernate

Ich hatte eine zeitlang einfach so eine TimeoutException, als ich mittelsn NHibernate und WCF Daten übertragen wollte. Der Fehler bestand aus zwei Problemen.

Erstens hatte ich Objekte vom Typ IList, die ich übertragen wollte. Das geht nicht, denn so ist ja gar nicht klar, was auf der anderen Seite erzeugt werden soll. Als ich dies nach List abänderte gings...

Parallel dazu gabs ein anderes Problem. Ich wollte, dass meine Entitäten von einer Basisentität erben, damit ich Gemeinsamkeiten nur einmal entwickeln muss. Und in der Schnittstelle sollte natürlich die Basisklasse angegeben werden, damit ich z.B. bei der Methode Add(DBEntityBase entity), auch jegliche Objekte angeben konnte, die von DBEntityBase erben. Dazu muss aber das Attribut KnownType benutzt werden, da er sonst nicht weiss, was denn nun eigentlich gemeint ist.

Diese beiden Fehler führten bei mir zu einer TimeoutException..

Mehr Beiträge Nächste Seite »
Nico Franze Herzlich Willkommen auf meinem Blog. Ich bin Nico, freier Softwareentwickler sowie Autor für Fachzeitschriften. Hab mit .NET Version 1.0 begonnen (damals noch VB.Net) und bin dann schlussendlich bei C# gelandet. Mehr Infos gibts unter www.nfranze.de


Suche

Los

Translator Widget

Dieser Blog

Syndikation


Locations of visitors to this page