Archiv

Artikel getaggt mit ‘ORM’

NHibernate: Resultate Transformieren mit DistinctRootEntityResultTransformer

30. August 2011 5 Kommentare

Ich hatte vor einiger Zeit ein recht mühsames Problem: Obwohl ich Objekte nach deren Id (Primärschlüssel) aus der DB geholt habe sind diese mehrmals in meinem Resultat erschienen. Wie so oft war der Fehler eigentlich eine Kleinigkeit, doch solange man gar nicht auf die Idee kommt an der richtigen Stelle danach zu suchen steht man vor einem grossen Mysterium.

 
Ausgangslage
Für das stark vereinfachte Beispiel nutze ich die 3 Klassen Order, OrderItem und Product. Die Objekte dienen nur zum ablegen der Daten und verfügen über keine Geschäftslogik. Das Feld Id ist jeweils der Primärschlüssel der gleichnamigen Tabellen. OrderItem ist sowohl mit Product wie auch mit Order verbunden.

 
Ein Test schlägt fehl
Mit dem untenstehenden Test werden die nötigen Objekte angelegt und danach versucht die Order anhand der Id zu laden.

[TestMethod]
public void ReproduceTheProblem()
{
    using (ISession session = PersistenceManager.OpenSession())
    {
        // Arrange
        Order order = new Order { Number = "000001" };
        AddDataToOrder(session, order);

        // Act
        List<Order> orders = GetOrderById(session, order.Id);

        // Assert
        Assert.AreEqual(1, orders.Count);
        // ==> Assert.AreEqual failed. Expected:<1>. Actual:<2>.
    }
}

private static List<Order> GetOrderById(ISession session, int id)
{
    var result = session.CreateCriteria(typeof(Order))
                        .Add(Expression.Eq("Id", id))
                        .List<Order>();

    return result.ToList();
}

Das Resultat in diesem Test ist allerdings nicht wie erwartet eine 1, sondern eine 2. Schaut man sich das generierte SQL-Query an kann man auch erkennen was das Problem ist:

SELECT 
	this_.Id as Id1_1_, 
	this_.Number as Number1_1_, 
	items2_.OrderId as OrderId3_, 
	items2_.Id as Id3_, 
	items2_.Id as Id2_0_, 
	items2_.OrderId as OrderId2_0_, 
	items2_.Quantity as Quantity2_0_, 
	items2_.ProductId as ProductId2_0_ 
FROM dbo.[Order] this_ 
left outer join dbo.OrderItem items2_ on this_.Id=items2_.OrderId 
WHERE this_.Id = 3;

Hier wird nicht einfach nur ein SELECT gemacht, sondern das Resultat wird noch mit einem JOIN verknüpft. Meine Erwartung war das nur eine Zeile mit den Daten für das von mir gewünschte Objekt zurück geliefert wird. Durch den JOIN werden nun aber auch alle dazugehörigen OrderItems geladen:

 
Ursache
Der JOIN wurde von NHibernate nicht einfach aus lauter Freude gemacht. Die dafür nötige Anweisung stand so im Mapping:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
                   assembly="NHDistinct" namespace="NHDistinct.Model">
  <class name="Order" table="`Order`" schema="dbo">
    <id name="Id">
      <generator class="identity" />
    </id>
    <property name="Number" />
    <bag name="Items" cascade="all-delete-orphan" inverse="true" fetch="join">
      <key column="OrderId"/>
      <one-to-many class="NHDistinct.Model.OrderItem"/>
    </bag>
  </class>
</hibernate-mapping>

Diese explizite Schreibweise hat den gleichen Effekt als würde man die Funktion zum holen der Daten so umschreiben:

private static List<Order> GetOrderById(ISession session, int id)
{
    var result = session.CreateCriteria(typeof(Order))
                        .Add(Expression.Eq("Id", id))
                        .SetFetchMode("Items", FetchMode.Join)
                        .List<Order>();

    return result.ToList();
}

Obwohl das Ergebnis gleich ist, sieht man so auf den ersten Blick das ein wenig mehr Daten kommen werden als man als Nutzer der Methode vermuten würde. (Klare Methodennamen wären wie so oft eine grosse Hilfe gewesen).

 
Lösung
Das Mapping durfte nicht verändert werden, da das so erzwungene Verhalten fürs gesamte Projekt gesehen Sinn machte. Auch war ein erzwungenes nicht laden der OrderItems für die weitere Verarbeitung ungünstig. Nach einigem Suchen wurde die Funktion schliesslich um eine Zeile erweitert:

private static List<Order> GetOrderByIdFixed(ISession session, int id)
{
    var result = session.CreateCriteria(typeof(Order))
                        .Add(Expression.Eq("Id", id))
                        .SetFetchMode("Items", FetchMode.Join)
    /*  NEU: ===>  */   .SetResultTransformer(new DistinctRootEntityResultTransformer()) 
                        .List<Order>();

    return result.ToList();
}

