Missing Index: CREATE NONCLUSTERED INDEX

by Oliver Wed, August 17 2011 18:28

Lately, we encountered a problem with the speed of our search on www.camping.info for a certain set of search criteria. It sometimes used to take over a few seconds before the updated results were shown. This most likely seemed to be a problem with the database so I went to investigate the offending queries using the wonderful NHibernate Profiler. I found a very slow query:

image

So I went and copied the long running query into a new query window in SSMS (SQL Server Management Studio). Since we use MS SQL Express on our production servers we don’t get the advanced database tuning advisor features of the full edition. Still, when you right click on any query window you’ll see the option “Include Actual Execution Plan” …

image [FULL]                                      image [EXPRESS]

… which already offers a lot of detail, as you can see in the following screenshot:

image

When you right click on the execution plan you’ll see the option “Missing Index Details…” - there you get a CREATE INDEX statement that is ready to use once you give a name to the new index. I did this for three indexes and now we have this for the same query:

image

115 ms instead of 1993 ms – that’s an improvement of 94%! Even if DB queries lasting longer than 50-60 ms are not really fast anymore, we’ve still got quite an improvement here.

Well, that’s it. Using a well-priced tool like NHibernate Profiler to identify slow queries and the Execution Plan feature of SSMS, we’re able to get quite a performance improvement in a short time.

Happy Coding and Optimizing,

Oliver

Tags:

Camping.Info | MS-SQL Server | NHibernate | Web Applikationen

NHibernate Tooling

by robert Mon, May 31 2010 15:08

Die Tools um NHiberante herum werden besser und mehr. Nachdem im letzten Jahr mit NHProf ein hervorragender Profiler erschien der uns schon manches mal sehr hilfreich war, kommt mit “Visual NHibernate” ein Code-Generator der sowohl von der Datenbankseite, als auch von der Modelseite her funktioniert.

Visual-NHibernate

Aus meiner Sicht ist für uns ist NHProf jedoch zunächst wenig interessant, da wir zukünftig für neue Projekte mit Fluent-NHibnerate auf Mapping per Konvention setzen wollen und bestehende Projekte ja Ihre Mapping schon haben.

Um bestehenden Legacy-Projekte den Wechsel auf ORM zu erleichtern, scheint Visual NHibernate bestens geeignet zu sein.

Links:

(Robert)

Tags:

NHibernate

NHibernate: Fetch Join + EntityCache

by Oliver Fri, October 16 2009 13:28

In unserer Test-basierten Entwicklung hat sich die Verwendung von Setup-Klassen für unsere Entities etabliert. Unsere Tests sehen folgendem sehr ähnlich:

[Test]
public void FetchJoinTest()
{
    var cat = _categorySetup.GetPersisted();
    var pitch = _pitchSetup.GetPersisted(p => { p.Categories.Add(cat); return p; });
    var reserv1 = _reservationSetup.GetPersisted(r =>
                                                     {
                                                         r.Pitch = pitch;
                                                         r.DateFrom = new DateTime(2009, 10, 31);
                                                         r.DateTo = new DateTime(2009, 11, 13);
                                                         return r;
                                                     });
 
    pitch = _pitchService.GetById(pitch.Id);
    Assert.That(pitch.Reservations.Count, EqualTo(2));
}
 

Leider funktioniert der Test so nicht - er schmeißt eine AssertionException, da pitch.Reservations keine Elemente enthält.

Warum nicht???

Nach langem hin und her, bin ich mir endlich sicher, es verstanden zu haben: es liegt an NHibernate's Entity-Cache! Da das Pitch-Objekt ja zum Zeitpunkt der Abfrage

 
    pitch = _pitchService.GetById(pitch.Id);
 

noch in der aktuellen Session geladen ist (was NHibernate meines Wissens nach anhand der ID feststellt), wird kein neues Objekt vom Typ Pitch instanziiert, sondern das schon existierende, pitch, zurückgegeben. Dieses enthielt aber noch keine Reservierungen zum Zeitpunkt der Persistierung - und so, wie es aussieht, werden diese auch nicht nachträglich geladen (für ein Objekt, das schon in der Session lebt).

Die Lösung ist jetzt gar nicht mehr so schwer: wir schmeißen das vorhandene Objekt einfach weg! Dafür gibt es Session.Evict():

