NHibernate - Kurzanleitung
In diesem Kapitel werden wir diskutieren, was NHibernate ist, welche Plattformen es implementieren kann, welche Vorteile es hat und welche anderen Aspekte damit verbunden sind.
Was ist NHibernate?
NHibernate ist ein ausgereifter, objektbezogener Open Source-Mapper für das .NET-Framework. Es wird aktiv entwickelt, voll funktionsfähig und in Tausenden von erfolgreichen Projekten verwendet. Es ist darauf aufgebautADO.NET und die aktuelle Version ist NHibernate 4.0.4.
NHibernate ist ein objektrelationaler Open-Source-Mapper für .NET und wird unter der GNU Lesser General Public License.
Es basiert auf Hibernate, einem beliebten objektrelationalen Java-Mapper, und verfügt über eine sehr ausgereifte und aktive Codebasis.
Es bietet einen Rahmen für die Zuordnung eines objektorientierten Domänenmodells zu einer herkömmlichen relationalen Datenbank.
NHibernate wurde von gestartet Tom Barrett und dieses Projekt gibt es seit Februar 2003, was ihr erstes Engagement war.
Es ist ein großes Projekt und bietet viele Funktionen.
Da ist ein NuGet package verfügbar, was das Hinzufügen zu einem Projekt sehr einfach macht.
Warum NHibernate?
Jetzt ist die Frage, warum wir brauchen object-relational mappers? Es liegt daran, dass es eine Trennung zwischen der Objektwelt und der relationalen Welt gibt.
In der Objektwelt basiert alles auf objects;; Wir nannten Objekte jene Dinge, die unsere Daten haben.
Die relationale Welt ist alle satzbasiert und wir haben es mit Tabellen und Zeilen zu tun, die sich von der Objektwelt unterscheiden.
In der Objektwelt haben wir unidirectional associations. Wenn ein Kunde einen Zeiger auf eine Bestellung hat, bedeutet dies nicht unbedingt, dass eine Bestellung einen Zeiger zurück auf einen Kunden hat, möglicherweise oder nicht.
In der relationalen Welt sind alle Assoziationen bidirectional und es kann durch einen Fremdschlüssel erfolgen.
Alle Assoziationen sind von Natur aus bidirektional. Wenn wir uns also mit objektrelationaler Zuordnung befassen, müssen wir uns auch mit dieser Trennung befassen.
In der Objektwelt arbeiten wir mit Zeigern, die unidirektional sind, während wir in der relationalen Welt Fremdschlüssel haben, die von Natur aus bidirektional sind.
Die Objektwelt hat diesen Begriff der Vererbung, bei dem ein Fahrzeug eine Reihe verschiedener Unterklassen haben kann, so dass ein Auto ein Fahrzeugtyp ist, ein Boot ein Fahrzeugtyp ist und ein Sportwagen ein Fahrzeugtyp ist, diese Arten von Vererbungsbeziehungen.
Die relationale Welt hat diesen Begriff der Vererbung nicht.
Kartierung
Wie ordnen wir all dies zu? disjoint relationships?Dieses Mapping-Konzept stammt aus dem objektrelationalen Mapper. Es gibt hauptsächlich drei Dinge zu verstehen, wie in der folgenden Abbildung gezeigt.
In Ihrer Anwendung benötigen Sie Klassendefinitionen, bei denen es sich normalerweise um C # -Code und den .NET-Code handelt, der unsere Klassen darstellt, z. B. Mitarbeiterklasse, Kundenklasse, Auftragsklasse usw.
Unten sehen Sie ein Datenbankschema, das unser ist Data Definition Language in einer relationalen Datenbank, die angibt, wie eine Kundentabelle aussieht, wie eine Mitarbeitertabelle aussieht.
Dazwischen befinden sich die Mapping-Metadaten, die dem objektrelationalen Mapper mitteilen, wie er in Zeilen und Spalten sowie Fremdschlüsselbeziehungen von der Objektwelt in C # in die Datenbankwelt übersetzen soll.
Diese Zuordnungsmetadaten können auf verschiedene Arten dargestellt werden, und wir werden eine Reihe dieser verschiedenen Arten untersuchen, die für NHibernate-Anwendungen am typischsten sind.
Es wird vertreten durch HBM (Hibernate Mapping) Dateien, die XML-Dateien sind.
Datenbank unterstützt
NHibernate unterstützt eine Vielzahl unterschiedlicher Datenbanken. Auf jede vorhandene relationale Datenbank kann auf NHibernate zugegriffen werden.
SQL Server ist die primär unterstützte Datenbank. Dies ist das, was die meisten Entwickler während der Entwicklung verwenden. Es ist wahrscheinlich die häufigste.
Es auch works very well with Oracle.
Es unterstützt auch DB2, Firebird, MySQL, PostgreSQL und SQL Lite
Es hat auch ODBC and OLEDB drivers.
Heutzutage sind viele Systeme mit einer mehrschichtigen Architektur entworfen, NHibernate hat sie auch und funktioniert perfekt mit diesem Design.
Schichtarchitektur
Eine geschichtete Architektur unterteilt ein System in mehrere Gruppen, wobei jede Gruppe Code enthält, der einen bestimmten Problembereich adressiert, und diese Gruppen als Schichten bezeichnet werden. Die meisten Anwendungen auf Unternehmensebene verwendenhigh-level application architecture das besteht aus drei Schichten -
- Die Präsentationsschicht
- Die Business-Schicht
- Die Persistenzschicht
Beispielsweise kann eine Benutzeroberflächenschicht, die auch als Präsentationsschicht bezeichnet wird, den gesamten Anwendungscode zum Erstellen von Webseiten und zum Verarbeiten von Benutzereingaben enthalten.
Ein Hauptvorteil des Layering-Ansatzes besteht darin, dass Sie häufig Änderungen an einer Ebene vornehmen können, ohne die anderen Ebenen wesentlich zu stören, wodurch die Systeme erstellt werden lesser fragile and more maintainable.
Präsentationsfolie
Es ist die oberste Ebene, die den Code enthält, der für das Zeichnen der Benutzeroberfläche, Seiten, Dialoge oder Bildschirme, das Sammeln von Benutzereingaben und das Steuern der Navigation verantwortlich ist.
Geschäftsschicht
Die Geschäftsschicht ist für die Implementierung aller Geschäftsregeln oder Systemanforderungen verantwortlich, die Benutzer als Teil der Problemdomäne verstehen würden.
Außerdem wird das durch die Persistenzschicht definierte Modell wiederverwendet.
Persistenzschicht
Die Persistenzschicht besteht aus Klassen und Komponenten, die für das Speichern und Abrufen von Anwendungsdaten verantwortlich sind.
Diese Ebene definiert auch eine Zuordnung zwischen der Modellklasse und der Datenbank. NHibernate wird hauptsächlich in dieser Schicht verwendet.
Datenbank
- Die Datenbank existiert außerhalb der .NET-Anwendung.
- Es ist die tatsächliche, dauerhafte Darstellung des Systemstatus.
- Wenn eine SQL-Datenbank verwendet wird, enthält die Datenbank das relationale Schema und möglicherweise gespeicherte Prozeduren.
Hilfs- / Dienstprogrammklassen
Jede Anwendung verfügt über eine Reihe von Hilfs- oder Dienstprogrammklassen, die die anderen Ebenen unterstützen: z. B. UI-Widgets, Messaging-Klassen, Ausnahmeklassen und Protokollierungsdienstprogramme.
Diese Elemente werden nicht als Ebenen betrachtet, da sie den Regeln für die Abhängigkeit zwischen Schichten in einer Ebenenarchitektur nicht entsprechen.
NHibernate Architektur
Es ist eine allgemeine Ansicht der NHibernate-Anwendung, und Sie können auch die einfache NHibernate-Architektur sehen.
Der Anwendungscode verwendet das NHibernate ISession und IQuery APIs für Persistenzoperationen und müssen nur Datenbanktransaktionen verwalten, idealerweise mithilfe von NHibernate ITransaction API.
Bevor wir NHibernate wirklich einsetzen können, müssen wir die Grundlage verstehen, auf der es aufgebaut ist. NHibernate ist eine Persistenztechnologie, die auf der Idee des objektrelationalen Mappings oder ORM basiert.
Was ist ORM?
Object-Relational Mapping (ORM) ist a programming techniquezum Konvertieren von Daten zwischen inkompatiblen Typsystemen in objektorientierten Programmiersprachen. Mit anderen Worten, es ist das Konzept, die Geschäftsobjekte einer Anwendung relationalen Datenbanktabellen zuzuordnen, so dass auf die Daten über das Objektmodell einer Anwendung leicht zugegriffen und diese vollständig aktualisiert werden können.
Wie Sie bereits wissen, bieten relationale Datenbanken ein gutes Mittel zum Speichern von Daten, während die objektorientierte Programmierung ein guter Ansatz zum Erstellen komplexer Anwendungen ist.
NHibernate und ORM sind im Allgemeinen am relevantesten für Anwendungen mit nicht trivialer Geschäftslogik, dem Domänenmodell und einer Art Datenbank.
Mit ORM ist es sehr einfach, eine Übersetzungsebene zu erstellen, mit der Objekte problemlos in relationale Daten und wieder zurück umgewandelt werden können.
Das Akronym ORM kann auch Objektrollenmodellierung bedeuten. Dieser Begriff wurde erfunden, bevor die Objekt- / relationale Zuordnung relevant wurde.
Es beschreibt eine Methode zur Informationsanalyse, die bei der Datenbankmodellierung verwendet wird.
Warum ORM?
ORM ist ein framework Auf diese Weise können Sie die Welt der Objekte in objektorientierten Sprachen Zeilen in relationalen Tabellen in relationalen Datenbanken zuordnen
Um dieses Konzept zu verstehen, schauen wir uns das folgende Diagramm an.
In der obigen Abbildung sehen Sie, dass wir auf der rechten Seite eine Tabelle mit dem Namen "Mitarbeiter" haben, die Spalten mit jedem Datenelement enthält, das einem einzelnen Mitarbeiter zugeordnet ist.
Wir haben eine Spalte für eine ID, die jeden Mitarbeiter eindeutig identifiziert.
Eine Spalte für den Namen des Mitarbeiters, eine weitere Spalte für das Beitrittsdatum und schließlich eine Spalte mit dem Alter eines Mitarbeiters.
Wenn wir Code schreiben wollten, um einen neuen Mitarbeiter in den Tabellen zu speichern, ist das nicht so einfach.
In der obigen Abbildung sehen Sie auch, dass wir ein Mitarbeiterobjekt haben, das Felder für ID, Name, Beitrittsdatum und Alter enthält.
Ohne ORM müssen wir dieses Objekt in einige verschiedene SQL-Anweisungen übersetzen, die die Mitarbeiterdaten in die Mitarbeitertabelle einfügen.
Das Schreiben von Code zum Erstellen des SQL für das obige Szenario ist also nicht so schwierig, aber es ist etwas mühsam und ziemlich leicht, sich zu irren.
Mit einem ORM wie NHibernate können wir deklarieren, wie bestimmte Klassen relationalen Tabellen zugeordnet werden sollen, und ORM oder NHibernate den unangenehmen Job des Erstellens der SQL zum Einfügen, Aktualisieren, Löschen in Abfragedaten in unserer Mitarbeitertabelle erledigen lassen.
Auf diese Weise können wir unseren Code auf die Verwendung von Objekten konzentrieren und diese Objekte automatisch in relationale Tabellen übersetzen lassen.
Ein ORM erspart uns also wirklich das manuelle Zuordnen von Objekten zu Tabellen.
Um mit NHibernate arbeiten zu können, benötigen wir Visual Studio und das NHibernate-Paket.
Visual Studio-Installation
Microsoft bietet eine free version von Visual Studio, das auch enthält SQL Server und es kann von heruntergeladen werden https://www.visualstudio.com Im Folgenden finden Sie die Schritte für die Installation.
Step 1 - Sobald der Download abgeschlossen ist, führen Sie das Installationsprogramm aus. Das folgende Dialogfeld wird angezeigt.
Step 2 - Klicken Sie auf die Schaltfläche Installieren und der Installationsvorgang wird gestartet.
Step 3 - Nach erfolgreichem Abschluss des Installationsvorgangs wird das folgende Dialogfeld angezeigt.
Step 4 - Schließen Sie dieses Dialogfeld und starten Sie Ihren Computer bei Bedarf neu.
Step 5- Öffnen Sie nun Visual Studio über das Startmenü, um den folgenden Dialog zu öffnen. Die Vorbereitung dauert zum ersten Mal einige Zeit.
Step 6 - Sobald dies alles erledigt ist, sehen Sie das Hauptfenster von Visual Studio.
Installation des NHibernate-Pakets
NHibernate ist ein ausgereifter, objektbezogener Open Source-Mapper für das .NET-Framework. Es wird aktiv entwickelt, voll ausgestattet und in Tausenden von erfolgreichen Projekten eingesetzt. Sie können das NHibernate-Paket mit den folgenden Methoden installieren.
Direkter Download
Laden Sie die Zip-Datei von herunter https://sourceforge.net/ Hier finden Sie alle erforderlichen Binärdateien.
Extrahieren Sie diese Zip-Datei und fügen Sie alle diese Binärdateien in Ihr Projekt ein.
Installation mit NuGet
Eine andere Möglichkeit, NHibernate zu installieren, besteht darin, das NHibernate-Paket mit NuGet zu installieren. Dies ist bei weitem die einfachste Möglichkeit, NHibernate in ein Projekt zu integrieren.
Es werden alle NHibernate-Abhängigkeiten heruntergeladen und Verweise auf alle erforderlichen Assemblys erstellt.
Führen Sie zum Installieren von NHibernate den folgenden Befehl in der Package Manager-Konsole aus.
install-package NHibernate
Sie können jetzt Ihre Bewerbung starten.
In diesem Kapitel erfahren Sie, wie Sie ein einfaches Beispiel mit NHibernate starten. Wir werden eine bauensimple console application. Um eine Konsolenanwendung zu erstellen, verwenden wir Visual Studio 2015, das alle Funktionen enthält, die Sie zum Erstellen benötigen. Testen Sie Ihre Anwendung mit dem NHibernate-Paket.
Im Folgenden finden Sie die Schritte zum Erstellen eines Projekts mit den in Visual Studio verfügbaren Projektvorlagen.
Step 1 - Öffnen Sie das Visual Studio und klicken Sie auf die Menüoption Datei → Neu → Projekt.
Step 2 - Ein neuer Projektdialog wird geöffnet.
Step 3 - Wählen Sie im linken Bereich Vorlagen → Visual C # → Windows.
Step 4 - Wählen Sie im mittleren Bereich Konsolenanwendung aus.
Step 5 - Geben Sie den Projektnamen 'NHibernateDemoApp' in das Feld Name ein und klicken Sie auf OK, um fortzufahren.
Step 6 - Sobald das Projekt von Visual Studio erstellt wurde, werden im Projektmappen-Explorer eine Reihe von Dateien angezeigt.
Da Sie wissen, dass wir ein einfaches Konsolenanwendungsprojekt erstellt haben, müssen wir jetzt das NHibernate-Paket in unser Konsolenprojekt aufnehmen.
Gehen Sie zum Menü Extras und wählen Sie NuGet Package Manager → Package Manager Console. Das Fenster Package Manager Console wird geöffnet.
Geben Sie den oben gezeigten Befehl an Package Manager ConsoleWenn Sie das Fenster drücken und die Eingabetaste drücken, werden alle NHibernate-Abhängigkeiten heruntergeladen und Verweise auf alle erforderlichen Assemblys erstellt. Nach Abschluss der Installation wird die folgende Meldung angezeigt.
Nachdem wir NHibernate hinzugefügt haben, können wir jetzt mit der Implementierung beginnen. Wir beginnen also mit der Abbildung eines sehr einfachentable namens Student, der einfach einen ganzzahligen Primärschlüssel namens ID und eine Spalte Vorname und Nachname enthält.
Wir benötigen eine Klasse, um diesen Schüler darzustellen. Erstellen Sie also eine neue Klasse mit dem Namen Schüler, indem Sie im Lösungs-Explorer mit der rechten Maustaste auf das Projekt klicken und dann Hinzufügen → Klasse auswählen, um das Dialogfeld Neues Element hinzufügen zu öffnen.
Eingeben Student.csKlicken Sie im Feld Name auf die Schaltfläche Hinzufügen. In dieser Schülerklasse benötigen wir unseren ganzzahligen Primärschlüssel ID und diese Zeichenfolge.FirstName und LastName Felder wie in der folgenden vollständigen Implementierung der Schülerklasse gezeigt.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NHibernateDemoApp {
class Student {
public virtual int ID { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstMidName { get; set; }
}
}
Wenn Sie mit Modellen in NHibernate-Anwendungen arbeiten, ist es am einfachsten, alle Ihre Felder virtuell zu gestalten. Dies ist also unser einfaches NHibernate-Modell, das wir verwenden und der Back-End-Datenbank zuordnen werden.
Gehen wir nun zur Main-Methode in der Program-Klasse und erstellen ein neues NHibernate-Konfigurationsobjekt.
Das erste, was wir bereitstellen müssen, ist das connection string. Dies ist eine datenbankspezifische Verbindungszeichenfolge. Der einfachste Weg, die Verbindungszeichenfolge zu finden, besteht darin, mit der rechten Maustaste auf die Datenbank in zu klickenSQL Server Object Explorer und wählen Sie Eigenschaften.
Es öffnet sich das Eigenschaftenfenster, scrollen Sie jetzt nach unten und Sie sehen das Feld Verbindungszeichenfolge im Eigenschaftenfenster.
Kopieren Sie die Verbindungszeichenfolge und geben Sie sie in Ihrem Code an. Es folgt die Implementierung der Main-Methode, bei der die Konfiguration für NHibernate erforderlich ist.
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
//perform database logic
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Nach der Verbindungszeichenfolge müssen wir einen Treiber angeben, nämlich den SQLClientDriver und dann müssen wir ihm auch einen Dialekt geben, welche Version von SQL Server, und wir werden MS SQL 2008 verwenden.
NHibernate weiß jetzt, wie eine Verbindung zur Datenbank hergestellt wird. Das andere, was wir tun müssen, ist, ihm eine Liste der Modelle zur Verfügung zu stellen, die wir abbilden werden.
Wir können dies tun, indem wir eine Baugruppe hinzufügen, also indem wir die angeben Assembly.GetExecutingAssemblyund hier findet das Programm Mapping-Dateien. Zuordnungsdateien teilen NHibernate mit, wie von C # -Klassen in Datenbanktabellen übergegangen werden soll.
SessionFactory kompiliert alle Metadaten, die zum Initialisieren von NHibernate erforderlich sind. Mit SessionFactory können Sitzungen erstellt werden, die in etwa den Datenbankverbindungen entsprechen. Der geeignete Weg ist also, es im using-Block zu verwenden. ich kann sagenvar session gleich sessionFactory.OpenSession und ich werde dies innerhalb seiner Transaktion tun wollen.
Sobald die Sitzung geöffnet ist, können wir die Sitzung anweisen, eine neue Transaktion zu beginnen, und dann hier eine Logik ausführen. Führen Sie also eine Datenbanklogik durch und schreiben Sie diese Transaktion schließlich fest.
In diesem Kapitel werden wir einige behandeln basic mappingund Sie wissen aus dem letzten Kapitel, dass wir die Datenbanktabelle sowie die C # -Klassendefinition haben. Wir benötigen jetzt eine Zuordnung, die erklärt, wie von C # in die Datenbank und wieder zurück übersetzt wird.
Fügen Sie also eine neue XML-Datei hinzu, indem Sie im Lösungs-Explorer mit der rechten Maustaste auf das Projekt klicken und Hinzufügen → Neues Element ... auswählen.
Eingeben Student.hbm.xmlim Namensfeld. Wir müssen eine Standardassembly angeben, die sein wirdNHibernateDemoAppund geben Sie auch einen Standard-Namespace an. Dies verkürzt nur viele der anderen Typdefinitionen, die wir in dieser Datei vornehmen werden.
Es folgt die Implementierung in der XML-Datei -
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "native"/>
</id>
<property name = "LastName"/>
<property name = "FirstMidName"/>
</class>
</hibernate-mapping>
Als nächstes müssen wir eine Klasse definieren; Diese Klasse wird unsere seinStudent class. Als nächstes müssen wir NHibernate den Namen der ID mitteilen, die ID ist, und ich muss NHibernate auch mitteilen, wie IDs generiert werden sollen, damit unser Generator vom Typ native ist.
Der native Typgenerator bedeutet, dass in einer Datenbank wie SQL Server die Identitätsspalte, der Identitätstyp, verwendet wird.
Als nächstes müssen wir die Namen der Eigenschaften angeben. Fügen Sie also zwei weitere Eigenschaften für den Vornamen und den Nachnamen hinzu.
Jetzt lesen wir diese Zuordnungsdateien aus der Assembly. Der bevorzugte Weg, dies zu tun, besteht darin, diese zu habenHBM filesin Ihre Versammlung gebacken. Wir können dies tun, indem wir einfach eine Eigenschaft festlegen.
Klicken Sie nun im Projektmappen-Explorer mit der rechten Maustaste auf das Projekt und wählen Sie Eigenschaften Build Action field in dem der Inhalt standardmäßig ausgewählt ist.
Wählen Sie die eingebettete Ressource aus der Dropdown-Liste aus.
Dadurch wird diese XML-Datei tatsächlich in die Datei eingebettet NHibernateDemoApp Versammlung.
In diesem Kapitel werden wir die Grundlagen behandeln CRUD operations. Nachdem unser System startbereit ist und wir unsere Domain Student-Klasse erfolgreich implementiert haben, haben wir auch die Zuordnungsdateien definiert und NHibernate konfiguriert. Wir können jetzt einige Abfragen verwenden, um CRUD-Operationen auszuführen.
Daten erstellen
Wie Sie sehen, haben wir keine Daten in unserer Schülertabelle in NHibernateDemoDB Datenbank.
Um einige Daten hinzuzufügen, müssen wir das ausführen Add/Create Betrieb wie unten gezeigt.
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var student1 = new Student {
ID = 1,
FirstMidName = "Allan",
LastName = "Bommer"
};
var student2 = new Student {
ID = 2,
FirstMidName = "Jerry",
LastName = "Lewis"
};
session.Save(student1);
session.Save(student2);
tx.Commit();
}
Console.ReadLine();
}
Wie Sie sehen, haben wir zwei Schüler erstellt und rufen dann die Save () -Methode von auf OpenSession und rufen Sie dann das Commit () des auf BeginTransaction. Hier ist die komplette Implementierung inProgram.cs Datei
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var student1 = new Student {
ID = 1,
FirstMidName = "Allan",
LastName = "Bommer"
};
var student2 = new Student {
ID = 2,
FirstMidName = "Jerry",
LastName = "Lewis"
};
session.Save(student1);
session.Save(student2);
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Führen Sie nun diese Anwendung aus, rufen Sie den SQL Server-Objekt-Explorer auf und aktualisieren Sie Ihre Datenbank. Sie werden sehen, dass die beiden oben genannten Schüler jetzt zur Schülertabelle in der NHibernateDemoDB-Datenbank hinzugefügt werden.
Lesen Sie die Daten aus der Schülertabelle
Sie können sehen, dass wir jetzt zwei Datensätze in unserer Schülertabelle haben. Um diese Datensätze aus der Tabelle zu lesen, müssen wir die aufrufenCreateCriteria() von OpenSession wie im folgenden Code gezeigt.
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}",
student.ID,student.FirstMidName, student.LastName);
}
tx.Commit();
}
Console.ReadLine();
}
Wenn Sie also die Liste der Datensätze möchten, können wir einfach die Liste des Typs Student sagen.
Verwenden Sie jetzt die foreach durch alle Schüler und sagen, drucken Sie den Ausweis, FirstMidName und LastNameauf der Konsole. Lassen Sie uns diese Anwendung jetzt erneut ausführen und Sie sehen die folgende Ausgabe im Konsolenfenster.
1 Allan Bommer
2 Jerry Lewis
Sie können jeden Datensatz auch abrufen, indem Sie die ID im Feld angeben Get() OpenSession-Methode mit dem folgenden Code.
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID,
student.FirstMidName, student.LastName);
}
var stdnt = session.Get<Student>(1);
Console.WriteLine("Retrieved by ID");
Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID,
stdnt.FirstMidName, stdnt.LastName);
tx.Commit();
}
Console.ReadLine();
}
Wenn Sie nun Ihre Anwendung ausführen, wird die folgende Ausgabe angezeigt.
1 Allan Bommer
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Datensatz aktualisieren
Um den Datensatz in der Tabelle zu aktualisieren, müssen wir zuerst diesen bestimmten Datensatz abrufen und diesen Datensatz dann durch Aufrufen von aktualisieren Update() OpenSession-Methode wie im folgenden Code gezeigt.
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID,
student.FirstMidName, student.LastName);
}
var stdnt = session.Get<Student>(1);
Console.WriteLine("Retrieved by ID");
Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName);
Console.WriteLine("Update the last name of ID = {0}", stdnt.ID);
stdnt.LastName = "Donald";
session.Update(stdnt);
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID,
student.FirstMidName, student.LastName);
}
tx.Commit();
}
Console.ReadLine();
}
Wenn Sie nun Ihre Anwendung ausführen, wird die folgende Ausgabe angezeigt.
1 Allan Bommer
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Update the last name of ID = 1
Fetch the complete list again
1 Allan Donald
2 Jerry Lewis
Wie Sie sehen können, wird der Nachname der ID gleich 1 von Bommer auf Donald aktualisiert.
Aufzeichnung löschen
Um einen Datensatz aus der Tabelle zu löschen, müssen wir zuerst diesen bestimmten Datensatz abrufen und diesen Datensatz dann durch Aufrufen von löschen Delete() OpenSession-Methode wie im folgenden Code gezeigt.
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID,
student.FirstMidName, student.LastName);
}
var stdnt = session.Get<Student>(1);
Console.WriteLine("Retrieved by ID");
Console.WriteLine("{0} \t{1} \t{2}", stdnt.ID, stdnt.FirstMidName, stdnt.LastName);
Console.WriteLine("Delete the record which has ID = {0}", stdnt.ID);
session.Delete(stdnt);
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
student.LastName);
}
tx.Commit();
}
Console.ReadLine();
}
Wenn Sie nun Ihre Anwendung ausführen, wird die folgende Ausgabe angezeigt.
1 Allan Donald
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Delete the record which has ID = 1
Fetch the complete list again
2 Jerry Lewis
Wie Sie sehen, ist der Datensatz mit der ID 1 nicht mehr in der Datenbank verfügbar. Sie können die Datenbank auch im SQL Server-Objekt-Explorer anzeigen.
In diesem Kapitel werden wir verstehen, wie alle Datensätze aus der Datenbank sind retrieved, updated, created, and deleted und wie genau werden diese Abfragen durchgeführt?
Um all dies zu verstehen, können wir unserer Konfiguration einfach eine Option hinzufügen, die das SQL in der Konsole protokolliert. Hier ist die einfache Anweisung, mit der die SQL-Abfrage protokolliert wird:
x.LogSqlInConsole = true;
Jetzt haben wir zwei Datensätze in unserer Schülertabelle in der NHibernateDemoDB-Datenbank. Lassen Sie uns alle Datensätze aus der Datenbank abrufen, wie im folgenden Code gezeigt.
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
Console.WriteLine("\nFetch the complete list again\n");
var students = session.CreateCriteria<Student>().List<Student>();
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
student.LastName);
}
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Lassen Sie uns diese Anwendung erneut ausführen, und Sie sehen die folgende Ausgabe:
NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,
this_.FirstMidName as FirstMid3_0_0_ FROM Student this_
Fetch the complete list again
3 Allan Bommer
4 Jerry Lewis
Wie Sie sehen können, ist die select clauseWenn es an die Datenbank gesendet wird, ist es tatsächlich wie eine Klausel, die die ID, den Vornamen und den Nachnamen abruft. All dies wird an die Datenbank gesendet und dort verarbeitet, anstatt dass viele Datensätze auf Ihren Server zurückgebracht und auf der Serverseite verarbeitet werden.
NHibernate Profiler
Eine andere Möglichkeit, diese Ergebnisse zu betrachten, ist die Verwendung von NHibernate Profiler. NHibernate Profiler ist ein kommerzielles Tool, aber es ist sehr nützlich für die Arbeit mit NHibernate-Anwendungen. Sie können den NHibernate Profiler einfach über NuGet in Ihre Anwendung installieren.
Wechseln Sie im Menü Extras zur NuGet Manager-Konsole, indem Sie NuGet Package Manager → Package Manager-Konsole auswählen. Das Fenster Package Manager Console wird geöffnet. Geben Sie den folgenden Befehl ein und drücken Sie die Eingabetaste.
PM> install-package NHibernateProfiler
Es werden alle erforderlichen Binärdateien für den NHibernate Profiler installiert. Nach erfolgreicher Installation wird die folgende Meldung angezeigt.
Sie werden auch sehen, dass der NHibernate Profiler nach seiner Installation gestartet wird. Für die Verwendung ist eine Lizenz erforderlich. Für Demozwecke können wir jedoch die 30-Tage-Testversion von NHibernate Profiler verwenden.
Jetzt ist NHibernate Profiler für die Arbeit mit Webanwendungen optimiert und Sie werden sehen, dass es hinzugefügt wurde App_Start folderim Lösungs-Explorer. Um all dies einfach zu halten, löschen Sie den Ordner App_Start. Außerdem werden Sie feststellen, dass am Anfang der Main-Methode in der Program-Klasse eine Anweisung hinzugefügt wird.
App_Start.NHibernateProfilerBootstrapper.PreStart();
Entfernen Sie auch diese Anweisung und fügen Sie einfach einen einfachen Aufruf hinzu NHibernateProfiler.Initialize wie im folgenden Code gezeigt.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()){
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstMidName,
student.LastName);
}
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Wenn Sie die Anwendung ausführen, werden Daten an die NHibernate Profiler-Anwendung gesendet.
Sie können hier sehen, wir haben eine schöne Anzeige, die zeigt, dass wir die Transaktion gestartet haben, was die SQL mit der Datenbank in einem schönen Format macht.
Dies ist also sehr nützlich, um festzustellen, was genau in Ihrer NHibernate-Anwendung passiert. Es wird unglaublich nützlich, sobald die Anwendung eine bestimmte Komplexität erreicht hat, bei der Sie eher einen SQL Profiler benötigen, jedoch mit dem Wissen von NHibernate.
In diesem Kapitel werden wir hinzufügen IntelliSense zu unseren NHibernate-Mapping-Dateien (*.hbm.xml files). Wie Sie beim Zuordnen der Domain Student-Klasse festgestellt haben, ist IntelliSense derzeit nicht verfügbar. Es ist sehr nützlich, das zu habenXML schemasverfügbar. In diesem Kapitel erfahren Sie, wie Sie IntelliSense in Visual Studio für diese NHibernate-XML-Dateien hinzufügen.
Öffnen Sie die Zuordnungsdatei und Sie werden sehen, dass die XML-Menüoption im Hauptmenü angezeigt wird.
Wählen Sie die Menüoption XML → Schemas… und das Dialogfeld XML-Schemas wird angezeigt.
Klicken Sie oben rechts im Dialogfeld auf die Schaltfläche Hinzufügen…, um das Dateidialogfeld zu öffnen. Nun geh zumpackages folderbefindet sich im Lösungsordner Ihres Projekts und Sie sehen die verschiedenen Pakete, die in Ihrem Projekt enthalten sind.
Doppelklicken Sie nun auf NHibernate.4.*** folder und Sie sehen die beiden Schemadateien (* .xsd) oder XML-Schemadefinitionsdateien, die die NHibernate-Konfiguration und -Zuordnung definieren.
Wählen Sie diese beiden Schemadateien aus und klicken Sie auf die Schaltfläche Öffnen.
Sie können sehen, dass die NHibernate-Schemas dem Dialogfeld XML-Schemas hinzugefügt werden. Klicken Sie auf die Schaltfläche OK. Beginnen wir jetzt mit einem neuen Eigenschaftstag, und Sie werden sehen, dass wir hier volles IntelliSense haben.
IntelliSense ist jetzt für Sie verfügbar, was beim objektrelationalen Mapping viel Zeit spart.
In diesem Kapitel werden Mapping-Datentypen behandelt. Das Zuordnen von Entitäten ist unkompliziert. Entitätsklassen werden immer mithilfe von Datenbanktabellen zugeordnet<class>, <subclass>, and <joined-subclass>Zuordnungselemente. Werttypen benötigen etwas mehr. Hier sind Zuordnungstypen erforderlich.
NHibernate kann eine Vielzahl von Datentypen abbilden. Hier ist die Liste der gängigsten Datentypen, die unterstützt werden.
Zuordnungstyp | .NET-Typ | System.Data.DbType |
---|---|---|
Int16 | System.Int16 | DbType.Int16 |
Int32 | System.Int32 | DbType.Int32 |
Int64 | System.Int64 | DbType.Int64 |
Single | System.Single | DbType.Single |
Doppelt | System.Double | DbType.Double |
Dezimal | System.Decimal | DbType.Decimal |
String | System.String | DbType.String |
AnsiString | System.String | DbType.AnsiString |
Byte | System.Byte | DbType.Byte |
Verkohlen | System.Char | DbType.StringFixedLength - ein Zeichen |
AnsiChar | System.Char | DbType.AnsiStringFixedLength - ein Zeichen |
Boolescher Wert | System.Boolean | DbType.Boolean |
Guid | System.Guid | DbType.Guid |
PersistentEnum | System.Enum (eine Aufzählung) | DbType für den zugrunde liegenden Wert |
Wahr falsch | System.Boolean | DbType.AnsiStringFixedLength - entweder 'T' oder 'F' |
Ja Nein | System.Boolean | DbType.AnsiStringFixedLength - entweder 'Y' oder 'N' |
Terminzeit | Terminzeit | DbType.DateTime - ignoriert Millisekunden |
Zecken | System.DateTime | DbType.Int64 |
Zeitspanne | System.TimeSpan | DbType.Int64 |
Zeitstempel | System.DateTime | DbType.DateTime - so spezifisch, wie es die Datenbank unterstützt |
Binär | System.Byte [] | DbType.Binary |
BinaryBlob | System.Byte [] | DbType.Binary |
StringClob | System.String | DbType.String |
Serialisierbar | Jedes System.Object, das mit SerializableAttribute markiert ist | DbType.Binary |
CultureInfo | System.Globalization.CultureInfo | DbType.String - fünf Zeichen für Kultur |
Art | Systemtyp | DbType.String mit dem qualifizierten Namen der Assembly |
In der oben angegebenen Tabelle werden die unten genannten Hinweise ausführlich erläutert.
Alles von einfachen numerischen Typen bis hin zu Zeichenfolgen, die auf verschiedene Arten zugeordnet werden können varchars, chars usw. sowie String-Blobs und die Vielzahl der von Datenbanken unterstützten Typen.
Es ist auch in der Lage zu kartieren Booleans, sowohl für Felder mit Nullen als auch für Einsen, Zeichenfelder, die wahr, falsch oder T und F enthalten.
Es gibt eine Vielzahl von Möglichkeiten, um zu definieren, wie diese dem Back-End zugeordnet werden, boolesche Werte in der Datenbank.
Wir können mit der Zuordnung von umgehen DateTime, einschließlich und ohne Zeitzonenversätze, Sommerzeit usw.
Wir können auch kartieren enumerations;; Wir können diese Zeichenfolgen oder ihren zugrunde liegenden numerischen Werten zuordnen.
Schauen wir uns ein einfaches Beispiel an, in dem wir sowohl in der Datenbank als auch in der Student-Klasse dieselben Eigenschaftsnamen haben.
Lassen Sie uns nun den FirstMidName in der Schülerklasse in FirstName ändern, wobei wir die FirstMidName-Spalte nicht ändern, aber wir werden sehen, wie NHibernate weiß, dass diese Konvertierung durchgeführt werden soll. Es folgt die aktualisierte Schülerklasse.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NHibernateDemoApp {
class Student {
public virtual int ID { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
}
}
Hier ist die Implementierung der NHibernate-Zuordnungsdatei.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemoApp"
namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "native"/>
</id>
<property name = "LastName"/>
<property name = "FirstName" column = "FirstMidName" type = "String"/>
</class>
</hibernate-mapping>
In diesem Beispiel wird davon ausgegangen, dass das Feld Vorname eine .NET-Zeichenfolge und die Spalte Vorname eine ist SQL nvarchar. Um NHibernate nun mitzuteilen, wie diese Konvertierung durchgeführt werden soll, setzen Sie den Namen aufFirstName und Spalte gleich FirstMidName und geben Sie den Zuordnungstyp gleich String an, der für diese bestimmte Konvertierung geeignet ist.
Das Folgende ist a Program.cs Dateiimplementierung.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2}", student.ID, student.FirstName,
student.LastName);
}
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Wenn Sie nun Ihre Anwendung ausführen, wird die folgende Ausgabe angezeigt.
NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,
this_.FirstMidName as FirstMid3_0_0_ FROM Student this_
Fetch the complete list again
3 Allan Bommer
4 Jerry Lewis
Wie Sie sehen, wurde der unterschiedliche Eigenschaftsname dem Spaltennamen in der Datenbank zugeordnet.
Schauen wir uns ein anderes Beispiel an, in dem wir der Student-Klasse von eine weitere Eigenschaft hinzufügen enumArt. Hier ist die Implementierung der Schülerklasse.
using System;
using System.Collections.Generic;
using System.Linq; using System.Text;
using System.Threading.Tasks;
namespace NHibernateDemoApp {
class Student {
public virtual int ID { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual StudentAcademicStanding AcademicStanding { get; set; }
}
public enum StudentAcademicStanding {
Excellent,
Good,
Fair,
Poor,
Terrible
}
}
Wie Sie sehen können, hat die Aufzählung eine Vielzahl unterschiedlicher Werte, die sie möglicherweise haben kann, wie z. B. Ausgezeichnet, Gut, Fair, Schlecht und Schrecklich.
Wenn Sie zur Zuordnungsdatei springen, sehen Sie, dass jede dieser Eigenschaften in der Zuordnungsdatei aufgeführt ist, einschließlich der neu hinzugefügten AcademicStanding Eigentum.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "native"/>
</id>
<property name = "LastName"/>
<property name = "FirstName" column = "FirstMidName" type = "String"/>
<property name = "AcademicStanding"/>
</class>
</hibernate-mapping>
Jetzt müssen wir auch die Datenbank ändern. Gehen Sie also zum SQL Server-Objekt-Explorer, klicken Sie mit der rechten Maustaste auf die Datenbank und wählen Sie die Option Neue Abfrage….
Der Abfrageeditor wird geöffnet und die folgende Abfrage angegeben.
DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[LastName] NVARCHAR (MAX) NULL,
[FirstMidName] NVARCHAR (MAX) NULL,
[AcademicStanding] NCHAR(10) NULL,
CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC)
);
Diese Abfrage löscht zuerst die vorhandene Schülertabelle und erstellt dann eine neue Tabelle.
Klicken Sie wie oben gezeigt auf das Symbol Ausführen. Sobald die Abfrage erfolgreich ausgeführt wurde, wird eine Meldung angezeigt.
Erweitern Sie die Dropdown-Liste Datenbank und Tabelle, klicken Sie mit der rechten Maustaste auf die Schülertabelle und wählen Sie View Designer.
Jetzt sehen Sie die neu erstellte Tabelle, die auch die neue Eigenschaft AcademicStanding hat.
Fügen wir zwei Datensätze hinzu, wie im Folgenden gezeigt Program.cs Datei.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var student1 = new Student {
ID = 1,
FirstName = "Allan",
LastName = "Bommer",
AcademicStanding = StudentAcademicStanding.Excellent
};
var student2 = new Student {
ID = 2,
FirstName = "Jerry",
LastName = "Lewis",
AcademicStanding = StudentAcademicStanding.Good
};
session.Save(student1);
session.Save(student2);
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
student.FirstName, student.LastName, student.AcademicStanding);
}
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Lassen Sie uns nun Ihre Anwendung ausführen und Sie werden die folgende Ausgabe in Ihrem Konsolenfenster sehen.
Fetch the complete list again
1 Allan Bommer Excellent
2 Jerry Lewis Good
Schauen wir uns nun die Datenbank an, indem Sie mit der rechten Maustaste auf die Schülertabelle klicken.
Wählen Sie Daten anzeigen und Sie sehen die beiden Datensätze in der Schülertabelle, wie im folgenden Screenshot gezeigt.
Sie können sehen, dass zwei Datensätze hinzugefügt wurden und Allan AcademicStanding 0 und Jerry AcademicStanding 1 hat. Dies liegt daran, dass in .Net der erste Aufzählungswert standardmäßig 0 hat, was bei Betrachtung ausgezeichnet ist StudentAcademicStanding. Während in der Student.cs-Datei Good die zweite ist, hat sie den Wert 1.
In diesem Kapitel werden wir uns mit der NHibernate-Konfiguration befassen. Wir haben verschiedene Möglichkeiten, NHibernate zu konfigurieren. Es teilt sich in zwei Hauptgruppen
- XML-basierte Konfiguration
- Codebasierte Konfiguration
Codebasierte Konfiguration
Die codebasierte Konfiguration ist in NHibernate integriert. Es wurde um das NHibernate 3 herum eingeführt und wir haben bisher die Codebasis-Konfiguration verwendet.
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
Alle Konfigurationen sind im C # -Code angegeben. Sie können hier sehen, dass wir unser neues Konfigurationsobjekt haben und es dann verwendenloquacious configurationDies wurde mit NHibernate 3.1 eingeführt, um die Datenbank zu konfigurieren. Welche Verbindungszeichenfolge wir verwenden, mit welcher Datenbank wir eine Verbindung herstellen und welchen Dialekt wir verwenden möchten. Wir fügen hier auch unsere Mapping-Assembly direkt hinzu.
XML-basierte Konfiguration
Wenn Sie eine XML-basierte Konfiguration verwenden, können Sie a verwenden hibernate.cfg.xml Datei, die nur eine eigenständige XML-Datei ist, die das NHibernate-Schema verwendet, oder Sie können diese NHibernate-spezifische Konfiguration in Ihre App oder einbetten web.cfg. Der Name hibernate.cfg.xml ist standardmäßig, aber wir können auch für diese XML-Datei einen beliebigen Namen verwenden.
Lassen Sie uns einen Blick auf die XML-basierte Konfiguration werfen, indem Sie dem NHibernateDemoApp-Projekt eine neue XML-Datei hinzufügen und sie hibernate.cfg.xml nennen.
Geben Sie die folgenden Informationen in die Datei hibernate.cfg.xml ein.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2">
<session-factory>
<property name = "connection.connection_string">
Data Source = asia13797\\sqlexpress;
Initial Catalog = NHibernateDemoDB;
Integrated Security = True;
Connect Timeout = 15;
Encrypt = False;
TrustServerCertificate = False;
ApplicationIntent = ReadWrite;
MultiSubnetFailover = False;
</property>
<property name = "connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name = "dialect">
NHibernate.Dialect.MsSql2008Dialect
</property>
<mapping assembly = "NHibernateDemoApp"/>
</session-factory>
</hibernate-configuration>
Wie Sie in der obigen XML-Datei sehen können, haben wir dieselbe Konfiguration wie in C # angegeben angegeben.
Lassen Sie uns nun diese Konfiguration aus der Datei Program.cs kommentieren und einfach die aufrufen Configure() Methode, die die lädt hibernate.cfg.xml Datei wie unten gezeigt.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
//cfg.DataBaseIntegration(x =>
//{
// x.ConnectionString = "Data Source = asia13797;\\sqlexpress
Initial Catalog = NHibernateDemoDB;
Integrated Security = True;
Connect Timeout = 15;
Encrypt =False;
TrustServerCertificate = False;
ApplicationIntent = ReadWrite;
MultiSubnetFailover = False";
// x.Driver<SqlClientDriver>();
// x.Dialect<MsSql2008Dialect>();
// x.LogSqlInConsole = true;
//});
//cfg.AddAssembly(Assembly.GetExecutingAssembly());
cfg.Configure();
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
student.FirstName, student.LastName, student.AcademicStanding);
}
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Lassen Sie uns Ihre Anwendung erneut ausführen und Sie werden dieselbe Ausgabe sehen.
Fetch the complete list again
1 Allan Bommer Excellent
2 Jerry Lewis Good
In diesem Kapitel wird erläutert, wie Sie die NHibernate-Konfiguration überschreiben. Es gibt nur ein paar Dinge, die Sie beachten müssen.
Zunächst ist die Konfiguration in NHibernate additiv.
Sie müssen also nicht nur eine einzelne XML-Datei oder die codebasierte Konfiguration oder fließend NHibernate verwenden.
Sie können alle diese Methoden mischen und anpassen, je nachdem, wie Sie Ihre Anwendung konfigurieren möchten.
Der wichtige Punkt, an den Sie sich erinnern sollten, ist, dass zuletzt die Konfiguration gewinnt.
Im folgenden Beispiel sehen Sie, dass wir unser Konfigurationsobjekt erstellen, es mithilfe der codebasierten Konfiguration konfigurieren und schließlich das aufrufen cfg.configure() Methode, die die Datei hibernate.cfg.xml lädt.
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
});
cfg.Configure();
Alles in einer hibernate.cfg.xml überschreibt also die Einstellungen, die durch die codebasierte Konfiguration festgelegt wurden.
Durch Umkehren dieser beiden Prozesse können wir die Standardeinstellungen in hibernate.cfg.xml festlegen und dann unsere Überschreibungen in einer codebasierten Konfiguration vornehmen.
Es gibt nichts, was ausschließt, wenn Sie eine codebasierte Konfiguration verwenden, und es gibt auch nichts, was Sie daran hindert, die Datei hibernate.cfg.xml zu verwenden.
Schauen wir uns ein einfaches Beispiel an, in dem wir die Konfiguration mithilfe einer Mischung aus XML-basierter und Code-basierter Konfiguration überschreiben.
Verschieben wir auch die Verbindungszeichenfolge in die app.config Datei mit dem folgenden Code.
<?xml version = "1.0" encoding = "utf-8" ?>
<configuration>
<startup>
<supportedRuntime version = "v4.0" sku = ".NETFramework,Version = v4.5" />
</startup>
<connectionStrings>
<add name = "default" connectionString = "Data Source =
asia13797\\sqlexpress;
Initial Catalog = NHibernateDemoDB;
Integrated Security = True;
Connect Timeout = 15;
Encrypt = False;
TrustServerCertificate = False;
ApplicationIntent = ReadWrite;
MultiSubnetFailover = False"/>
</connectionStrings>
</configuration>
Die Verbindungszeichenfolge sitzt in einigen app.configDatei mit einem Standardnamen. Jetzt müssen wir den Standardnamen in der Datei hibernate.cfg.xml anstelle der Verbindungszeichenfolge erwähnen.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2">
<session-factory>
<property name = "connection.connection_string">default</property>
<property name = "connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name = "dialect">
NHibernate.Dialect.MsSql2008Dialect
</property>
<mapping assembly = "NHibernateDemoApp"/>
</session-factory>
</hibernate-configuration>
Lassen Sie uns den Teil der Verbindungszeichenfolge, den Treiber und den Dialektteil aus der codebasierten Konfiguration kommentieren, da das Programm ihn aus der Datei hibernate.cfg.xml und der Datei liest LogSqlInConsole Teil bleibt in der codebasierten Konfiguration.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { //x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
//x.Driver<SqlClientDriver>();
//x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
});
cfg.Configure();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
student.FirstName, student.LastName, student.AcademicStanding);
}
tx.Commit();
}
Console.ReadLine();
}
}
}
}
Wenn Sie nun die Anwendung ausführen, sehen Sie, dass das Programm das Protokoll aus der codebasierten Konfiguration und andere Konfigurationen aus der Datei hibernate.cfg.xml gelesen hat.
NHibernate: SELECT this_.ID as ID0_0_, this_.LastName as LastName0_0_,
this_.FirstMidName as FirstMid3_0_0_, this_.AcademicStanding as Academic4_0_0_ FROM
Student this_
Fetch the complete list again
1 Allan Bommer Excellent
2 Jerry Lewis Good
Jetzt haben wir einen Teil unserer Konfiguration in unserem hibernate.cfg.xml Ein Teil davon befindet sich in der codebasierten Konfiguration und hängt von der Reihenfolge des Aufrufs von codebasiert im Vergleich zu ab configure()können wir ändern, welcher von ihnen den anderen überschreibt.
In diesem Kapitel werden wir uns mit der Aktualisierung der Stapelgröße befassen. Die Chargengröße ermöglicht es Ihnencontrol the number of updates die in einem einzigen Roundtrip zu Ihrer Datenbank für die unterstützten Datenbanken ausgehen.
Die Update-Stapelgröße wurde ab NHibernate 3.2 voreingestellt.
Wenn Sie jedoch eine frühere Version verwenden oder Ihre NHibernate-Anwendung optimieren müssen, sollten Sie sich die Update-Stapelgröße ansehen. Dies ist ein sehr nützlicher Parameter, mit dem Sie die Leistung von NHibernate optimieren können.
Die tatsächliche Stapelgröße steuert, wie viele Einfügungen in einer Gruppe an eine Datenbank gesendet werden sollen.
Derzeit unterstützen nur SQL Server und Oracle diese Option, da der zugrunde liegende Datenbankanbieter das Batching von Abfragen unterstützen muss.
Schauen wir uns ein einfaches Beispiel an, in dem wir die Stapelgröße auf 10 festgelegt haben, um 10 Datensätze in einen Satz einzufügen.
cfg.DataBaseIntegration(x => {
x.ConnectionString = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
x.BatchSize = 10;
});
Hier ist die vollständige Implementierung, in der 25 Datensätze zur Datenbank hinzugefügt werden.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver>SqlClientDriver<();
x.Dialect>MsSql2008Dialect>();
x.LogSqlInConsole = true;
x.BatchSize = 10;
});
//cfg.Configure();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
for (int i = 0; i < 25; i++) {
var student = new Student {
ID = 100+i,
FirstName = "FirstName"+i.ToString(),
LastName = "LastName" + i.ToString(),
AcademicStanding = StudentAcademicStanding.Good
};
session.Save(student);
}
tx.Commit();
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,student.FirstName,
student.LastName, student.AcademicStanding);
}
}
Console.ReadLine();
}
}
}
}
Lassen Sie uns nun Ihre Anwendung ausführen und sehen, dass all diese Updates auf den NHibernate-Profiler übertragen werden. Wir haben 26 individuelle Rundreisen in die Datenbank 25 zum Einfügen und einen zum Abrufen der Studentenliste.
Warum ist das so? Der Grund ist, dass NHibernate a tun mussselect scope identity Wir verwenden die Strategie zur Generierung nativer Bezeichner in der Zuordnungsdatei für ID, wie im folgenden Code gezeigt.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
assembly = "NHibernateDemoApp"
namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "native"/>
</id>
<property name = "LastName"/>
<property name = "FirstName" column = "FirstMidName" type = "String"/>
<property name = "AcademicStanding"/>
</class>
</hibernate-mapping>
Wir müssen also eine andere Methode wie die verwenden guid.combMethode. Wenn wir zu guid.comb gehen wollen, müssen wir zu unserem Kunden gehen und dies in a ändernguid. Das wird also gut funktionieren. Wechseln wir nun mit dem folgenden Code von native zu guid.comb.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly =
"NHibernateDemoApp" namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "guid.comb"/>
</id>
<property name = "LastName"/>
<property name = "FirstName" column = "FirstMidName" type = "String"/>
<property name = "AcademicStanding"/>
</class>
</hibernate-mapping>
Es ist also die Datenbank, die für die Generierung dieser IDs verantwortlich ist. Die einzige Möglichkeit für NHibernate, herauszufinden, welche ID generiert wurde, bestand darin, sie unmittelbar danach auszuwählen. Wenn wir einen Stapel von Schülern erstellt haben, kann die ID des erstellten Schülers nicht übereinstimmen.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NHibernateDemoApp {
class Student {
public virtual Guid ID { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual StudentAcademicStanding AcademicStanding { get; set; }
}
public enum StudentAcademicStanding {
Excellent,
Good,
Fair,
Poor,
Terrible
}
}
Wir müssen nur unsere Datenbank aktualisieren. Lassen Sie uns die Schülertabelle löschen und eine neue Tabelle erstellen, indem Sie die folgende Abfrage angeben. Gehen Sie also zum SQL Server-Objekt-Explorer, klicken Sie mit der rechten Maustaste auf die Datenbank und wählen Sie die ausNew Query… Möglichkeit.
Es öffnet den Abfrageeditor und gibt dann die folgende Abfrage an.
DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] (
-- [ID] INT IDENTITY (1, 1) NOT NULL,
[ID] UNIQUEIDENTIFIER NOT NULL,
[LastName] NVARCHAR (MAX) NULL,
[FirstMidName] NVARCHAR (MAX) NULL,
[AcademicStanding] NCHAR(10) NULL,
CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC)
);
Diese Abfrage löscht zuerst die vorhandene Schülertabelle und erstellt dann eine neue Tabelle. Wie Sie sehen, haben wir verwendetUNIQUEIDENTIFIER anstatt einen ganzzahligen Primärschlüssel als ID zu verwenden.
Führen Sie diese Abfrage aus und gehen Sie dann zu Designer view und Sie werden sehen, dass jetzt die ID mit einer eindeutigen Kennung erstellt wird, wie im folgenden Bild gezeigt.
Jetzt müssen wir die ID aus der Datei program.cs entfernen, während wir Daten einfügen, da jetzt die ID generiert wird guids dafür automatisch.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
x.BatchSize = 10;
});
//cfg.Configure();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
for (int i = 0; i > 25; i++) {
var student = new Student {
FirstName = "FirstName"+i.ToString(),
LastName = "LastName" + i.ToString(),
AcademicStanding = StudentAcademicStanding.Good
};
session.Save(student);
}
tx.Commit();
var students = session.CreateCriteria<Student>().List<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3}", student.ID,
student.FirstName,student.LastName, student.AcademicStanding);
}
}
Console.ReadLine();
}
}
}
}
Führen Sie nun die Anwendung erneut aus und sehen Sie sich den NHibernate-Profiler an. Jetzt macht der NHibernate-Profiler statt 26 Rundreisen nur noch vier.
Es werden zehn Zeilen in die Tabelle eingefügt, dann weitere zehn Zeilen und später die restlichen fünf. Nach dem Festschreiben wurde eine weitere zum Abrufen aller Datensätze eingefügt.
Also hat es es in Zehnergruppen aufgeteilt, so gut es geht.
Wenn Sie also viele Einfügungen vornehmen, kann dies die Einfügungsleistung in Ihrer Anwendung erheblich verbessern, da Sie sie stapeln können.
Dies liegt daran, dass NHibernate diese Guids selbst mithilfe von zuweist guid.comb Algorithmus, und es muss nicht auf die Datenbank angewiesen sein, um dies zu tun.
Die Verwendung der Stapelgröße ist daher eine hervorragende Möglichkeit, diese zu optimieren.
In diesem Kapitel werden wir behandeln, wie die cachingarbeitet in NHibernate-Anwendungen. Es verfügt über eine integrierte Unterstützung für das Caching. Es sieht aus wie eine einfache Funktion, ist aber in Wirklichkeit eine der komplexesten Funktionen. Wir beginnen mit dem First Level Cache.
First Level Cache
Dieser Cache-Mechanismus ist in NHibernate standardmäßig aktiviert, und wir müssen nichts für die Arbeit mit dem Cache tun. Um dies zu verstehen, werfen wir einen Blick auf ein einfaches Beispiel, da Sie sehen können, dass wir zwei Datensätze in unserer Datenbank haben.
In diesem Beispiel rufen wir den Schüler ab, dessen ID 1 ist, und verwenden dieselbe Sitzungsabfrage zweimal, wie im folgenden Code gezeigt.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cache;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
x.BatchSize = 10;
});
//cfg.Configure();
cfg.Cache(c => {
c.UseMinimalPuts = true;
c.UseQueryCache = true;
});
cfg.SessionFactory().Caching .Through<HashtableCacheProvider>()
.WithDefaultExpiration(1440);
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()){
using (var tx = session.BeginTransaction()) {
var studentUsingTheFirstQuery = session.Get<Student>(1);
var studentUsingTheSecondQuery = session.Get<Student>(1);
}
Console.ReadLine();
}
}
}
}
Führen Sie nun diese Anwendung aus und sehen Sie das Ergebnis im NHibernate Profiler.
Sie werden überrascht sein, dass NHibernate nur eine Abfrage auslöst. Auf diese Weise verwendet NHibernate den Cache der ersten Ebene. Wenn die erste Abfrage ausgeführt wird, hat NHibernate den Schüler mit der ID = 1 in seinem Cache der ersten Ebene zwischengespeichert.
Wenn also die zweite Abfrage ausgeführt wird, sucht NHibernate zuerst die Cache der ersten Ebene. Student-Entität mit ID = 1. Wenn diese Entität gefunden wird, weiß NHibernate, dass keine weitere Abfrage ausgelöst werden muss, um dasselbe Mitarbeiterobjekt erneut abzurufen .
In diesem Kapitel werden wir uns mit Mapping-Komponenten befassen. In NHibernate,component is a value object. Es hat keine eigene Identität.
Ein Beispiel hierfür wäre ein Geldobjekt, eine Geldbörse oder eine Brieftasche, in der sich möglicherweise Geld befindet, aber die genaue Identität dieses Geldes ist irrelevant.
Es hat keinen eigenen Primärschlüssel, aber die Komponenten selbst bleiben in derselben Tabelle wie das besitzende Objekt erhalten.
Schauen wir uns ein einfaches Beispiel an, in dem ein Schüler eine Adresse hat, deren Objekt er ist Location class mit ihr verbundenen.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace NHibernateDemoApp {
class Student {
public virtual int ID { get; set; }
public virtual string LastName { get; set; }
public virtual string FirstName { get; set; }
public virtual StudentAcademicStanding AcademicStanding { get; set; }
public virtual Location Address { get; set; }
}
public class Location {
public virtual string Street { get; set; }
public virtual string City { get; set; }
public virtual string Province { get; set; }
public virtual string Country { get; set; }
}
public enum StudentAcademicStanding {
Excellent,
Good,
Fair,
Poor,
Terrible
}
}
Jetzt müssen wir auch die Datenbank aktualisieren, indem wir die folgende Abfrage ausführen, die zuerst die Student-Tabelle löscht und dann eine neue Tabelle erstellt, die auch eine Spalte für die Location-Klasse enthält.
DROP TABLE [dbo].[Student]
CREATE TABLE [dbo].[Student] (
[ID] INT IDENTITY (1, 1) NOT NULL,
[LastName] NVARCHAR (MAX) NULL,
[FirstMidName] NVARCHAR (MAX) NULL,
[AcademicStanding] NCHAR(10) NULL,
[Street] NVARCHAR (100) NULL,
[City] NVARCHAR (100) NULL,
[Province] NVARCHAR (100) NULL,
[Country] NVARCHAR (100) NULL,
CONSTRAINT [PK_dbo.Student] PRIMARY KEY CLUSTERED ([ID] ASC)
);
Um nun die Spalten zuzuordnen, die nicht direkt Teil der Schülerklasse sind, sondern Eigenschaften der Standortklasse sind, wird das Objekt der Standortklasse in der Schülerklasse definiert. Wir brauchen eine Komponente, um sie richtig abzubilden. Lassen Sie uns eine Komponente in erstellenstudent.hbm.xml Datei wie im folgenden Code gezeigt.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
assembly = "NHibernateDemoApp" namespace = "NHibernateDemoApp">
<class name = "Student">
<id name = "ID">
<generator class = "native"/>
</id>
<property name = "LastName"/>
<property name = "FirstName" column = "FirstMidName" type = "String"/>
<property name = "AcademicStanding"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
</class>
</hibernate-mapping>
Diese Komponente ist die Adresse und hat diese verschiedenen Eigenschaften. Mit diesen Informationen verfügt NHibernate nun über genügend Informationen, um dies tatsächlich abzubilden.
Hier ist nun die Program.cs-Datei, in der ein neues Schülerobjekt erstellt und initialisiert und dann in der Datenbank gespeichert wird. Anschließend wird die Liste aus der Datenbank abgerufen.
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cache;
using NHibernate.Caches.SysCache;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
using System;
using System.Linq;
using System.Reflection;
namespace NHibernateDemoApp {
class Program {
static void Main(string[] args) {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
cfg.DataBaseIntegration(x = > { x.ConnectionString = "Data Source +
Initial Catalog + Integrated Security + Connect Timeout + Encrypt +
TrustServerCertificate + ApplicationIntent + MultiSubnetFailover";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
});
cfg.AddAssembly(Assembly.GetExecutingAssembly());
var sefact = cfg.BuildSessionFactory();
using (var session = sefact.OpenSession()) {
using (var tx = session.BeginTransaction()) {
var student1 = new Student {
ID = 1,
FirstName = "Allan",
LastName = "Bommer",
AcademicStanding = StudentAcademicStanding.Poor,
Address = new Location {
Street = "123 Street",
City = "Lahore",
Province = "Punjab",
Country = "Pakistan"
}
};
session.Save(student1);
tx.Commit();
var students = session.Query<Student>().ToList<Student>();
Console.WriteLine("\nFetch the complete list again\n");
foreach (var student in students) {
Console.WriteLine("{0} \t{1} \t{2} \t{3} \t{4} \t{5} \t{6} \t{7}",
student.ID,
student.FirstName,
student.LastName,
student.AcademicStanding,
student.Address.Street,
student.Address.City,
student.Address.Province,
student.Address.Country
);
}
}
Console.ReadLine();
}
}
}
}
Jetzt können wir diese Anwendung ausführen und NHibernate kann diese Werte in der Datenbank speichern. Wenn Sie die Anwendung ausführen, wird die folgende Ausgabe angezeigt.
Fetch the complete list again
2 Allan Bommer Poor 123 Street Lahore Punjab Pakistan
Hier sind die Werte in der Datenbank.
Mit den Komponenten können wir Spalten in einer Datenbanktabelle in eine eigene Klasse aufteilen.
Die andere Sache, die hier zu beachten ist, ist, dass der Ort eine Klasse ist, es ist keine Entität.
Es ist ein Objekt vom Typ Wert und hat keinen eigenen Primärschlüssel.
Es wird in derselben Tabelle gespeichert wie der Student, der es enthält.
Deshalb verwenden wir hier die Komponente.
Dies ermöglicht viel Flexibilität bei der Änderung unserer Klassenschicht, der Definition unserer Klassen im Vergleich zur Anordnung unserer Datenbank.
In diesem Kapitel werden wir uns mit den Beziehungen in NHibernate befassen. Wenden wir uns der Frage zu, wie wir Beziehungen in NHibernate verstehen können. Am einfachsten ist es, über die Beziehungen aus der Datenbankperspektive nachzudenken.
Wir werden zuerst eine neue Anwendung erstellen, in der wir einige Beziehungen zwischen den Kunden- und Auftragseinheiten erstellen.
Die erste Beziehung, die wir uns ansehen werden, ist eine klassische Sammlungsbeziehung.
Wir haben einen Kunden mit einer Sammlung von Bestellungen.
Dies ist eine Eins-zu-Viele-Beziehung, die in der Datenbank durch 2 Tabellen dargestellt wird. In der Auftragstabelle befindet sich eine Kunden-ID, und wir haben eine Fremdschlüsselbeziehung zum Kunden.
Zuerst müssen wir eine Datenbank und zwei Tabellen Customer und Order erstellen. Sie können dies erstellen, indem Sie im SQL Server Explorer die folgende Abfrage angeben.
USE [master]
GO
CREATE DATABASE [NHibernateDemo]
GO
USE [NHibernateDemo]
GO
CREATE TABLE [dbo].[Customer](
[Id] [uniqueidentifier] NOT NULL,
[FirstName] [nvarchar](100) NOT NULL,
[LastName] [nvarchar](100) NOT NULL,
[Points] [int] NULL, [HasGoldStatus] [bit] NULL,
[MemberSince] [date] NULL,
[CreditRating] [nchar](20) NULL,
[AverageRating] [decimal](18, 4) NULL,
[Street] [nvarchar](100) NULL,
[City] [nvarchar](100) NULL,
[Province] [nvarchar](100) NULL,
[Country] [nvarchar](100) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
)
GO
CREATE TABLE [dbo].[Order](
[Id] [uniqueidentifier] NOT NULL,
[CustomerId] [uniqueidentifier] NULL,
[Ordered] [datetime] NULL,
[Shipped] [datetime] NULL,
[Street] [nvarchar](100) NULL,
[City] [nvarchar](100) NULL,
[Province] [nvarchar](100) NULL,
[Country] [nvarchar](100) NULL,
PRIMARY KEY CLUSTERED ([Id] ASC)
)
GO
Es werden zwei Tabellen in der Datenbank erstellt. Das folgende Bild zeigt die Kundentabelle.
Das folgende Bild zeigt die Auftragstabelle, in der Sie die Fremdschlüsselbeziehung zum Kunden sehen können.
Wir müssen die Verbindungszeichenfolge in der definieren app.config Datei, hier ist die Implementierung der Datei app.config.
<?xml version = "1.0" encoding = "utf-8" ?>
<configuration>
<connectionStrings>
<add name = "default" connectionString = "Data Source =
(localdb)\MSSQLLocalDB;Initial Catalog = NHibernateDemo;Integrated Security =
True;Connect Timeout = 30;Encrypt = False;TrustServerCertificate = False;
ApplicationIntent = ReadWrite;MultiSubnetFailover = False"/>
</connectionStrings>
</configuration>
Führen Sie den folgenden Befehl im NuGet Manager Console-Fenster aus, um NHibernate in Ihrer Anwendung zu installieren.
install-package NHibernate
Um die NHibernate-Konfiguration zu konfigurieren, müssen Sie die Konfiguration in definieren hibernate.cfg.xml Datei wie im folgenden Code gezeigt.
<xml version = "1.0" encoding = "utf-8" ?>
<hibernate-configuration xmlns = "urn:nhibernate-configuration-2.2">
<session-factory>
<property name = "connection.connection_string_name">default</property>
<property name = "connection.driver_class">
NHibernate.Driver.SqlClientDriver
</property>
<property name = "dialect">
NHibernate.Dialect.MsSql2008Dialect
</property>
<property name = "show_sql">true</property>
</session-factory>
</hibernate-configuration>
In diesem Beispiel arbeiten wir mit zwei Domänenklassen, Kunde und Bestellung.
Hier ist die Implementierung der Customer.cs-Datei, in der wir zwei Klassen haben, eine ist die Customer-Klasse und eine andere ist die Location-Klasse, in der das Objekt als Adresse in der Customer-Klasse verwendet wird.
using System;
using System.Text;
using Iesi.Collections.Generic;
namespace NHibernateDemo {
public class Customer {
public Customer() {
MemberSince = DateTime.UtcNow;
Orders = new HashedSet<Order>();
}
public virtual Guid Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual double AverageRating { get; set; }
public virtual int Points { get; set; }
public virtual bool HasGoldStatus { get; set; }
public virtual DateTime MemberSince { get; set; }
public virtual CustomerCreditRating CreditRating { get; set; }
public virtual Location Address { get; set; }
public virtual ISet<Order> Orders { get; set; }
public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }
public override string ToString() {
var result = new StringBuilder();
result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
{4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
{8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
CreditRating, MemberSince.Kind, AverageRating);
result.AppendLine("\tOrders:");
foreach(var order in Orders) {
result.AppendLine("\t\t" + order);
}
return result.ToString();
}
}
public class Location {
public virtual string Street { get; set; }
public virtual string City { get; set; }
public virtual string Province { get; set; }
public virtual string Country { get; set; }
}
public enum CustomerCreditRating {
Excellent,
VeryVeryGood,
VeryGood,
Good,
Neutral,
Poor,
Terrible
}
}
Hier ist die Mapping-Datei Customer.hbm.xml in which Customer class is mapped to the Customer table.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
</class>
</hibernate-mapping>
We also have an Order Class and here is the implementation of Order.cs file.
using System; using Iesi.Collections.Generic;
namespace NHibernateDemo {
public class Order {
public virtual Guid Id { get; set; }
public virtual DateTime Ordered { get; set; }
public virtual DateTime? Shipped { get; set; }
public virtual Location ShipTo { get; set; }
public virtual Customer Customer { get; set; }
public override string ToString() {
return string.Format("Order Id: {0}", Id);
}
}
}
Many-to-One Relationship
We also need to map the Order class to the Order table in the database, so here is the implementation of the Order.hbm.xml file.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Order" table = "`Order`">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "Ordered"/>
<property name = "Shipped"/>
<component name = "ShipTo">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<!--<many-to-one name = "Customer" column = "CustomerId" cascade =
"save-update"/>-->
</class>
</hibernate-mapping>
One-to-Many Relationship
Here, we are going to take a look at a one-to-many relationship, in this case, between customer and orders. We've got our customer here, we're creating a new one, and you can see that the collection is initialized with the following pair of orders.
private static Customer CreateCustomer() {
var customer = new Customer {
FirstName = "John",
LastName = "Doe",
Points = 100,
HasGoldStatus = true,
MemberSince = new DateTime(2012, 1, 1),
CreditRating = CustomerCreditRating.Good,
AverageRating = 42.42424242,
Address = CreateLocation()
};
var order1 = new Order {
Ordered = DateTime.Now
};
customer.AddOrder(order1);
var order2 = new Order {
Ordered = DateTime.Now.AddDays(-1),
Shipped = DateTime.Now,
ShipTo = CreateLocation()
};
customer.AddOrder(order2);
return customer;
}
So we will create a new customer and then save it, after saving it, we will find the ID and then reload it in another session in the Main method as shown in the following program.
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
Guid id;
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
id = newCustomer.Id;
tx.Commit();
}
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var reloaded = session.Load<Customer>(id);
Console.WriteLine("Reloaded:");
Console.WriteLine(reloaded);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
Here is the complete Program.cs file implementation.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
Guid id;
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
id = newCustomer.Id;
tx.Commit();
}
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var reloaded = session.Load<Customer>(id);
Console.WriteLine("Reloaded:");
Console.WriteLine(reloaded);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Customer CreateCustomer() {
var customer = new Customer {
FirstName = "John",
LastName = "Doe",
Points = 100,
HasGoldStatus = true,
MemberSince = new DateTime(2012, 1, 1),
CreditRating = CustomerCreditRating.Good,
AverageRating = 42.42424242,
Address = CreateLocation()
};
var order1 = new Order {
Ordered = DateTime.Now
};
customer.AddOrder(order1);
var order2 = new Order {
Ordered = DateTime.Now.AddDays(-1),
Shipped = DateTime.Now,
ShipTo = CreateLocation()
};
customer.AddOrder(order2);
return customer;
}
private static Location CreateLocation() {
return new Location {
Street = "123 Somewhere Avenue",
City = "Nowhere",
Province = "Alberta",
Country = "Canada"
};
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x =&ht; {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10; x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
When you run this application, you will see the following output.
New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Order Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (9b0fcf10-83f6-4f39-bda5-a5b800ede2ba)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Press <ENTER> to exit...
As you can see that initially the customer has 2 orders, but when we reload it, there are no orders to be seen. If you look at customer.hbm.xml file, you can see here that we do not map actual orders collection. So NHibernate knows nothing about it. Let's go ahead and add it.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2"
assembly = "NHibernateDemo" namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<set name = "Orders" table = "`Order`">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</set>
</class>
</hibernate-mapping>
This is a set and the name of this collection is ‘Orders’, which is stored in a table called order. We need to specify a key which is the name of the foreign key or to find orders. These orders are identified or belong to a customer through the customer ID. And then I have to note that this is a one-to-many relationship and it is with the order class.
We also need to slightly change the Main method by saving the new customer orders to the database as well as shown in the following program.
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
Guid id;
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
foreach (var order in newCustomer.Orders) {
session.Save(order);
}
id = newCustomer.Id;
tx.Commit();
}
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var reloaded = session.Load<Customer>(id);
Console.WriteLine("The orders were ordered by: ");
foreach (var order in reloaded.Orders) {
Console.WriteLine(order.Customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit..."); Console.ReadLine();
}
We have also specified which customer ordered that particular product. So we need to create a many-to-one relationship to relate that order back to that customer.
So let's go into the Order.hbm.xml file and add a many-to-one, and then name the customer field and the column with the customer ID.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Order" table = "`Order`">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "Ordered"/>
<property name = "Shipped"/>
<component name = "ShipTo">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<many-to-one name = "Customer" column = "CustomerId"/>
</class>
</hibernate-mapping>
Let’s run this application again and now you will see the following output.
New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Order Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3
The orders were ordered by:
John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3
John Doe (660a6f29-650e-4380-99e0-a5b800febbde)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 57314deb-e023-4e55-ac1e-a5b800febbe3
Order Id: fc065683-d5f5-484b-ae42-a5b800febbe3
Press <ENTER> to exit...
In this chapter, we will be covering how to represent collections. There are different types of collections that we can use within the NHibernate such as −
- Lists
- Sets
- Bags
Now, from the .NET perspective, we generally deal with lists or like very simple data structures, lists, dictionaries. .NET does not have a wide variety of different collection types. So why does NHibernate need all these different types? It really comes back to the database.
List
A list is an ordered collection of elements that are not necessarily unique.
We can map this using the IList <T>.
So although we might conventionally have a list of addresses, and from application point of view we know that the elements are unique, nothing in the list prevents us from inserting duplicate elements in that list.
Set
A set is an unordered collection of unique elements. If you try to insert 2 duplicate elements into a set, it will throw an exception.
There's nothing specific in NHibernate about it.
It's just a convenient way a have a generic set implementation. If you're on .NET 4, you can use the new HashSet <T> to represent these, but in most NHibernate applications, we represent this is an ISet.
It is an unordered, if you pull back a list of addresses from a database or a list of orders, you don't know what order they're coming in unless you put in a specific Order by clause.
So in general, the data that you're pulling back from a database are sets.
They are unique collections of elements that are unordered.
Bag
Another common collection that we will see in the database world is a bag, which is just like a set except it can have duplicate elements.
In the .NET world, we represent this by an IList.
Sets are probably the most common, but you will see lists and bags as well depending on your application. Let’s have a look into a below customer.hbm.xml file from the last chapter in which Set orders are defined.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<set name = "Orders" table = "`Order`">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</set>
</class>
</hibernate-mapping>
As you can see, we have mapped the orders collection as a set. Remember that a set is an unordered collection of unique elements.
Now, if you look at the Customer class, you will see that Orders property is defined with an ISet as shown in the following program.
public virtual ISet<Order> Orders { get; set; }
Now when this application is run, you will see the following output.
New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Order Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7
The orders were ordered by:
John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7
John Doe (1f248133-b50a-4ad7-9915-a5b8017d0ff1)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: c41af8f2-7124-42a7-91c5-a5b8017d0ff6
Order Id: 657f6bb0-1f42-45fc-8fc7-a5b8017d0ff7
Press <ENTER> to exit...
If the items in the collection didn't need to be unique, if you could have multiple orders with the same primary key occurring multiple times in this collection, then this would be better mapped as a bag as shown in the following program.
<bag name = "Orders" table = "`Order`">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</bag>
Now, if you run this application you will get an exception because if we take a look at the customer class, you'll notice that the orders are marked as an ISet in the C# code.
So we will also need to change this to an IList and then here, we would need to change from the HashSet to a List in the constructor.
public class Customer {
public Customer() {
MemberSince = DateTime.UtcNow;
Orders = new List<Order>();
}
public virtual Guid Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual double AverageRating { get; set; }
public virtual int Points { get; set; }
public virtual bool HasGoldStatus { get; set; }
public virtual DateTime MemberSince { get; set; }
public virtual CustomerCreditRating CreditRating { get; set; }
public virtual Location Address { get; set; }
public virtual IList<Order> Orders { get; set; }
public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }
public override string ToString() {
var result = new StringBuilder();
result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
{4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
{8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
CreditRating, MemberSince.Kind, AverageRating); result.AppendLine("\tOrders:");
foreach(var order in Orders) {
result.AppendLine("\t\t" + order);
}
return result.ToString();
}
}
When you run the application, you will see the same behavior. But, now we can have an order occurring multiple times in the same collection.
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Order Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287
The orders were ordered by:
John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287
John Doe (fbde48f5-d620-4d1c-9a7f-a5b8017c3280)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 6dd7dbdb-354f-4c82-9c39-a5b8017c3286
Order Id: 9b3e2441-a81b-404d-9aed-a5b8017c3287
Press <ENTER> to exit...
In this chapter, we will be covering how to use the Cascade feature. If you have a set or a collection of items or a relationship between two classes such as our customer and order and have a foreign key relationship. If we delete the customer by default, NHibernate doesn't do anything to the child objects, so the ones that belong to that customer and we could be orphaning orders.
We could also be violating foreign key constraints, so we can use the notion of cascades.
By default, NHibernate does not cascade operations to child objects.
The reason for this is that you can have relationships such as a customer having a default shipping address and that shipping address is shared with many different customers.
So you wouldn't want to cascade that relationship necessarily because other customers are still referring to it.
So the whole notion of cascades is to tell NHibernate how to handle its child entities.
There are different options for cascading, which are as follows −
none − which is the default and it means no cascading.
all − which is going to cascade saves, updates, and deletes.
save-update − it will cascade, saves and updates.
delete − it will cascade deletes.
all-delete-orphan − it is a special one which is quite frequently used and is the same as All Except, if it finds Delete-orphan rows, it will delete those as well.
You can specify the default in your hbm.xml file, so you can provide a default cascade on that Hibernate mapping element or you can also specify it for specific collections and relationships such as the many-to-one.
Let’s have a look into simple example cascades, let's fix the problem in the program, where we have to manually cascade the save to the orders as shown in the following code.
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
foreach (var order in newCustomer.Orders) {
session.Save(order);
}
id = newCustomer.Id;
tx.Commit();
}
In the above code snippet, you can see that we are manually saving all the orders for the customer. Now let’s remove manual cascade code in which all the orders are saved.
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
id = newCustomer.Id;
tx.Commit();
}
We need to specify the cascade option in customer.hbm.xml.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<set name = "Orders" table = "`Order`" cascade = "all-delete-orphan">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</set>
</class>
</hibernate-mapping>
Now, orders fully belong to the customer. So if the customers were deleted from the database, our application here would want to delete all of those orders, including any that might have been orphaned.
It will end up doing a delete. By that, it will say delete from order table, where the customer ID equals the customer that you're deleting.
So you can actually cascade these deletes. So with the All, it will do saves, updates, and deletes.
Now when you run this application, you will see the following output.
New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Order Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
The orders were ordered by:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
Press <ENTER> to exit...
As you can see that we have deleted the code from the program that manually cascaded and our application is still working.
So depending on your relationship, you might want to cascade those. Now, let's take a look at a different cascade relationship. Let’s go to the Order.hbm.xml file and we can cascade that many-to-one relationship.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Order" table = "`Order`">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "Ordered"/>
<property name = "Shipped"/>
<component name = "ShipTo">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<many-to-one name = "Customer" column = "CustomerId" cascade = "save-update"/>
</class>
</hibernate-mapping>
So if we create a new order and there's a new customer attached to it and we say, save that order, we might want to cascade that. But one thing that we'd probably don't want to do is if an order is deleted to delete the corresponding customer.
So here, we would want to do a save update, so using a save-update, it will cascade any saves or updates to that customer. So, if we get a new customer or if we are changing the customer, it will cascade that. If it is a delete, it won't delete that from the database.
So running our application again, everything still works as expected.
New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
The orders were ordered by:
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
John Doe (10b2a3d7-7fcf-483c-b1da-a5bb00b8512e)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: e6680e30-5b3b-4efa-b017-a5bb00b85133
Order Id: b03858e7-8c36-4555-8878-a5bb00b85134
Press <ENTER> to exit...
Now you should have a look at your application, remember that the default is None and you have to think about your entities and your relationships between them to determine the appropriate cascades for each of your entities as well as each of your relationships in that database.
In this chapter, we will be covering the lazy loading feature. It is an entirely different concept by default and NHibernate doesn't have lazy loading, for example if you load a customer, it's not going to load all of the orders.
The order collection will be loaded on demand.
Any association, whether it be a many-to-one or a collection is lazy loaded by default, it requires an Open ISession.
If you have closed your session, or if you have committed your transaction, you can get a lazy load exception that it cannot pull in those additional objects.
You have to be careful about lazy loading and how much data you actually need.
You can turn off lazy loading for an entire association or you could put lazy equals false or you can also specify a fetching strategy.
Here is the Program.cs file implementation.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
Guid id;
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
id = newCustomer.Id;
tx.Commit();
}
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var reloaded = session.Load<Customer>(id);
Console.WriteLine("Reloaded:");
Console.WriteLine(reloaded);
Console.WriteLine("The orders were ordered by: ");
foreach (var order in reloaded.Orders) {
Console.WriteLine(order.Customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Customer CreateCustomer() {
var customer = new Customer {
FirstName = "John",
LastName = "Doe",
Points =100,
HasGoldStatus = true,
MemberSince = new DateTime(2012, 1, 1),
CreditRating = CustomerCreditRating.Good,
AverageRating = 42.42424242,
Address = CreateLocation()
};
var order1 = new Order { Ordered = DateTime.Now };
customer.AddOrder(order1);
var order2 = new Order {
Ordered = DateTime.Now.AddDays(-1),
Shipped = DateTime.Now,
ShipTo = CreateLocation()
};
customer.AddOrder(order2); return customer;
}
private static Location CreateLocation() {
return new Location {
Street = "123 Somewhere Avenue",
City = "Nowhere",
Province = "Alberta",
Country = "Canada"
};
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect<();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
To understand this, let's run the application and take a look at the NHibernate Profiler.
As you can see that we have the Select From Customer, given a particular customer ID and then we also have another Select From Orders table, when it actually accesses that customer's collection.
So we have 2 roundtrips to the database. Now, sometimes, we would want to optimize this. To do this, let’s go to the customer.hbm.xml file and add a fetching strategy and ask it to do a join fetch.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"
fetch = "join">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</set>
</class>
</hibernate-mapping>
As you can see that we haven't changed any code in our application, we have just added a fetching strategy in the customer.hbm.xml. Let’s run this application again, it still behaves exactly the same way. Let's look at NHibernate Profiler.
Before, program had two round trips to the database, now, it only has one and that's because it's doing a left outer join here.
We can see that it's doing a left outer join between the customer table and the order table based on the customer ID, and therefore, it's able to load in all of that information at once.
We have saved 1 roundtrip to the database.
The down side is that the customer information will be duplicated on both lines and that's the way that a SQL left outer join works.
So with the fetching strategy, we are pulling back a bit more data and we are saving a roundtrip.
Sie können dies auch auf Abfrageebene tun. Gehen wir also zu Program.cs Datei und schauen Sie sich das einfachere neu geladene Beispiel an.
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
//var query = from customer in session.Query<Customer>()
// select customer;
//var reloaded = query.Fetch(x => x.Orders).ToList();
var reloaded = session.Load<Customer>(id);
Console.WriteLine("Reloaded:");
Console.WriteLine(reloaded);
Console.WriteLine("The orders were ordered by: ");
foreach (var order in reloaded.Orders) {
Console.WriteLine(order.Customer);
}
tx.Commit();
}
Hier machen wir eine Ladung durch den Kunden. Jetzt ändern wir es in eine Abfrage und wir werden eine Linkabfrage verwenden, wie im folgenden Code gezeigt.
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var query = from customer in session.Query<Customer>()
where customer.Id == id select customer;
var reloaded = query.Fetch(x => x.Orders).ToList().First();
Console.WriteLine("Reloaded:");
Console.WriteLine(reloaded);
tx.Commit();
}
Entfernen wir auch die Abrufstrategie aus dem customer.hbm.xml Datei.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<set name = "Orders" table = "`Order`" cascade = "all-delete-orphan">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</set>
</class>
</hibernate-mapping>
Lassen Sie uns diese Anwendung erneut ausführen und Sie werden die folgende Ausgabe sehen.
New Customer:
John Doe (00000000-0000-0000-0000-000000000000)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Unspecified)
CreditRating: Good
AverageRating: 42.42424242
Orders:
Order Id: 00000000-0000-0000-0000-000000000000
Order Id: 00000000-0000-0000-0000-000000000000
Reloaded:
John Doe (6ebacd17-f9ba-4ad8-9817-a5bb01112a5a)
Points: 100
HasGoldStatus: True
MemberSince: 1/1/2012 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 42.4242
Orders:
Order Id: 16a6596b-d56e-41c7-9681-a5bb01112a60
Order Id: d41d615b-0f21-4032-81db-a5bb01112a61
Press <ENTER> to exit...
Schauen wir uns nun den NHibernate Profiler an. Sie können sehen, dass dieser eifrige Join-Abruf erneut stattfindet, aber diesmal basiert er auf der Abfrage.
In diesem Kapitel werden wir eine weitere Funktion behandeln, nämlich Inverse Beziehungen. Es ist eine amüsante Option, die Sie in einer Sammlung sehen werden, die umgekehrt gleich true ist, und sie verwirrt auch viele Entwickler. Sprechen wir also über diese Option. Um dies zu verstehen, muss man wirklich über das relationale Modell nachdenken. Angenommen, Sie haben bidirektionale Zuordnungen mit einem einzelnen Fremdschlüssel.
Aus relationaler Sicht haben Sie einen Fremdschlüssel, der sowohl Kundenbestellung als auch Kundenbestellung darstellt.
Aus dem OO-Modell haben Sie unidirektionale Assoziationen, die diese Referenzen verwenden.
Es gibt nichts, was besagt, dass zwei unidirektionale Assoziationen dieselbe bidirektionale Assoziation in der Datenbank darstellen.
Das Problem hierbei ist, dass NHibernate nicht über genügend Informationen verfügt, um dies zu wissen customer.orders und order.customer repräsentieren die gleiche Beziehung in der Datenbank.
Wir müssen liefern inverse equals true Dies ist ein Hinweis darauf, dass die unidirektionalen Zuordnungen dieselben Daten verwenden.
Wenn wir versuchen, diese Beziehungen mit zwei Verweisen zu speichern, versucht NHibernate, diesen Verweis zweimal zu aktualisieren.
Es wird tatsächlich eine zusätzliche Hin- und Rückfahrt zur Datenbank durchgeführt, und es werden auch zwei Aktualisierungen dieses Fremdschlüssels vorgenommen.
Die Umkehrung gleich true teilt NHibernate mit, welche Seite der Beziehung ignoriert werden soll.
Wenn Sie es auf die Auflistungsseite anwenden, aktualisiert NHibernate den Fremdschlüssel immer von der anderen Seite, von der Seite des untergeordneten Objekts.
Dann haben wir nur ein Update für diesen Fremdschlüssel und wir haben keine zusätzlichen Updates für diese Daten.
Auf diese Weise können wir diese doppelten Aktualisierungen des Fremdschlüssels verhindern und Verstöße gegen Fremdschlüssel verhindern.
Werfen wir einen Blick auf die customer.cs Datei, in der Sie die sehen AddOrderMethode und die Idee hier ist, dass wir jetzt diesen Rückzeiger von der Bestellung zurück zum Kunden haben und er gesetzt werden muss. Wenn also eine Bestellung zu einem Kunden hinzugefügt wird, wird der Rückzeiger dieses Kunden gesetzt, andernfalls wäre er null. Daher benötigen wir dies, um diese im Objektdiagramm ordnungsgemäß miteinander zu verbinden.
using System;
using System.Text;
using Iesi.Collections.Generic;
namespace NHibernateDemo {
public class Customer {
public Customer() {
MemberSince = DateTime.UtcNow; Orders = new HashedSet<Order>();
}
public virtual Guid Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
public virtual double AverageRating { get; set; }
public virtual int Points { get; set; }
public virtual bool HasGoldStatus { get; set; }
public virtual DateTime MemberSince { get; set; }
public virtual CustomerCreditRating CreditRating { get; set; }
public virtual Location Address { get; set; }
public virtual ISet<Order> Orders { get; set; }
public virtual void AddOrder(Order order) { Orders.Add(order); order.Customer = this; }
public override string ToString() {
var result = new StringBuilder();
result.AppendFormat("{1} {2} ({0})\r\n\tPoints: {3}\r\n\tHasGoldStatus:
{4}\r\n\tMemberSince: {5} ({7})\r\n\tCreditRating: {6}\r\n\tAverageRating:
{8}\r\n", Id, FirstName, LastName, Points, HasGoldStatus, MemberSince,
CreditRating, MemberSince.Kind, AverageRating);
result.AppendLine("\tOrders:");
foreach(var order in Orders) {
result.AppendLine("\t\t" + order);
}
return result.ToString();
}
}
public class Location {
public virtual string Street { get; set; }
public virtual string City { get; set; }
public virtual string Province { get; set; }
public virtual string Country { get; set; }
}
public enum CustomerCreditRating {
Excellent,
VeryVeryGood,
VeryGood,
Good,
Neutral,
Poor,
Terrible
}
}
Hier ist das Program.cs Dateiimplementierung.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
Guid id;
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var newCustomer = CreateCustomer();
Console.WriteLine("New Customer:");
Console.WriteLine(newCustomer);
session.Save(newCustomer);
id = newCustomer.Id;
tx.Commit();
}
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var query = from customer in session.Query<Customer>() where
customer.Id == id select customer;
var reloaded = query.Fetch(x => x.Orders).ToList().First();
Console.WriteLine("Reloaded:"); Console.WriteLine(reloaded);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Customer CreateCustomer() {
var customer = new Customer {
FirstName = "John",
LastName = "Doe",
Points = 100,
HasGoldStatus = true,
MemberSince = new DateTime(2012, 1, 1),
CreditRating = CustomerCreditRating.Good,
AverageRating = 42.42424242,
Address = CreateLocation()
};
var order1 = new Order { Ordered = DateTime.Now };
customer.AddOrder(order1); var order2 = new Order {
Ordered = DateTime.Now.AddDays(-1),
Shipped = DateTime.Now,
ShipTo = CreateLocation()
};
customer.AddOrder(order2);
return customer;
}
private static Location CreateLocation() {
return new Location {
Street = "123 Somewhere Avenue",
City = "Nowhere",
Province = "Alberta",
Country = "Canada"
};
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Es wird das in der Datenbank speichern und dann neu laden. Lassen Sie uns nun Ihre Anwendung ausführen und den NHibernate Profiler öffnen und sehen, wie er tatsächlich gespeichert wurde.
Sie werden feststellen, dass wir 3 Gruppen von Aussagen haben. Der erste fügt den Kunden ein, und die ID dieses Kunden ist die Guid, die hervorgehoben ist. Die zweite Anweisung wird in die Auftragstabelle eingefügt.
Sie werden feststellen, dass dort dieselbe Kunden-ID-Richtlinie festgelegt ist. Stellen Sie daher den Fremdschlüssel ein. Die letzte Anweisung ist das Update, mit dem der Fremdschlüssel erneut auf dieselbe Kunden-ID aktualisiert wird.
Das Problem ist nun, dass der Kunde die Bestellungen hat und die Bestellungen den Kunden haben. Wir haben NHibernate auf keinen Fall gesagt, dass es sich tatsächlich um dieselbe Beziehung handelt. Die Art und Weise, wie wir dies tun, ist mit invers gleich wahr.
Also lass uns zu unserem gehen customer.hbm.xml Mapping-Datei und setzen Sie die Umkehrung auf true, wie im folgenden Code gezeigt.
<?xml version = "1.0" encoding = "utf-8" ?>
<hibernate-mapping xmlns = "urn:nhibernate-mapping-2.2" assembly = "NHibernateDemo"
namespace = "NHibernateDemo">
<class name = "Customer">
<id name = "Id">
<generator class = "guid.comb"/>
</id>
<property name = "FirstName"/>
<property name = "LastName"/>
<property name = "AverageRating"/>
<property name = "Points"/>
<property name = "HasGoldStatus"/>
<property name = "MemberSince" type = "UtcDateTime"/>
<property name = "CreditRating" type = "CustomerCreditRatingType"/>
<component name = "Address">
<property name = "Street"/>
<property name = "City"/>
<property name = "Province"/>
<property name = "Country"/>
</component>
<set name = "Orders" table = "`Order`" cascade = "all-delete-orphan"
inverse = "true">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</set>
</class>
</hibernate-mapping>
Beim Speichern der Bestellungen wird dieser Fremdschlüssel auf der Bestellseite festgelegt. Führen Sie nun diese Anwendung erneut aus und öffnen Sie den NHibernate-Profiler.
Wenn wir uns ansehen, wie diese eingefügt werden, erhalten wir die Einfügung im Kunden und die Einfügung in Bestellungen, aber wir haben diese doppelte Aktualisierung des Fremdschlüssels nicht, da sie aktualisiert wird, wenn die Bestellungen gespeichert werden.
Nun sollten Sie beachten, dass, wenn Sie nur eine unidirektionale Zuordnung haben und es die Menge ist, die diese Beziehung aufrechterhält, wenn Sie invers gleich true werden, dieser Fremdschlüssel niemals gesetzt wird und diese Elemente niemals ihre haben werden Fremdschlüssel in der Datenbank festgelegt.
Wenn Sie sich die Viele-zu-Eins-Beziehung in der Order.hbm.xml Datei und Sie suchen nach inversen, es hat eigentlich kein inverses Attribut.
Es wird immer aus dem untergeordneten Element festgelegt. Wenn Sie jedoch über eine Viele-zu-Viele-Sammlung verfügen, können Sie es von beiden Seiten festlegen.
In diesem Kapitel werden wir beschreiben, wie die Funktionen Laden und Abrufen funktionieren und wie wir sie verwenden können. Dies sind zwei sehr ähnliche APIs, die von bereitgestellt werdenISession zum Laden eines Objekts per Primärschlüssel.
Get - Es wird das Objekt oder eine Null zurückgegeben.
Load - es wird das Objekt zurückgeben oder es wird ein werfen ObjectNotFoundException.
Warum haben wir diese zwei verschiedenen APIs?
Belastung
Dies liegt daran, dass Load Datenbank-Roundtrips viel effizienter optimieren kann.
Load gibt tatsächlich ein Proxy-Objekt zurück und muss nicht direkt auf die Datenbank zugreifen, wenn Sie diesen Load-Aufruf ausführen.
Wenn Sie auf diesen Proxy zugreifen, befindet sich das Objekt nicht in der Datenbank, sondern kann an diesem Punkt eine ObjectNotFoundException auslösen.
Bekommen
Umgekehrt mit Get wegen Einschränkungen der CLR oder Common Language Runtime und NHibernate muss sofort zur Datenbank gehen, prüfen, ob die Objekte vorhanden sind, und null zurückgeben, wenn sie nicht vorhanden sind.
Es hat nicht die Objektoption, diesen Abruf, diesen Roundtrip zur Datenbank zu einem späteren Zeitpunkt zu verzögern, da es kein Proxy-Objekt zurückgeben kann und dieses Proxy-Objekt gegen eine Null ausgetauscht hat, wenn der Benutzer tatsächlich darauf zugreift.
Schauen wir uns ein einfaches Beispiel an, in dem Sie sehen, wie diese tatsächlich verwendet werden und welchen Unterschied es zwischen Get und Load gibt. Wir werden mit denselben Domainklassen fortfahrenCustomers und Orders und ähnlich die gleichen Mapping-Dateien aus dem letzten Kapitel.
In diesem Beispiel verwenden wir zuerst Get, wie im folgenden Programm gezeigt.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var id1 = Guid.Parse("4e97c816-6bce-11e1-b095-6cf049ee52be");
var id2 = Guid.Parse("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE");
var customer1 = session.Get<Customer>(id1);
Console.WriteLine("Customer1 data");
Console.WriteLine(customer1);
var customer2 = session.Get<Customer>(id2);
Console.WriteLine("Customer2 data");
Console.WriteLine(customer2);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Wie Sie sehen, haben wir zwei GuidIDs, die erste ist eine gute ID, es ist die ID eines Kunden, von dem wir wissen, dass er sich in der Datenbank befindet. Während die zweite ID nicht in der Datenbank vorhanden ist. Diese beiden IDs werden als Parameter an übergebenGet() Methode und dann wird das Ergebnis auf der Konsole gedruckt.
Wenn der obige Code kompiliert und ausgeführt wird, wird die folgende Ausgabe angezeigt.
Customer1 data
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Customer2 data
Press <ENTER> to exit...
Wie Sie sehen können, werden Customer1-Daten gedruckt, aber Customer2-Daten sind leer, da der Customer2-Datensatz nicht in der Datenbank verfügbar ist.
Wenn Sie Ihre Anwendung erneut ausführen, können wir vor der Festschreibungsanweisung einen Haltepunkt einfügen und dann beide Kunden im Überwachungsfenster betrachten.
Wie Sie sehen können, sind Customer1-Daten verfügbar, während Customer2 null und der Typ ist NHibernateDemo.Customer für beide.
Verwenden wir nun die Load-Methode anstelle von Get im selben Beispiel wie im folgenden Code.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var id1 = Guid.Parse("4e97c816-6bce-11e1-b095-6cf049ee52be");
var id2 = Guid.Parse("AAAAAAAA-BBBB-CCCC-DDDD-EEEEEEEEEEEE");
var customer1 = session.Load<Customer>(id1);
Console.WriteLine("Customer1 data");
Console.WriteLine(customer1);
var customer2 = session.Load<Customer>(id2);
Console.WriteLine("Customer2 data");
Console.WriteLine(customer2);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Lassen Sie uns nun dieses Beispiel ausführen und Sie werden sehen, dass die folgende Ausnahme ausgelöst wird, wie im Screenshot gezeigt.
Wenn Sie sich nun das Überwachungsfenster ansehen, sehen Sie, dass der Typ für beide Objekte ein Kundenproxy ist. Die gleichen Daten für Customer1 werden auch im Konsolenfenster angezeigt.
Customer1 data
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Customer2 data
In diesem Kapitel werden wir eine weitere gängige API behandeln, die von Menschen verwendet wird, nämlich den NHibernate LINQ-Anbieter. Sein Zugriff über eine Erweiterungsmethode auf ISession und die Signatur ist aQuery <T>. Bei der Verwendung von LINQ gibt es zwei Arten von Syntax:
- Syntax zur Abfrageverkettung
- Syntax zum Abfrageverständnis
Syntax zur Abfrageverkettung
Sie können mit der im folgenden Programm gezeigten Methodenkettensyntax auf jeden Datensatz aus der Datenbank zugreifen.
var customer = session.Query<Customer>() .Where(c => c.FirstName == "Laverne")
Sie können sehen, dass wir Abfrage haben, und auch WHERE-Klausel, Sie können zusätzliche WHERE-Klauseln und ähnlich select-Klausel haben.
Dies ist eine Standard-Syntax für die Methodenkette, die Sie in normalem LINQ verwenden können.
LINQ to Objects oder LINQ to SQL, einer der anderen LINQ-Anbieter, mit denen Sie möglicherweise vertraut sind.
Schauen wir uns ein einfaches Beispiel an, in dem wir den Kunden abrufen, dessen Vorname Laverne ist. Jetzt besteht die Möglichkeit, dass wir mehr als einen Kunden haben, dessen Vorname Laverne ist, sodass wir nur den ersten abrufen.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customer = session.Query<Customer>()
.Where(c => c.FirstName == "Laverne").First();
Console.WriteLine(customer);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Wenn der obige Code kompiliert und ausgeführt wird, wird die folgende Ausgabe angezeigt.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Press <ENTER> to exit...
Syntax zum Abfrageverständnis
Es gibt auch die Syntax für das Abfrageverständnis, die SQL mit den Schlüsselwörtern from, where und select viel ähnlicher ist.
Schauen wir uns also dasselbe Beispiel an, aber dieses Mal verwenden wir die LINQ-Verständnis-Syntax, die viel mehr wie SQL aussieht, wie im folgenden Programm gezeigt.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customer = (from c in session.Query<Customer>()
where c.FirstName == "Laverne" select c).First();
Console.WriteLine(customer);
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Führen Sie diese Anwendung nun erneut aus, und Sie sehen die folgende Ausgabe.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Press <ENTER> to exit...
Schauen wir uns ein anderes Beispiel an, in dem wir alle Kunden abrufen, deren Vorname mit dem Buchstaben H beginnt.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = session.Query<Customer>() .Where(c =<
c.FirstName.StartsWith("H"));
foreach (var customer in customers.ToList()) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
In ähnlicher Weise sieht die Syntax des Abfrageverständnisses wie im folgenden Programm aus.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = from c in session.Query<Customer>()
where c.FirstName.StartsWith("H") select c;
foreach (var customer in customers.ToList()) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Lassen Sie uns diese Anwendung erneut ausführen und Sie werden alle Kunden sehen, deren Vorname mit dem Alphabet H beginnt.
Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 12/3/2010 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be
Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
Points: 56
HasGoldStatus: False
MemberSince: 10/20/2008 12:00:00 AM (Utc)
CreditRating: Terrible
AverageRating: 0
Orders:
Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be
Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
Points: 82
HasGoldStatus: False
MemberSince: 4/10/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be
Press <ENTER> to exit...
In diesem Kapitel werden wir uns mit der Abfragesprache im Ruhezustand befassen. HQL wird sowohl im Java-Ruhezustand als auch im NHibernate gemeinsam genutzt.
Es ist der älteste Abfragemechanismus zusammen mit Criteria.
Es wurde sehr früh implementiert und ist eine stringbasierte Abfrage API.
Sie greifen darauf zu ISession CreateQueryund es ist fast ähnlich wie SQL.
Es verwendet viele der gleichen Schlüsselwörter, hat jedoch eine vereinfachte Syntax.
Dies ist eines der häufigsten Beispiele. Wenn Sie nach einer Abfrage suchen, finden Sie häufig HQL-Beispiele.
Das Folgende ist ein einfaches Beispiel für HQL -
var customers = session.CreateQuery("select c from Customer c where c.FirstName = 'Laverne'");
Hier können Sie also sehen, dass sie C vom Kunden auswählen. Es sieht sehr nach SQL aus. Für NHibernate ist dies eine undurchsichtige Zeichenfolge. Sie wissen also nicht, ob dies bis zur Laufzeit eine gültige HQL ist, was einer der Nachteile ist.
Eine der Stärken des LINQ-Anbieters besteht darin, dass Sie Zeitunterstützung für die Kompilierung erhalten.
HQL ist jedoch einer der flexibelsten Abfragemechanismen, die häufig verwendet werden. Es heißt, wenn es keinen anderen Weg gibt, gibt es einen Weg, dies in HQL zu tun.
Schauen wir uns ein einfaches Beispiel an, in dem wir unsere LINQ-Abfragen stattdessen mit HQL neu erstellen. Sie können auf HQL zugreifen, indem Sie diesession.CreateQuery und als Parameter mit einer HQL-Zeichenfolge übergeben.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = session.CreateQuery("select c from Customer c
where c.FirstName = 'Laverne'");
foreach (var customer in customers.List<Customer>()) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Diese HQL-Zeichenfolge ähnelt stark SQL. Der Hauptunterschied besteht darin, dass FirstName der Eigenschaftsname und nicht der Spaltenname ist.
Wenn also eine Diskrepanz zwischen den beiden besteht, verwenden Sie den Eigenschaftsnamen. Das Gleiche sieht aus wie ein Tabellenname, aber es ist tatsächlich der Name der Klasse, aus der wir auswählen.
Wenn die Back-End-Tabelle als Kunden benannt würde, würden wir weiterhin Kunden in unserer HQL-Abfrage verwenden.
Lassen Sie uns diese Anwendung ausführen und Sie werden die folgende Ausgabe sehen.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Press <ENTER> to exit...
Schauen wir uns ein weiteres einfaches Beispiel an, in dem wir alle Kunden abrufen, deren Vorname mit dem Buchstaben H unter Verwendung von HQL beginnt.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = session.CreateQuery("select c from Customer c
where c.FirstName like 'H%'");
foreach (var customer in customers.List<Customer>()) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Lassen Sie uns Ihre Anwendung erneut ausführen und Sie werden sehen, dass alle Kunden, deren Name mit H beginnt, von dieser Abfrage zurückgegeben werden.
Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 12/3/2010 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be
Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
Points: 56
HasGoldStatus: False
MemberSince: 10/20/2008 12:00:00 AM (Utc)
CreditRating: Terrible
AverageRating: 0
Orders:
Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be
Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
Points: 82
HasGoldStatus: False
MemberSince: 4/10/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be
Press <ENTER> to exit...
Wir können kompliziertere Dinge tun, z. B. alle Bestellungen wünschen, bei denen Kunden mit einer Auftragsanzahl größer als 9 sind. Es folgt die HQL-Abfrage für dieselbe.
var customers = session.CreateQuery("select c from Customer c
where size(c.Orders) > 9");
foreach (var customer in customers.List<Customer>()) {
Console.WriteLine(customer);
}
Wir müssen auch angeben, dass wir hier eine Größe oder Anzahl oder Länge benötigen. In HQL haben wir die Möglichkeit, die oben gezeigte spezielle Größenmethode zu verwenden.
Der andere Weg, dies zu schreiben, wenn Sie es vorziehen, ist c.Orders.size, und das hat genau die Wirkung.
var customers = session.CreateQuery("select c from Customer c
where c.Orders.size > 9");
foreach (var customer in customers.List<Customer>()) {
Console.WriteLine(customer);
}
Lassen Sie uns diese Anwendung ausführen.
Lindsay Towne (4ea3aef6-6bce-11e1-b0cb-6cf049ee52be)
Points: 50
HasGoldStatus: False
MemberSince: 4/13/2007 12:00:00 AM (Utc)
CreditRating: VeryGood
AverageRating: 0
Orders:
Order Id: 4ea3aef6-6bce-11e1-b0cc-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0cd-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0ce-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0cf-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0d0-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0d1-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0d2-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0d3-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0d4-6cf049ee52be
Order Id: 4ea3aef6-6bce-11e1-b0d5-6cf049ee52be
Wyman Hammes (4ea61056-6bce-11e1-b0e2-6cf049ee52be)
Points: 32
HasGoldStatus: False
MemberSince: 2/5/2011 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 0
Orders:
Order Id: 4ea61056-6bce-11e1-b0e3-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0e4-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0e5-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0e6-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0e7-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0e8-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0e9-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0ea-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0eb-6cf049ee52be
Order Id: 4ea61056-6bce-11e1-b0ec-6cf049ee52be
Press <ENTER> to exit...
Sie können sehen, dass alle Kunden mit mehr als 9 Bestellungen aus der Datenbank abgerufen werden.
In diesem Kapitel werden wir den Mechanismus für Kriterienabfragen behandeln. DasNHibernate Query by Criteria API Mit dieser Option können Sie eine Abfrage erstellen, indem Sie zur Laufzeit Kriterienobjekte bearbeiten.
Mit diesem Ansatz können Sie Einschränkungen dynamisch angeben, ohne direkte Zeichenfolgenmanipulationen durchführen zu müssen, ohne jedoch die Flexibilität oder Leistungsfähigkeit von HQL zu beeinträchtigen.
Andererseits sind als Kriterien ausgedrückte Abfragen oft weniger lesbar als in HQL ausgedrückte Abfragen.
Die klassische Kriteriensyntax ist eine objektbasierte Abfrage-API, wie im folgenden Programm gezeigt.
var customers = session.CreateCriteria<Customer>().Add(Restrictions.Like("FirstName", "H%"));
Wie Sie sehen, erstellen wir eine Sitzung, um Kriterien für den Kunden zu erstellen, und fügen dieser Abfrage nun ein Einschränkungsobjekt hinzu.
Dies ist nützlich für Abfrageseiten, auf denen Benutzer bestimmte Optionen auswählen können, andere jedoch nicht.
Es ist einfacher, die Abfrage als eine Art baumähnliche Abfragestruktur aufzubauen, als in HQL oder LINQ, wo Sie das AND oder OR in der WHERE-Klausel verwenden können.
Es ist einfacher, mit diesen Kriterienobjekten zusätzliche Einschränkungen hinzuzufügen.
Schauen wir uns ein einfaches Beispiel an, in dem wir eine Abfrage erstellen und über die Kriterien-API Zugriff erhalten createCriteria und fügen Sie dann eine Einschränkung hinzu, dass der Vorname mit H beginnt.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = session.CreateCriteria<Customer>()
.Add(Restrictions.Like("FirstName", "H%"));
foreach (var customer in customers.List<Customer>()) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Wenn der obige Code kompiliert und ausgeführt wird, wird die folgende Ausgabe angezeigt.
Herman Crooks (4ead3480-6bce-11e1-b15c-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 12/3/2010 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ead3480-6bce-11e1-b15d-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b15e-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b15f-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b160-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b161-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b162-6cf049ee52be
Order Id: 4ead3480-6bce-11e1-b163-6cf049ee52be
Hudson Bins (4ec03f80-6bce-11e1-b2b7-6cf049ee52be)
Points: 56
HasGoldStatus: False
MemberSince: 10/20/2008 12:00:00 AM (Utc)
CreditRating: Terrible
AverageRating: 0
Orders:
Order Id: 4ec03f80-6bce-11e1-b2b8-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2b9-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2ba-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bb-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bc-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bd-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2be-6cf049ee52be
Order Id: 4ec03f80-6bce-11e1-b2bf-6cf049ee52be
Hettie Feest (4ec50240-6bce-11e1-b300-6cf049ee52be)
Points: 82
HasGoldStatus: False
MemberSince: 4/10/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ec50240-6bce-11e1-b301-6cf049ee52be
Order Id: 4ec50240-6bce-11e1-b302-6cf049ee52be
Order Id: 4ec50240-6bce-11e1-b303-6cf049ee52be
Press <ENTER> to exit…
Schauen wir uns ein weiteres einfaches Beispiel an, in dem wir den Kunden abrufen, dessen Vorname gleich "Laverne" ist.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = session.CreateCriteria<Customer>()
.Add(Restrictions.Eq("FirstName", "Laverne")) .List<Customer>();
foreach (var customer in customers) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Lassen Sie uns diese Anwendung erneut ausführen und Sie werden die folgende Ausgabe sehen.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Press <ENTER> to exit...
Einer der Hauptnachteile der Kriterien-API sind diese undurchsichtigen Zeichenfolgen in den Eigenschaftsnamen. Wenn also der Vorname als etwas anderes umgestaltet würde, würde das Refactoring-Tool nicht unbedingt die undurchsichtige Zeichenfolge aufnehmen.
In diesem Kapitel werden QueryOver-Abfragen behandelt. Es handelt sich um eine neue Syntax, die eher LINQ ähnelt und die in der folgenden Abfrage gezeigte Methodenkettensyntax verwendet.
var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "Laverne");
Es ist immer noch ein Kriterium unter dem Deckmantel, aber jetzt sind unsere Anfragen stark typisiert.
Wie wir in der Kriterienabfrage gesehen haben, ist der Vorname nur eine undurchsichtige Zeichenfolge, jetzt verwenden wir tatsächlich eine x.FirstNameDaher wird der Vorname überarbeitet und umbenannt, der in der Abfrage der Kriterien für den Verbindungsstil mithilfe der Abfrage über geändert wird.
Wir können immer noch viele ähnliche Dinge tun, aber Sie können die Syntax des Abfrageverständnisses nicht mit Abfrage verwenden, Sie müssen die Syntax der Methodenkette verwenden und Sie können den Link und die Kriterien nicht mischen und abgleichen.
Für viele Abfragen ist die Abfrage über die API sehr nützlich und bietet eine viel einfachere Verständnis der Objektsyntax als die direkte Verwendung von Kriterien.
Schauen wir uns ein einfaches Beispiel an, in dem wir einen Kunden mit dem Vornamen Laverne mithilfe einer Abfrage abrufen.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
var customers = session.QueryOver<Customer>()
.Where(x => x.FirstName == "Laverne");
foreach (var customer in customers.List()) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Wie Sie sehen können, ist es immer noch Kriterien unter dem Cover, aber es ist nur eine schönere Syntax.
Wenn der obige Code kompiliert und ausgeführt wird, wird die folgende Ausgabe angezeigt.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Press <ENTER> to exit...
Einer der Nachteile ist, dass wir das sagen wollen FirstName.StartsWith(“A”) wie im folgenden Programm gezeigt.
var customers = session.QueryOver<Customer>() .Where(x => x.FirstName.StartsWith("A"));
foreach (var customer in customers.List()) {
Console.WriteLine(customer);
}
tx.Commit();
Lassen Sie uns nun die Anwendung erneut ausführen und Sie werden sehen, dass dies kein LINQ-Anbieter ist, da er nicht weiß, was dies ist StartsWith Methode ist, so erhalten Sie eine RunTime exception.
Die Ausnahme besagt, dass der Methodenaufruf nicht erkannt wurde. Hier machen wir das Offensichtliche, aber es funktioniert nicht unbedingt.
Versuchen wir etwas anderes, wie Vorname ist gleich "A%", wie im folgenden Code gezeigt.
var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "A%");
foreach (var customer in customers.List()) {
Console.WriteLine(customer);
}
Lassen Sie uns dies noch einmal ausführen und Sie werden sehen, dass wir keine Ergebnisse zurückerhalten werden, wie unten gezeigt.
Press <ENTER> to exit...
Um zu verstehen, warum wir keine Ergebnisse erhalten, werfen wir einen Blick auf den NHibernate-Profiler.
Wie Sie sehen können, ist der Vorname gleich A%, was nicht der Fall ist. Ein% wird in SQL mit dem ähnlichen Operator verwendet. Jetzt müssen wir eine Einschränkung in der WHERE-Klausel erstellen, wie im folgenden Programm gezeigt.
var customers = session.QueryOver<Customer>()
.Where(Restrictions.On<Customer>(c => c.FirstName).IsLike("A%"));
foreach (var customer in customers.List()) {
Console.WriteLine(customer);
}
Lassen Sie uns Ihre Anwendung erneut ausführen und Sie werden sehen, dass alle Kunden mit dem Vornamen abgerufen werden, der mit A beginnt.
Alejandrin Will (4ea3aef6-6bce-11e1-b0b4-6cf049ee52be)
Points: 24
HasGoldStatus: False
MemberSince: 10/1/2011 12:00:00 AM (Utc)
CreditRating: VeryVeryGood
AverageRating: 0
Orders:
Order Id: 4ea3aef6-6bce-11e1-b0b5-6cf049ee52be
Austyn Nolan (4ea871b6-6bce-11e1-b110-6cf049ee52be)
Points: 67
HasGoldStatus: True
MemberSince: 12/29/2007 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea871b6-6bce-11e1-b111-6cf049ee52be
Antonia Murphy (4ea871b6-6bce-11e1-b121-6cf049ee52be)
Points: 72
HasGoldStatus: True
MemberSince: 6/15/2009 12:00:00 AM (Utc)
CreditRating: Terrible
AverageRating: 0
Orders:
Order Id: 4ea871b6-6bce-11e1-b122-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b123-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b124-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b125-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b126-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b127-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b128-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b129-6cf049ee52be
Order Id: 4ea871b6-6bce-11e1-b12a-6cf049ee52be
Es funktioniert genauso wie zuvor, außer dass dieses neue verwendet wird QueryOverSyntax. Viele Entwickler finden, dass die LINQ-Syntax zugänglicher ist und oft die richtigen Dinge tut.
Wenn LINQ damit nicht umgehen kann, werden Sie sich HQL oder Kriterien ansehen, um festzustellen, ob dies besser geeignet ist.
Es gibt Ihnen nur eine andere Syntax, daher bieten Kriterien, sowohl die Erstellungskriterien als auch QueryOver, nur einen weiteren Abfragemechanismus, mit dem Sie Daten mit NHibernate aus der Datenbank abrufen können.
In diesem Kapitel wird erläutert, wie die nativen SQL-Abfragen in NHibernate verwendet werden. Wenn Sie seit einigen Jahren handgeschriebenes SQL verwenden, können Sie befürchten, dass ORM Ihnen die gewohnte Ausdruckskraft und Flexibilität nimmt.
Mit den leistungsstarken Abfragefunktionen von NHibernate können Sie fast alles tun, was Sie in SQL tun würden, und in einigen Fällen auch mehr.
In den seltenen Fällen, in denen die NHibernate-eigenen Abfragefunktionen nicht genau das tun können, was Sie möchten.
Mit NHibernate können Sie Objekte mit dem nativen SQL-Dialekt Ihrer Datenbank abrufen.
Schauen wir uns ein einfaches Beispiel für die Native SQL-Abfragen in NHibernate an.
using System;
using System.Data;
using System.Linq;
using System.Reflection;
using HibernatingRhinos.Profiler.Appender.NHibernate;
using NHibernate.Cfg;
using NHibernate.Criterion;
using NHibernate.Dialect;
using NHibernate.Driver;
using NHibernate.Linq;
using NHibernate;
namespace NHibernateDemo {
internal class Program {
private static void Main() {
var cfg = ConfigureNHibernate();
var sessionFactory = cfg.BuildSessionFactory();
using(var session = sessionFactory.OpenSession())
using(var tx = session.BeginTransaction()) {
IQuery sqlQuery = session.CreateSQLQuery("SELECT * FROM
CUSTOMER").AddEntity(typeof(Customer));
var customers = sqlQuery.List<Customer>();
foreach (var customer in customers) {
Console.WriteLine(customer);
}
tx.Commit();
}
Console.WriteLine("Press <ENTER> to exit...");
Console.ReadLine();
}
private static Configuration ConfigureNHibernate() {
NHibernateProfiler.Initialize();
var cfg = new Configuration();
cfg.DataBaseIntegration(x => {
x.ConnectionStringName = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.IsolationLevel = IsolationLevel.RepeatableRead;
x.Timeout = 10;
x.BatchSize = 10;
});
cfg.SessionFactory().GenerateStatistics();
cfg.AddAssembly(Assembly.GetExecutingAssembly());
return cfg;
}
}
}
Das obige Beispiel verwendet CreateSQLQuery() Um eine Liste der Objekte zurückzugewinnen, werden Sie auch feststellen, dass der Stammentitätstyp, den die Abfrage zurückgeben soll, als Kunde angegeben ist.
Lassen Sie uns Ihre Anwendung ausführen und Sie werden sehen, dass alle Kunden aus der Datenbank abgerufen werden.
Emerson Prosacco (4ec2a0e0-6bce-11e1-b2cf-6cf049ee52be)
Points: 17
HasGoldStatus: False
MemberSince: 6/22/2007 12:00:00 AM (Utc)
CreditRating: Excellent
AverageRating: 0
Orders:
Order Id: 4ec2a0e0-6bce-11e1-b2d0-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d1-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d2-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d3-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d4-6cf049ee52be
Kaci Friesen (4ec2a0e0-6bce-11e1-b2d5-6cf049ee52be)
Points: 30
HasGoldStatus: True
MemberSince: 5/25/2007 12:00:00 AM (Utc)
CreditRating: VeryVeryGood
AverageRating: 0
Orders:
Order Id: 4ec2a0e0-6bce-11e1-b2d6-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d7-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d8-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2d9-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2da-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2db-6cf049ee52be
Eveline Waters (4ec2a0e0-6bce-11e1-b2dc-6cf049ee52be)
Points: 58
HasGoldStatus: False
MemberSince: 10/29/2009 12:00:00 AM (Utc)
CreditRating: Good
AverageRating: 0
Orders:
Order Id: 4ec2a0e0-6bce-11e1-b2dd-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2de-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2df-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e0-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e2-6cf049ee52be
Molly Kuhn (4ec2a0e0-6bce-11e1-b2e3-6cf049ee52be)
Points: 73
HasGoldStatus: False
MemberSince: 12/16/2007 12:00:00 AM (Utc)
CreditRating: VeryGood
AverageRating: 0
Orders:
Order Id: 4ec2a0e0-6bce-11e1-b2e4-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e5-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e6-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e7-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e8-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2e9-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2ea-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2eb-6cf049ee52be
Order Id: 4ec2a0e0-6bce-11e1-b2ec-6cf049ee52be
Hier ist eine andere Möglichkeit, eine native SQL-Abfrage zu schreiben, wie unten gezeigt.
IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER")
.AddScalar("Id", NHibernateUtil.Guid)
.AddScalar("FirstName", NHibernateUtil.String)
.AddScalar("LastName", NHibernateUtil.String) .List<Customer>();
Wie Sie sehen, hat die obige Abfrage die SQL-Abfragezeichenfolge sowie die zurückzugebenden Spalten und Typen angegeben.
Dadurch wird für jede Spalte in der Kundentabelle eine IList of Object-Anordnung mit skalaren Werten zurückgegeben.
Nur diese drei Spalten werden zurückgegeben, obwohl die Abfrage * verwendet und möglicherweise mehr als die drei aufgelisteten Spalten zurückgibt.
Schauen wir uns ein weiteres einfaches Beispiel an.
IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER WHERE
FirstName = 'Laverne'")
.AddEntity(typeof(Customer)) .List<Customer>();
foreach (var customer in customers) {
Console.WriteLine(customer);
}
Lassen Sie uns Ihre Anwendung erneut ausführen und Sie werden die folgende Ausgabe sehen.
Laverne Hegmann (4e97c816-6bce-11e1-b095-6cf049ee52be)
Points: 74
HasGoldStatus: True
MemberSince: 4/4/2009 12:00:00 AM (Utc)
CreditRating: Neutral
AverageRating: 0
Orders:
Order Id: 4ea14d96-6bce-11e1-b095-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b096-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b097-6cf049ee52be
Order Id: 4ea14d96-6bce-11e1-b098-6cf049ee52be
Press <ENTER> to exit...
Ebenso können Sie eine beliebige Art von SQL-Abfrage angeben, um Daten aus der Datenbank abzurufen.
In diesem Kapitel werden wir uns mit fließendem NHibernate befassen. Fließendes NHibernate ist eine andere Art der Zuordnung, oder Sie können sagen, es ist eine Alternative zu den Standard-XML-Zuordnungsdateien von NHibernate. Anstatt XML zu schreiben(.hbm.xml files)Unterlagen. Mit Hilfe von Fluent NHibernate können Sie Zuordnungen in stark typisiertem C # -Code schreiben.
In Fluent NHibernate werden Zuordnungen zusammen mit dem Rest Ihrer Anwendung erstellt.
Sie können Ihre Zuordnungen genauso wie Ihren Anwendungscode leicht ändern, und der Compiler schlägt bei Tippfehlern fehl.
Es verfügt über ein herkömmliches Konfigurationssystem, in dem Sie Muster zum Überschreiben von Namenskonventionen und vielen anderen Dingen angeben können.
Sie können auch festlegen, wie die Dinge einmal benannt werden sollen, dann erledigt Fluent NHibernate den Rest.
Schauen wir uns ein einfaches Beispiel an, indem wir ein neues Konsolenprojekt erstellen. In diesem Kapitel verwenden wir eine einfache Datenbank, in der wir eine einfache Kundentabelle haben, wie in der folgenden Abbildung gezeigt.
Installieren Sie Fluent NHibernate
Der erste Schritt zum Starten von Fluent NHibernate ist die Installation des Fluent NHibernate-Pakets. Also öffne dasNuGet Package Manager Console und geben Sie den folgenden Befehl ein.
PM> install-package FluentNHibernate
Nach erfolgreicher Installation wird die folgende Meldung angezeigt.
Fügen wir eine einfache Modellklasse des Kunden hinzu, und das folgende Programm zeigt die Implementierung der Kundenklasse.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FluentNHibernateDemo {
class Customer {
public virtual int Id { get; set; }
public virtual string FirstName { get; set; }
public virtual string LastName { get; set; }
}
}
Jetzt müssen wir Mappings mit fließendem NHibernate erstellen, also fügen Sie eine weitere Klasse hinzu CustomerMapin Ihrem Projekt. Hier ist die Implementierung der CustomerMap-Klasse.
using FluentNHibernate.Mapping;
using System;
using System.Collections.Generic;
using System.Linq; using System.Text;
using System.Threading.Tasks;
namespace FluentNHibernateDemo {
class CustomerMap : ClassMap<Customer> {
public CustomerMap() {
Id(x => x.Id);
Map(x => x.FirstName);
Map(x => x.LastName);
Table("Customer");
}
}
}
Fügen wir eine weitere Klasse hinzu NHibernateHelper in dem wir verschiedene Konfigurationseinstellungen vornehmen.
using FluentNHibernate.Cfg;
using FluentNHibernate.Cfg.Db;
using NHibernate;
using NHibernate.Tool.hbm2ddl;
namespace FluentNHibernateDemo {
public class NHibernateHelper {
private static ISessionFactory _sessionFactory;
private static ISessionFactory SessionFactory {
get {
if (_sessionFactory == null)
InitializeSessionFactory(); return _sessionFactory;
}
}
private static void InitializeSessionFactory() {
_sessionFactory = Fluently.Configure()
String Data Source = asia13797\\sqlexpress;
String Initial Catalog = NHibernateDemoDB;
String Integrated Security = True;
String Connect Timeout = 15;
String Encrypt = False;
String TrustServerCertificate = False;
String ApplicationIntent = ReadWrite;
String MultiSubnetFailover = False;
.Database(MsSqlConfiguration.MsSql2008 .ConnectionString(
@"Data Source + Initial Catalog + Integrated Security + Connect Timeout
+ Encrypt + TrustServerCertificate + ApplicationIntent +
MultiSubnetFailover") .ShowSql() )
.Mappings(m => m.FluentMappings
.AddFromAssemblyOf<Program>())
.ExposeConfiguration(cfg => new SchemaExport(cfg)
.Create(true, true))
.BuildSessionFactory();
}
public static ISession OpenSession() {
return SessionFactory.OpenSession();
}
}
}
Gehen wir jetzt zum Program.cs Datei, in der wir eine Sitzung starten und dann einen neuen Kunden erstellen und diesen Kunden wie unten gezeigt in der Datenbank speichern.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace FluentNHibernateDemo {
class Program {
static void Main(string[] args) {
using (var session = NHibernateHelper.OpenSession()) {
using (var transaction = session.BeginTransaction()) {
var customer = new Customer {
FirstName = "Allan",
LastName = "Bomer"
};
session.Save(customer);
transaction.Commit();
Console.WriteLine("Customer Created: " + customer.FirstName + "\t" +
customer.LastName);
}
Console.ReadKey();
}
}
}
}
Lassen Sie uns Ihre Anwendung ausführen und Sie werden die folgende Ausgabe sehen.
if exists (select * from dbo.sysobjects where id = object_id(N'Customer') and
OBJECTPROPERTY(id, N'IsUserTable') = 1) drop table Customer
create table Customer (
Id INT IDENTITY NOT NULL,
FirstName NVARCHAR(255) null,
LastName NVARCHAR(255) null,
primary key (Id)
)
NHibernate: INSERT INTO Customer (FirstName, LastName) VALUES (@p0, @p1);
select SCOPE_IDENTITY();@p0 = 'Allan' [Type: String (4000)],
@p1 = 'Bomer' [Type: String (4000)]
Customer Created: Allan Bomer
Wie Sie sehen, wird der neue Kunde erstellt. Um den Kundendatensatz anzuzeigen, gehen wir zur Datenbank und sehen die Daten anzeigen. Sie werden sehen, dass 1 Kunde hinzugefügt wurde.