DistinctRootEntityResultTransformer nimmt das Resultat der Abfrage und transformiert dieses wieder in die Root Entitäten. NHibernate packt auch ohne diese Zeile die OrderItems ins Order-Objekt, so aber merkt es dass es nur einen Order gibt und liefert entsprechend auch nur noch eines zurück.

 
Fazit
Wenn man mit OR-Mappern arbeitet sollte man bei der Entwicklung immer einen Blick auf die generierten Abfragen werfen. Meistens macht es das Richtige aber für den kleinen Spezialfall den man nun gerade braucht gibt es halt ab und zu ein klein wenig Nacharbeit. Was wieder mal zeigt: Trotz OR-Mappern sollte man als Entwickler doch ein wenig Ahnung von SQL haben.

 
Danksagung
Ich möchte hier noch Patrick Weibel danken. Ich durfte für den Blogpost seine Klasse PersistenceManager.cs aus dem ORM-Vortrag bei der .Net User Group Bern verwenden. Die Klasse zusammen mit den zahlreichen Mappings zum Nachschauen hat mir ermöglicht ein Minimal-Beispiel zusammen zu stellen, das man fürs selber experimentieren auf BitBucket herunterladen kann.

Kategorien:.Net Schlagworte: , ,

Buch-Rezension zu “Programming Entity Framework“

31. Mai 2011 1 Kommentar

Programming Entity Framework“ von Julia Lerman erschien im August 2010 in der 2. Ausgabe bei O’Reilly. Diese Ausgabe wurde für Entity Framework 4 komplett überarbeitet. Will man mit EF 4 arbeiten sollte man also unbedingt schauen das man diese Ausgabe kauft.

 

Ich bin jeweils ein wenig skeptisch wenn ich ein Buch mit über 700 Seiten sehe. Zu oft reicht das Wissen der Autoren nur für rund 500 Seiten und der Rest ist dann eine mehr oder weniger gekonnte Wiederholung. Dies ist hier aber definitiv nicht der Fall!

Julia Lerman gilt zu Recht als DIE Expertin zu Entity Framework ausserhalb von Microsoft. Die Teile zu EF sind sehr fundiert und liefern auch einen sehr guten Einblick in die Entstehungsgeschichte des neusten OR-Mappers von Microsoft.

 
Mehr als nur Entity Framework
Das Buch behandelt alle Teile die man zum Verstehen von Entity Framework braucht. Es beginnt mit dem Entity Data Model und den Abfragemöglichkeiten mit LINQ to Entities oder Entity SQL. Es folgen Tipps und Trick wie man mit dem EDM Designer arbeitet und wo man ihn umgehen muss. Ob man Self-Tracking Entities oder POCOs nutzen will, man findet für beide Ansätze ausführliche Anleitungen. Kapitel zu Transaktionen oder dem Mappen von Stored Procedures fehlen ebenso wenig wie die Einbindung von realitätsnahen (und damit komplexen) DB-Schemas in Entity Framework. Das Buch lässt diesbezüglich keine Wünsche offen.

Am meisten beeindruckte mich aber wie der konkrete Einsatz von EF erklärt wird. Ob man Windows Forms, WPF, WCF oder ASP.Net nutzt – für alle diese Technologien gibt es mindestens ein Kapitel das einem zeigt worauf man achten muss. Man ist so in der Lage EF in seinem eigenen Projekt zu nutzen ohne noch viele zusätzliche Informationen suchen zu müssen.

Mit seinem Umfang an Informationen ist „Programming Entity Framework“ auch sehr gut als Nachschlagewerk geeignet. Ein ausführlicher und gut gepflegter Index hilft beim Finden der passenden Stelle.

 
Kleine Kritikpunkte
Es gibt aber auch bei dem Buch noch einige wenige Verbesserungsmöglichkeiten. Die meisten Bilder im Kapitel 14 sind um 1-2 Nummern verschoben. Dies stört beim Lesen doch recht stark, da man vergeblich versucht das gerade erklärte auf dem Bild zu finden. Auch die Strukturierung könnte man bei einer 3. Ausgabe wohl noch ein wenig optimieren. Etliche der Verweise auf eine spätere Erklärung des Themas würden so entfallen.

 
Fazit
Wer sich mit Entity Framework beschäftigen will sollte sich dieses Buch unbedingt kaufen. Vor allem die Kapitel die auf die Nutzung von EF mit den einzelnen Microsoft Technologien (wie WPF, WCF, Windows Forms oder ASP.Net) eingehen sind eine sehr grosse Hilfe bei der täglichen Arbeit.

 
Zum Buch
Programming Entity Framework“ von Julia Lerman, 2010 O’Reilly, ISBN 978-0-596-80726-9, 912 Seiten, Englisch

Follow

Bekomme jeden neuen Artikel in deinen Posteingang.

Join 142 other followers