[Test]
public void FetchJoinTest()
{
    var cat = _categorySetup.GetPersisted();
    var pitch = _pitchSetup.GetPersisted(p => { p.Categories.Add(cat); return p; });
    var reserv1 = _reservationSetup.GetPersisted(r =>
                                                     {
                                                         r.Pitch = pitch;
                                                         r.DateFrom = new DateTime(2009, 10, 31);
                                                         r.DateTo = new DateTime(2009, 11, 13);
                                                         return r;
                                                     });
 
    _nHibernateHelper.Session.Flush();
    _nHibernateHelper.Session.Evict(pitch);
 
    pitch = _pitchService.GetById(pitch.Id);
    Assert.That(pitch.Reservations.Count, EqualTo(2));
}

Jetzt läuft der Test mit Erfolg durch. Happy Coding!

Tags:

NHibernate | Test Driven Development (TDD)

"NHibernate.MappingException: Association references unmapped class" die Zweite

by Stefan Thu, August 13 2009 11:26

Ich bin heute auf ebendiese Exception gestossen als ich folgende zeilen zu einem Mapping-File in CO hinzufuegte: 

<set name="NHibernateFiles">
<key column="EntityId"/>
<one-to-many class="SpeakFriend.Utilities.StoredFile, SpeakFriend.Utilities"/>
</set>

Per Google-Suche landete ich direkt auf unserem Blog und fand Olivers Beitrag. Ich dachte schon, das Problem waere erledigt. Einfach das Mapping-File auf "Embedded Ressource" setzen und fertig. Aber Pustekuchen, das war ja schon alles richtig eingestellt. Ich konnte auch StoredFile von CO aus persistieren . Ein Blick in die App.config half mit auch nicht weiter:

<mapping assembly="SpeakFriend.Utilities" />
<mapping assembly="ConceptOffice.Core" />
<mapping assembly="ConceptOffice.Core.Client" />

Alles war fein referenziert. Und es klappte ja auch alles. Nur beim parsen des Mapping-Files hatte er ein Problem.

Nun die Aufloesung: Ein Durchwuehlen des Stack-Traces fuehrte mich auf folgende Stelle: 

protected NHibernateHelper _nhibernateHelperSqlLite =
new NHibernateHelper(new Configuration().Configure("lite-nhibernate.cfg.xml")
.BuildSessionFactory().OpenSession());

Beim Blick in lite-nhibernate.cfg.xml fiel es mir dann wie Schuppen von den Augen. Die fettgedruckte Zeile fehlte: 

<mapping assembly="SpeakFriend.Utilities" />
<mapping assembly="ConceptOffice.Core" />
<mapping assembly="ConceptOffice.Core.Client" />

Fazit: Nur nicht aufgeben, man findet den Fehler immer irgendwann.

Tags:

NHibernate

Cache + NHibernate.HibernateException: Illegal attempt to associate a collection with two open sessions + NUnit

by Oliver Tue, April 07 2009 18:55

In unserem Projekt verwenden wir einen Cache zum Vorhalten von oft benutzten Daten, u.a. für Länder und Bundesländer. Für die Übersetzungen derselben in viele Sprachen habe ich einfach je eine einfache Tabelle angelegt mit einer Referenz auf das jeweilige (Bundes-)Land, dem Sprachen-Isocode und dem Namen in der entsprechenden Sprache. Per NHibernate-Mapping dann die Übersetzungen als <bag> an das (Bundes-)Land angeheftet - fertig.

Beim NUnit-Testen bekam ich nach dem Einbau der Übersetzungen beim Update von (Bundes-)Ländern allerdings diesen Fehler:

NHibernate.HibernateException: Illegal attempt to associate a collection with two open sessions.

Offensichtlich waren die neuen Collections an den Domainobjekten Schuld. Was war passiert? Beim Aufrufen der Frontend-Seiten kam dieser Fehler nicht...

Nun, die Exception gibt uns einen guten Tipp: Wir haben zwei offene Session, mit denen die Collection von Übersetzungen assoziiert werden soll. Aber warum nur in den Tests und warum dort überhaupt?

Eine erste Erklärung: Im Frontend benutzen wir für die NHibernate-Sessions das One-Session-Per-Request-Pattern, so dass alle Manipulationen an den Session-Objekten (inkl. Update) innerhalb derselben Session passieren. Außerdem funktionierte dort das Dispose der Session zuverlässig. In unseren Tests war das ja leider nicht der Fall, so dass wir also irgendwie eine geöffnete Session nicht schließen. Durch die Benutzung eines Caches kamen wir in die Verlegenheit, Länderobjekte in einem Testcase in den Cache zu schreiben und diese in einem folgenden Testcase wiederzuverwenden. Als an dieser zweiten Stelle ein Update passieren sollte, knallte es, denn das Objekt im Cache hielt eine Referenz auf NHibernate-Collection, die noch mit einer vergangenen Session assoziiert war. Beim Update versucht NHibernate aber, dieselbe Collection an die aktuelle Session zu binden, was eine Exception wirft.

Der Cache war also Schuld. Oder zumindest war das mein erster Gedanke. Und tatsächlich: Wenn ich im [SetUp] der NUnit-TestFixture ein Cache.Clear() aufrufe, verschwindet der Fehler! Keine Überraschung, denn jetzt benutzen wir ja keine Objekte aus "alten" Sessions mehr. Aber Moment mal, im Frontend benutzen wir doch denselben Cache?! Und da gibt es diesen Fehler nicht.

Nach ein wenig Haareraufen, kam ich dann zurück zu der Feststellung, dass es ja ein Problem mit einer offenen Session gab. Also setzte ich da nochmal an.

Für das Lifecycle-Management unserer Serviceklassen benutzen einen Autofac-IoC-Container. Dieser stellt auch die NHibernat-Session bereit. Zum Benutzen der Services haben wir eine ServiceLocator-Klasse, die uns die vom Container generierten Serviceinstanzen auf Anfrage zurückgibt. Um diese Architektur in den NUnit-Tests zu nutzen, schrieben wir flugs eine Basisklasse BaseTest, von der alle Testklassen ableiten und fortan Zugriff auf alle Services haben. Das Lifecycle-Management des Containers wird dort ebenfalls verwaltet.

        public BaseTest()
        {
            InitializeContainer();
        }

        ~BaseTest()
        {
            DisposeContainer();
        }

Wie sich nun herausstellt, eine dumme Idee! Beim Debuggen stellte ich fest, dass der Konstruktor zwar von jeder TestFixture (also jeder Testklasse, die selbst mehrere Tests enthält) in Reihenfolge aufgerufen wird, dass aber der Destruktor einer Testklasse keineswegs zuverlässig vor dem Konstruktor der nächsten Testklasse ausgeführt wird! Damit wird der Container der ersten Testklasse nicht Dispose'd und in Folge die NHibernate-Session nicht geschlossen, so dass wir den o.g. Fehler bekommen.

Wahrscheinlich ist das Ausführen von Businesslogik in einem NUnit-TestFixture-Konstruktor bzw. viel schlimmer im Destruktor ein absolutes NoNo. Dafür gibt es schließlich die Attribute [TestFixtureSetUp] und [TestFixtureTearDown]. Mit dem folgenden Code funktioniert denn auch alles wie gewollt:

        [TestFixtureSetUp]
        public virtual void TestFixtureSetUp()
        {
            InitializeContainer();
        }

        [TestFixtureTearDown]
        public virtual void TestFixtureTearDown()
        {
            DisposeContainer();
        }

Ein kleiner Wehrmutstropfen bei dieser Lösung ist, dass man beim Testklassen-Schreiben jetzt daran denken muss, diese Methoden zu überschreiben, wenn man weitere Funktionalität in [TestFixtureSetUp] und/oder [TestFixtureTearDown] benötigt...

Alles Gute, Oliver

Tags:

ASP.NET | NHibernate

NHibernate: spannende Fehlermeldung für ein NULL-DateTime

by Oliver Sat, March 21 2009 22:13

Heute war mal wieder NHibernate-Prime-Time: der Bildupload und das Löschen im Adminbereich funktionierten für einige Campingplätze problemlos, für andere gab es beim Aufruf von Session.Flush() bzw. jetzt Transaction.Commit() folgenden Fehler:

 

Hm, was mag das wohl für ein Fehler sein?

Ein Blick in die InnerException sollte uns mehr verraten:

Google anschmeißen und nach "Connection has been closed by peer" suchen... ein paar Seiten lesen... aber eine Antwort fehlt. Aus Verlegenheit noch ein paarmal probieren - und siehe da, irgendwie habe ich eine neue innere Ausnahme bekommen, die mir mehr sagt:

Wir haben es mit einem nicht initialisierten, not-nullable DateTime-Wert zu tun. Nur wo finden wir ihn? Das zu löscchen Image-Objekt zeigt keine Auffälligkeiten.

Der Profiler bringt's dann an's Licht: es ist der zugehörige Campingplatz, der aus bisher unerfindlichen Gründen in der DB ein NULL als DateModified zu stehen hat. Wie das dorthin gekommen ist, muss an dieser Stelle leider unbeantwortet bleiben. Aber ein beherztes SQL-Skriptlein

UPDATE Campsites SET DateModified = DateCreated WHERE DateModified IS NULL

macht alles wieder ganz. Jetzt gibt's auch keine Exception in unseren Batch-Queries mehr Laughing

Oli

Tags:

NHibernate | Web Applikationen

NHibernate: wundersame automatische Updates

by Oliver Fri, February 27 2009 16:48

Kurze Suche, etwas überzogener Artikel (hier), aber die Kerninformation ist wichtig:

Eigene Enumerationen sollten im Mapping-File nicht auf Int gemappt werden, denn ansonsten muss NHibernate die casten und das entsprechende Objekt wird "dirty", was zu dem wundersamen UPDATE führt.

Bei uns z.B. in der Translation-Klasse der Fall:

...
<property name="Type" column="Type" type="int" not-null="true" />
...
<property name="MetaType" column="MetaType" type="int" not-null="true" />
...

 

Stattdessen also immer die Enumeration-Klasse angeben:

...
<property name="Type" column="Type" type="TranslationType" not-null="true" />
...
<property name="MetaType" column="MetaType" type="MetaType" not-null="true" />
...

Jetzt gibt's keine ungewollten UPDATEs mehr. Schön.

Tags:

NHibernate

NHibernate Criteria API zum Knutschen

by Robert Tue, September 25 2007 14:26

public RequestVisitedList GetBy(int userId, List<int> requestIds)
{
Disjunction disjunctionRequests
= new Disjunction();
foreach (int requestId in requestIds)
disjunctionRequests.Add(
Expression.Eq(
"RequestId", requestId));

return new RequestVisitedList(
_session.CreateCriteria(
typeof(RequestVisited))
.Add(Expression.Eq(
"UserId", userId))
.Add(disjunctionRequests)
.List
<RequestVisited>()
);
}

Der Code oben gibt eine Liste von allen Aufrufen einer Anfrage für eine bestimmten Benutzer zurück, eigentlich nichts Besonderes. Das großartige ist, das das Ganze in 3 min geschrieben war. Keine Stored Procedures, keine Strings zusammen bauen, einfach nur mit IntelliSense bewaffnet einen Abfrage Wunsch beschreiben und fertig ist das Ganze.

Ein hübscher Nebeneffekt, der Code ist Datenbank unabhängig.

Noch schöner wäre das Ganze wenn es für NHibernate schon einen LINQ Provider gäbe, denn soweit ich das Überblicke ist nur MS-SQL zurzeit voll unterstützt, alle anderen LINQ Provider stecken wohl noch in den Kinderschuhen.

Tags:

NHibernate

((Research + Development) - Sleep) > 24

by Robert Tue, September 04 2007 04:53

Cooler Blog Titel, zu lesen gibt es den low frequency feed hier. Haupthema zur Zeit ist Linq für NHibernate.

Tags:

NHibernate

Nur die CLR 2.0 und Du

by Robert Fri, May 18 2007 17:18

Oren Eini stellt eine interessante Frage."Du hast nur die CLR und must ein komplexe Anwendung bauen. Aus projektpolitischen Gründen ist es nicht erlaubt externe Bibliotheken zu verwenden - egal ob Open Source (OSS) oder von Microsoft. Kannst Du eine Anwendung auf diese Art und Weise bauen?".

Oren verneint die Frage rhetorisch. Die Entwicklung eines einfachen IoC schätzt er auf 2 Tage, die Entwicklung einer rudimentären OR/M Schicht auf eine Woche. Ersteres empfinde ich dank Activator.CreateInstance() als einfach. Eine OR/M Schicht dürfte nach einer Woche kritisch zu beurteilen sein.

Ob objektrelationales Mapping zwinged erforderlich ist sei dahin gestellt. ADO.NET ist mächtig. Objekte lassen sich mit eine DataReader händisch befüllen. Mit Hilfe einer trivialen Template Engine ist auch auf diese Art und Weise eine beachtliche Entwicklungsgeschwindigkeit bei hoher Codequalität zu erreichen. Inversion of Control sollte nicht für alles verwendet werden. Für mich ist es eher eine mögliche Refaktorisierung, die bei Bedarf durchgeführt werden sollte. Der eigentlich Punkt den Oren heraustellt ist, wie wesentlich Bibliotheken die tägliche Arbeit beeinflussen, verbessern und ermöglichen. Gelobt seien HHibernate, Castle, NUnit, NLog und all die vielen anderen von AJAX.NET bis #ziplib :-)

Tags:

.NET | Castle | NHibernate | Software development