NHibernate - Guía rápida
En este capítulo discutiremos sobre qué es NHibernate, en qué plataformas se puede implementar, cuáles son sus ventajas y otros aspectos relacionados.
¿Qué es NHibernate?
NHibernate es un mapeador relacional de objetos de código abierto maduro para el marco .NET. Está desarrollado de forma activa, con todas las funciones y se utiliza en miles de proyectos exitosos. Está construido sobreADO.NET y la versión actual es NHibernate 4.0.4.
NHibernate es un mapeador relacional de objetos .NET de código abierto y se distribuye bajo la GNU Lesser General Public License.
Está basado en Hibernate, que es un mapeador relacional de objetos de Java popular y tiene una base de código muy madura y activa.
Proporciona un marco para mapear un modelo de dominio orientado a objetos a una base de datos relacional tradicional.
NHibernate fue iniciado por Tom Barrett y este proyecto existe desde febrero de 2003, que fue su primer compromiso.
Es un gran proyecto y ofrece mucha funcionalidad.
Hay un NuGet package disponible, lo que hace que sea muy fácil de agregar a un proyecto.
¿Por qué NHibernate?
Ahora la pregunta es por qué necesitamos object-relational mappers? Es porque hay una desconexión entre el mundo de los objetos y el mundo relacional.
En el mundo de los objetos, todo se basa en objects; llamamos objetos a aquellas cosas que tienen nuestros datos.
El mundo relacional está basado en conjuntos y estamos tratando con tablas y filas que son diferentes al mundo de los objetos.
En el mundo de los objetos, tenemos unidirectional associations. Si un cliente tiene un puntero a un pedido, no significa necesariamente que un pedido tenga un puntero a un cliente, podría o no.
En el mundo relacional, todas las asociaciones son bidirectional y se puede hacer con una clave externa.
Todas las asociaciones son intrínsecamente bidireccionales, por lo que cuando se trata de un mapeo relacional de objetos, también debemos abordar esta desconexión.
En el mundo de los objetos, estamos trabajando con punteros que son unidireccionales, mientras que en el mundo relacional, tenemos claves externas que son inherentemente bidireccionales.
El mundo de los objetos tiene esta noción de herencia, donde un vehículo puede tener varias subclases diferentes, por lo que un automóvil es un tipo de vehículo, un bote es un tipo de vehículo y un automóvil deportivo es un tipo de automóvil, estos tipos de relaciones de herencia.
El mundo relacional no tiene esta noción de herencia.
Cartografía
Entonces, ¿cómo mapeamos todos estos disjoint relationships?Este concepto de mapeo proviene del mapeador relacional de objetos. Hay principalmente tres cosas que entender como se muestra en el siguiente diagrama.
En su aplicación, necesitará definiciones de clase, que normalmente es código C # y su código .NET que representa nuestras clases, como clase de empleado, clase de cliente, clase de pedido, etc.
En la parte inferior, puede ver un esquema de base de datos, que es nuestro Data Definition Language en una base de datos relacional que especifica cómo se ve una tabla de clientes, cómo se ve una tabla de empleados.
Entre estos tenemos los metadatos de mapeo que le dicen al mapeador relacional de objetos cómo traducir del mundo de objetos en C # al mundo de la base de datos en términos de filas y columnas y relaciones de clave externa.
Estos metadatos de mapeo se pueden representar de diversas formas y veremos varias de estas formas diferentes, más típicas en la aplicación NHibernate.
Está representado por HBM (Hibernate Mapping) archivos, que son archivos XML.
Base de datos compatible
NHibernate admite una amplia variedad de bases de datos diferentes. Se puede acceder a cualquier base de datos relacional existente a NHibernate.
El servidor SQL es la base de datos principal compatible, eso es lo que la mayoría de los desarrolladores utilizan durante el desarrollo, probablemente sea la más común.
También works very well with Oracle.
También es compatible con DB2, Firebird, MySQL, PostgreSQL, SQL Lite
También tiene ODBC and OLEDB drivers.
Hoy en día, muchos sistemas están diseñados con arquitectura en capas, NHibernate también la tiene y funciona perfectamente bien con ese diseño.
Arquitectura en capas
Una arquitectura en capas divide un sistema en varios grupos, donde cada grupo contiene código que aborda un área problemática particular y estos grupos se denominan capas. La mayoría de las aplicaciones de nivel empresarial utilizanhigh-level application architecture que constan de tres capas -
- La capa de presentación
- La capa empresarial
- La capa de persistencia
Por ejemplo, una capa de interfaz de usuario que también se conoce como capa de presentación puede contener todo el código de la aplicación para crear páginas web y procesar la entrada del usuario.
Un beneficio importante del enfoque de capas es que a menudo puede realizar cambios en una capa sin ninguna interrupción significativa en las otras capas, lo que hace que los sistemas lesser fragile and more maintainable.
Capa de presentación
Es la capa superior, que contiene el código responsable de dibujar la interfaz de usuario, páginas, cuadros de diálogo o pantallas, y recopilar la entrada del usuario y controlar la navegación.
Capa empresarial
La capa empresarial es responsable de implementar las reglas empresariales o los requisitos del sistema que los usuarios entenderían como parte del dominio del problema.
También reutiliza el modelo definido por la capa de persistencia.
Capa de persistencia
La capa de persistencia consta de clases y componentes que son responsables de guardar y recuperar datos de la aplicación.
Esta capa también define un mapeo entre la clase de modelo y la base de datos. NHibernate se utiliza principalmente en esta capa.
Base de datos
- La base de datos existe fuera de la aplicación .NET.
- Es la representación real y persistente del estado del sistema.
- Si se utiliza una base de datos SQL, la base de datos incluye el esquema relacional y posiblemente procedimientos almacenados.
Clases de ayuda / utilidad
Cada aplicación tiene un conjunto de clases auxiliares o de utilidad que admiten las otras capas: por ejemplo, widgets de IU, clases de mensajería, clases de excepción y utilidades de registro.
Estos elementos no se consideran capas, porque no obedecen las reglas de dependencia entre capas en una arquitectura en capas.
Arquitectura NHibernate
Es una vista de alto nivel de la aplicación NHibernate y también puede ver la arquitectura simple de NHibernate.
El código de la aplicación utiliza NHibernate ISession y IQuery API para operaciones de persistencia y solo tiene que administrar las transacciones de la base de datos, idealmente utilizando NHibernate ITransaction API.
Antes de que podamos realmente comenzar a usar NHibernate, debemos comprender la base sobre la que se construye. NHibernate es una tecnología de persistencia que se basa en la idea de mapeo relacional de objetos u ORM.
¿Qué es ORM?
El mapeo objeto-relacional (ORM) es un programming techniquepara convertir datos entre sistemas de tipos incompatibles en lenguajes de programación orientados a objetos. En otras palabras, es el concepto de mapear los objetos de negocio de una aplicación a tablas de bases de datos relacionales, de modo que se pueda acceder fácilmente a los datos y actualizarlos por completo a través del modelo de objetos de una aplicación.
Como ya sabe, las bases de datos relacionales proporcionan un buen medio para almacenar datos, mientras que la programación orientada a objetos es un buen enfoque para crear aplicaciones complejas.
NHibernate y ORM en general son más relevantes para aplicaciones con lógica empresarial no trivial, el modelo de dominio y algún tipo de base de datos.
Con ORM, es muy fácil crear una capa de traducción que pueda transformar fácilmente objetos en datos relacionales y viceversa.
El acrónimo ORM también puede significar modelado de roles de objetos, y este término se inventó antes de que el mapeo relacional / objeto se volviera relevante.
Describe un método para el análisis de información, utilizado en el modelado de bases de datos.
¿Por qué ORM?
ORM es un framework que le permite mapear el mundo de objetos que se encuentran en lenguajes orientados a objetos a filas en tablas relacionales que se encuentran en bases de datos relacionales
Para comprender este concepto, echemos un vistazo al siguiente diagrama.
En el diagrama anterior, puede ver que tenemos una tabla llamada Empleado en el lado derecho que contiene columnas con cada dato asociado con un empleado individual.
Tenemos una columna para una identificación que identifica de forma única a cada empleado.
Una columna para el nombre del empleado, otra columna para su fecha de incorporación y, finalmente, una columna que tiene la edad de un empleado.
Si quisiéramos escribir algún código para almacenar un nuevo empleado en las tablas, no es tan fácil.
En el diagrama anterior, también puede ver que tenemos un objeto de empleado que tiene campos para Id, nombre, fecha de ingreso y edad.
Sin un ORM, tenemos que traducir este objeto en algunas declaraciones SQL diferentes que insertarán los datos de los empleados en la tabla de empleados.
Entonces, escribir código para crear el SQL para hacer el escenario anterior no es tan difícil, pero es un poco tedioso y bastante fácil equivocarse.
Usando un ORM como NHibernate, podemos declarar cómo ciertas clases deben mapearse a tablas relacionales y dejar que el ORM o NHibernate se ocupen del desagradable trabajo de crear el SQL para insertar, actualizar, eliminar, en los datos de consulta en nuestra tabla de empleados.
Esto nos permite mantener nuestro código enfocado en el uso de objetos y hacer que esos objetos se traduzcan automáticamente a tablas relacionales.
Entonces, realmente lo que hace un ORM es que nos ahorra tener que mapear objetos a tablas manualmente.
Para comenzar a trabajar en NHibernate, necesitaremos Visual Studio y el paquete NHibernate.
Instalación de Visual Studio
Microsoft proporciona un free version de Visual Studio, que también contiene SQL Server y se puede descargar desde https://www.visualstudio.com Los siguientes son los pasos para la instalación.
Step 1 - Una vez completada la descarga, ejecute el instalador y se mostrará el siguiente cuadro de diálogo.
Step 2 - Haga clic en el botón Instalar y comenzará el proceso de instalación.
Step 3 - Una vez que el proceso de instalación se haya completado con éxito, verá el siguiente cuadro de diálogo.
Step 4 - Cierre este cuadro de diálogo y reinicie su computadora si es necesario.
Step 5- Ahora abra Visual Studio desde el menú Inicio, que abrirá el siguiente cuadro de diálogo. Por primera vez, llevará algún tiempo prepararlo.
Step 6 - Una vez hecho todo esto, verá la ventana principal de Visual Studio.
Instalación del paquete NHibernate
NHibernate es un mapeador relacional de objetos de código abierto maduro para el marco .NET. Se desarrolla activamente, presenta todas las funciones y se utiliza en miles de proyectos exitosos. Puede instalar el paquete NHibernate con los siguientes métodos.
Descarga directa
Descargue el zip del archivo de https://sourceforge.net/ que contiene todos los binarios necesarios.
Extraiga este archivo zip e incluya todos estos binarios en su proyecto.
Instalar con NuGet
Otra forma de instalar NHibernate es usar NuGet para instalar el paquete NHibernate, que es, con mucho, la forma más fácil de incorporar NHibernate en un proyecto.
Va a descargar todas las dependencias de NHibernate y crear referencias a todos los ensamblados necesarios.
Para instalar NHibernate, ejecute el siguiente comando en la Consola del Administrador de paquetes.
install-package NHibernate
Ahora está listo para iniciar su aplicación.
En este capítulo, veremos cómo iniciar un ejemplo simple usando NHibernate. Estaremos construyendo unsimple console application. Para crear una aplicación de consola, usaremos Visual Studio 2015, que contiene todas las características que necesita para crear, pruebe su aplicación usando el paquete NHibernate.
A continuación se muestran los pasos para crear un proyecto con las plantillas de proyecto disponibles en Visual Studio.
Step 1 - Abra Visual Studio y haga clic en Archivo → Nuevo → Opción de menú Proyecto.
Step 2 - Se abre un nuevo cuadro de diálogo Proyecto.
Step 3 - Desde el panel izquierdo, seleccione Plantillas → Visual C # → Windows.
Step 4 - En el panel central, seleccione Aplicación de consola.
Step 5 - Ingrese el nombre del proyecto, 'NHibernateDemoApp', en el campo Nombre y haga clic en Aceptar para continuar.
Step 6 - Una vez que Visual Studio crea el proyecto, verá varios archivos en la ventana del Explorador de soluciones.
Como sabe que hemos creado un proyecto de aplicación de consola simple, ahora necesitamos incluir el paquete NHibernate en nuestro proyecto de consola.
Vaya al menú Herramientas y seleccione NuGet Package Manager → Package Manager Console, se abrirá la ventana Package Manager Console.
Especifique el comando que se muestra arriba Package Manager Consoley presione enter, descargará todas las dependencias de NHibernate y creará referencias a todos los ensamblados requeridos. Una vez finalizada la instalación, verá el mensaje como se muestra en la siguiente imagen.
Ahora que hemos agregado NHibernate, ahora podemos comenzar la implementación. Entonces, comenzaremos mapeando un muy simpletable llamado Student, que simplemente tiene una clave primaria entera llamada ID y una columna de Nombre y Apellido.
Necesitamos una clase para representar a este estudiante, así que creemos una nueva clase llamada Estudiante haciendo clic derecho en el proyecto en el explorador de soluciones y luego seleccione Agregar → Clase que abrirá el cuadro de diálogo Agregar nuevo elemento.
Entrar Student.csen el campo de nombre, haga clic en el botón Agregar. En esta clase de Estudiante, necesitamos tener nuestra clave primaria entera llamada ID, y necesitamos crear esta cadena,FirstName y LastName campos como se muestra en la siguiente implementación completa de la clase Student.
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; }
}
}
Cuando se trata de modelos en la aplicación NHibernate, es más fácil hacer que todos sus campos sean virtuales. Así que este es nuestro modelo NHibernate simple que usaremos y lo mapearemos a la base de datos de back-end.
Ahora vayamos al método Main en la clase Program y creemos un nuevo objeto de configuración NHibernate.
Lo primero que debemos proporcionar es la connection string. Esta es una cadena de conexión específica de la base de datos y la forma más fácil de encontrar la cadena de conexión es haciendo clic derecho en la base de datos enSQL Server Object Explorer y seleccione Propiedades.
Se abrirá la ventana Propiedades, ahora desplácese hacia abajo y verá el campo Cadena de conexión en la ventana Propiedades.
Copie la cadena de conexión y especifique en su código. A continuación se muestra la implementación del método Main en el que necesitamos configuración para 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) {
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();
}
}
}
}
Después de la cadena de conexión, necesitamos proporcionar un controlador, que es el SQLClientDriver y luego también debemos proporcionarle un dialecto, qué versión de SQL Server, y vamos a usar MS SQL 2008.
NHibernate ahora sabe cómo conectarse a la base de datos. La otra cosa que debemos hacer es proporcionarle una lista de modelos que mapearemos.
Podemos hacer esto agregando un ensamblaje, por lo que especificando el Assembly.GetExecutingAssemblyy aquí es donde el programa encontrará archivos de mapeo. Los archivos de mapeo le dicen a NHibernate cómo pasar de las clases C # a las tablas de la base de datos.
SessionFactory compila todos los metadatos necesarios para inicializar NHibernate. SessionFactory se puede utilizar para crear sesiones, que son aproximadamente análogas a las conexiones de bases de datos. Entonces, la forma adecuada es usarlo en el bloque using. puedo decirvar session es igual a sessionFactory.OpenSession y voy a querer hacer esto dentro de su transacción.
Una vez que se abre la sesión, podemos decirle a la sesión que comience una nueva transacción y luego podemos realizar algo de lógica aquí. Así que realice alguna lógica de base de datos y finalmente confirme esa transacción.
En este capítulo, cubriremos algunos basic mappingy sabes que desde el último capítulo tenemos la tabla de la base de datos, así como la definición de la clase C #. Ahora necesitamos un mapeo que explique cómo traducir de C # a la base de datos y viceversa.
Así que sigamos adelante y agreguemos un nuevo archivo XML haciendo clic derecho en el proyecto en el explorador de soluciones y seleccione Agregar → Nuevo elemento ...
Entrar Student.hbm.xmlen el campo de nombre. Necesitamos especificar un ensamblaje predeterminado que seráNHibernateDemoAppy también especificar un espacio de nombres predeterminado. Esto simplemente acorta muchas de las otras definiciones de tipo que vamos a hacer en este archivo.
A continuación se muestra la implementación en el archivo XML:
<?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>
Lo siguiente que necesitamos para definir una clase; esta clase va a ser nuestraStudent class. A continuación, debemos decirle a NHibernate el nombre de la identificación, que es ID y también tengo que decirle a NHibernate cómo generar las ID, por lo que nuestro generador será de tipo nativo.
El generador de tipo nativo significa que en una base de datos como SQL Server, utilizará la columna de identidad, el tipo de identidad.
Lo siguiente que tenemos que hacer es dar los nombres de las propiedades. Entonces, agregue dos propiedades más para FirstName y LastName.
Ahora, estamos leyendo estos archivos de mapeo del ensamblaje. Entonces, la forma preferida de hacer esto es tener estosHBM fileshorneado en su asamblea. Podemos hacer esto simplemente estableciendo una propiedad.
Ahora haga clic derecho en el proyecto en el explorador de soluciones y seleccione Propiedades, verá el Build Action field en el que el Contenido está seleccionado de forma predeterminada.
Seleccione el recurso incrustado de la lista desplegable.
Entonces, esto realmente incrusta ese archivo XML dentro del NHibernateDemoApp montaje.
En este capítulo, cubriremos los aspectos básicos CRUD operations. Ahora que nuestro sistema está listo para comenzar, ya que hemos implementado con éxito nuestra clase de dominio Student, también hemos definido los archivos de mapeo y configurado NHibernate. Ahora podemos usar algunas consultas para realizar operaciones CRUD.
Crear datos
Como puede ver, no tenemos datos en nuestra tabla de Estudiantes en NHibernateDemoDB base de datos.
Entonces, para agregar algunos datos, necesitamos realizar el Add/Create operación como se muestra a continuación.
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();
}
Como puede ver, hemos creado dos estudiantes y luego llamamos al método Save () del OpenSession y luego llame al Commit () del BeginTransaction. Aquí está la implementación completa enProgram.cs archivo
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();
}
}
}
}
Ahora ejecutemos esta aplicación y luego vayamos al Explorador de objetos de SQL Server y actualice su base de datos. Verá que los dos estudiantes anteriores ahora se agregan a la tabla Student en la base de datos NHibernateDemoDB.
Leer datos de la tabla de estudiantes
Puede ver que ahora tenemos dos registros en nuestra tabla de estudiantes. Para leer estos registros de la tabla, necesitamos llamar alCreateCriteria() de OpenSession como se muestra en el siguiente código.
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();
}
Entonces, si desea la lista de registros, simplemente podemos decir lista de tipo Estudiante.
Ahora usa el foreach a través de todos los estudiantes y diga imprima la identificación, FirstMidName y LastNameen la consola. Ahora, ejecutemos esta aplicación nuevamente y verá el siguiente resultado en la ventana de la consola.
1 Allan Bommer
2 Jerry Lewis
También puede recuperar cualquier registro especificando el ID en el Get() método de OpenSession utilizando el siguiente código.
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();
}
Ahora, cuando ejecute su aplicación, verá el siguiente resultado.
1 Allan Bommer
2 Jerry Lewis
Retrieved by ID
1 Allan Bommer
Actualizar registro
Para actualizar el registro en la tabla, primero debemos buscar ese registro en particular y luego actualizar ese registro llamando al Update() método de OpenSession como se muestra en el siguiente código.
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();
}
Ahora, cuando ejecute su aplicación, verá el siguiente resultado.
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
Como puede ver, LastName of ID igual a 1 se actualiza de Bommer a Donald.
Eliminar el registro
Para eliminar cualquier registro de la tabla, primero debemos buscar ese registro en particular y luego eliminar ese registro llamando al Delete() método de OpenSession como se muestra en el siguiente código.
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();
}
Ahora, cuando ejecute su aplicación, verá el siguiente resultado.
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
Como puede ver, el registro que tiene ID igual a 1 ya no está disponible en la base de datos. También puede ver la base de datos en el Explorador de objetos de SQL Server.
En este capítulo, entenderemos cómo todos los registros de la base de datos están retrieved, updated, created, and deleted y ¿cómo se realizan exactamente estas consultas?
Para comprender todo esto, simplemente podemos agregar una opción en nuestra configuración, que registra el SQL en la consola. Aquí está la declaración simple que registrará la consulta SQL:
x.LogSqlInConsole = true;
Ahora, tenemos dos registros en nuestra tabla de estudiantes en la base de datos NHibernateDemoDB. Recuperemos todos los registros de la base de datos como se muestra en el siguiente código.
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();
}
}
}
}
Así que sigamos adelante y ejecutemos esta aplicación nuevamente, y verá el siguiente resultado:
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
Como puede ver, el select clausesiendo enviado a la base de datos, es en realidad como una cláusula que recuperará el ID, FirstMidName y LastName. Entonces, todo esto se envía a la base de datos y se procesa allí en lugar de tener muchos registros devueltos a su servidor y procesados en el lado del servidor.
Perfilador NHibernate
Otra forma de ver estos resultados es utilizar NHibernate Profiler. NHibernate Profiler es una herramienta comercial, pero es muy útil para trabajar con aplicaciones NHibernate. Puede instalar fácilmente NHibernate Profiler en su aplicación desde NuGet.
Vayamos a la consola de NuGet Manager desde el menú Herramientas seleccionando NuGet Package Manager → Package Manager Console. Se abrirá la ventana de la Consola del Administrador de paquetes. Ingrese el siguiente comando y presione enter.
PM> install-package NHibernateProfiler
Instalará todos los binarios necesarios para NHibernate Profiler, una vez que se haya instalado correctamente, verá el siguiente mensaje.
También verá que se inicia NHibernate Profiler, una vez instalado. Necesitará una licencia para usarlo, pero para fines de demostración, podemos usar la versión de prueba de 30 días de NHibernate Profiler.
Ahora, NHibernate Profiler está optimizado para trabajar con aplicaciones web y verá que ha agregado App_Start folderen el explorador de soluciones. Para mantener todo esto simple, elimine la carpeta App_Start y también observará que se agrega una instrucción al comienzo del método Main en la clase Program.
App_Start.NHibernateProfilerBootstrapper.PreStart();
Elimine también esta declaración y simplemente agregue una simple llamada NHibernateProfiler.Initialize como se muestra en el siguiente código.
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();
}
}
}
}
Ahora, cuando ejecute la aplicación, enviará datos a la aplicación NHibernate Profiler.
Puede ver aquí, tenemos una pantalla agradable que muestra que hemos comenzado la transacción, lo que SQL le está haciendo a la base de datos en un formato agradable.
Por lo tanto, esto es muy útil para determinar qué está sucediendo exactamente dentro de su aplicación NHibernate. Se vuelve increíblemente útil una vez que la aplicación alcanza un cierto nivel de complejidad, donde necesita algo más como un SQL Profiler, pero con el conocimiento de NHibernate.
En este capítulo, agregaremos IntelliSense a nuestros archivos de mapeo NHibernate (*.hbm.xml files). Como ha observado al mapear el dominio de la clase Student, actualmente no tenemos IntelliSense disponible. Es muy útil tener elXML schemasdisponible. Entonces, en este capítulo comprenderá cómo agregar IntelliSense en Visual Studio para estos archivos XML de NHibernate.
Abra el archivo de mapeo y verá que la opción de menú XML aparece en el menú principal.
Seleccione la opción de menú XML → Esquemas ... y se mostrará el cuadro de diálogo Esquemas XML.
Seleccione el botón Agregar… que se encuentra en la parte superior derecha del cuadro de diálogo, que abre el cuadro de diálogo del archivo. Ahora ve alpackages folder, que se encuentra en la carpeta Solución de su proyecto y verá los diferentes paquetes incluidos en su proyecto.
Ahora, haga doble clic en NHibernate.4.*** folder y verá los dos archivos de esquemas (* .xsd) o archivos de definición de esquema XML que definen la configuración y el mapeo de NHibernate.
Seleccione estos dos archivos de esquema y haga clic en el botón Abrir.
Puede ver que los esquemas NHibernate se agregan al cuadro de diálogo Esquemas XML. Haga clic en el botón Aceptar. Ahora, comencemos una nueva etiqueta de propiedad y verá que tenemos IntelliSense completo aquí.
IntelliSense ahora está disponible para usted, lo que le ahorra mucho tiempo durante el mapeo relacional de objetos.
En este capítulo, cubriremos los tipos de datos de mapeo. El mapeo de entidades es sencillo, las clases de entidad siempre se mapean a tablas de base de datos usando<class>, <subclass>, and <joined-subclass>elementos de mapeo. Los tipos de valor necesitan algo más, que es donde se requieren los tipos de mapeo.
NHibernate puede mapear una amplia variedad de tipos de datos. Aquí está la lista de los tipos de datos más comunes que son compatibles.
Tipo de mapeo | Tipo .NET | System.Data.DbType |
---|---|---|
Int16 | System.Int16 | DbType.Int16 |
Int32 | System.Int32 | DbType.Int32 |
Int64 | System.Int64 | DbType.Int64 |
Soltero | System.Single | DbType.Single |
Doble | Sistema.Doble | DbType.Double |
Decimal | System.Decimal | DbType.Decimal |
Cuerda | System.String | DbType.String |
AnsiString | System.String | DbType.AnsiString |
Byte | System.Byte | DbType.Byte |
Carbonizarse | System.Char | DbType.StringFixedLength: un carácter |
AnsiChar | System.Char | DbType.AnsiStringFixedLength: un carácter |
Booleano | System.Boolean | DbType.Boolean |
Guid | System.Guid | DbType.Guid |
PersistentEnum | System.Enum (una enumeración) | DbType para el valor subyacente |
Verdadero Falso | System.Boolean | DbType.AnsiStringFixedLength: 'T' o 'F' |
Sí No | System.Boolean | DbType.AnsiStringFixedLength: 'Y' o 'N' |
Fecha y hora | Fecha y hora | DbType.DateTime: ignora milisegundos |
Garrapatas | System.DateTime | DbType.Int64 |
Espacio de tiempo | System.TimeSpan | DbType.Int64 |
Marca de tiempo | System.DateTime | DbType.DateTime: tan específico como la base de datos admita |
Binario | System.Byte [] | DbType.Binary |
BinaryBlob | System.Byte [] | DbType.Binary |
StringClob | System.String | DbType.String |
Serializable | Cualquier System.Object marcado con SerializableAttribute | DbType.Binary |
CultureInfo | System.Globalization.CultureInfo | DbType.String: cinco caracteres para referencia cultural |
Tipo | Tipo de sistema | DbType.String que contiene el nombre calificado de ensamblado |
La tabla anterior explica en detalle los indicadores mencionados a continuación.
Todo, desde simples tipos numéricos hasta cadenas, que se pueden asignar de diversas formas mediante varchars, chars etc., así como blobs de cadenas y toda la variedad de tipos que admiten las bases de datos.
También es capaz de mapear Booleans, tanto para campos que usan ceros como unos, campos de caracteres que contienen verdadero, falso o T y F.
Hay una amplia variedad de formas de definir cómo eso se asigna al back-end, los valores booleanos en la base de datos.
Podemos manejar el mapeo de DateTime, que incluyen y excluyen las compensaciones de zona horaria, el horario de verano, etc.
También podemos mapear enumerations; podemos asignarlos a cadenas oa sus valores numéricos subyacentes.
Echemos un vistazo a un ejemplo simple en el que tenemos los mismos nombres de propiedad tanto en la base de datos como en la clase Student.
Ahora cambiemos FirstMidName a FirstName en la clase del estudiante, donde no cambiaremos la columna FirstMidName, pero veremos cómo decirle a NHibernate que sepa realizar esta conversión. A continuación se muestra la clase de estudiantes actualizada.
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; }
}
}
Aquí está la implementación del archivo de mapeo NHibernate.
<?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>
En este ejemplo, suponga que el campo FirstName es una cadena .NET y la columna FirstMidName es una SQL nvarchar. Ahora, para decirle a NHibernate cómo realizar esta conversión, establezca el nombre igual aFirstName y columna igual a FirstMidName y especifique el tipo de mapeo igual a String, que es apropiado para esta conversión en particular.
La siguiente es una Program.cs implementación de archivos.
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();
}
}
}
}
Ahora, cuando ejecute su aplicación, verá el siguiente resultado.
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
Como puede ver, ha asignado el nombre de la propiedad diferente al nombre de la columna en la base de datos.
Echemos un vistazo a otro ejemplo en el que agregaremos otra propiedad en la clase Student de enumtipo. Aquí está la implementación de la clase Student.
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
}
}
Como puede ver, la enumeración tiene una variedad de valores diferentes que posiblemente pueda tener, como Excelente, Bueno, Regular, Deficiente y Terrible.
Saltando al archivo de mapeo, puede ver que cada una de estas propiedades se enumeran en el archivo de mapeo, incluido el recién agregado AcademicStanding propiedad.
<?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>
Ahora también necesitamos cambiar la base de datos, así que vaya al Explorador de objetos de SQL Server, haga clic con el botón derecho en la base de datos y seleccione la opción Nueva consulta ...
Abrirá el editor de consultas y luego especificará la siguiente consulta.
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)
);
Esta consulta primero eliminará la tabla de estudiantes existente y luego creará una nueva tabla.
Haga clic en el icono Ejecutar como se muestra arriba. Una vez que la consulta se ejecuta con éxito, verá un mensaje.
Expanda el menú desplegable de la base de datos y la tabla y, a continuación, haga clic con el botón derecho en la tabla de estudiantes y seleccione Ver diseñador.
Ahora, verá la tabla recién creada, que también tiene la nueva propiedad AcademicStanding.
Agreguemos dos registros como se muestra a continuación. Program.cs archivo.
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();
}
}
}
}
Ahora ejecutemos su aplicación y verá el siguiente resultado en la ventana de su consola.
Fetch the complete list again
1 Allan Bommer Excellent
2 Jerry Lewis Good
Ahora echemos un vistazo a la base de datos haciendo clic derecho en la tabla de estudiantes.
Seleccione Ver datos y verá los dos registros en la tabla de estudiantes como se muestra en la siguiente captura de pantalla.
Puede ver que se agregan dos registros y Allan tiene AcademicStanding 0 y Jerry tiene AcademicStanding 1. Esto se debe a que en .Net el primer valor de enumeración predeterminado tiene 0, que es Excelente si miras StudentAcademicStanding. Mientras que, en el archivo Student.cs, Good es el segundo, por lo que tiene un valor de 1.
En este capítulo, veremos la configuración de NHibernate. Tenemos diferentes formas de configurar NHibernate. Se divide en dos grupos principales
- Configuración basada en XML
- Configuración basada en código
Configuración basada en código
La configuración basada en código está integrada en NHibernate. Se introdujo alrededor de NHibernate 3 y hemos utilizado la configuración de bases de código hasta ahora.
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());
Todas las configuraciones se especifican en el código C #. Puede ver aquí que tenemos nuestro nuevo objeto de configuración, y luego usamosloquacious configurationque se introdujo con NHibernate 3.1 para configurar la base de datos. Qué cadena de conexión estamos usando, a qué base de datos nos estamos conectando y el dialecto a usar. También agregamos nuestro ensamblaje de mapeo directamente aquí.
Configuración basada en XML
Si está utilizando una configuración basada en XML, puede utilizar un hibernate.cfg.xml , que es solo un archivo xml independiente que usa el esquema NHibernate, o puede incrustar esa configuración específica de NHibernate dentro de su aplicación o web.cfg. El nombre hibernate.cfg.xml es el predeterminado, pero también podemos usar un nombre arbitrario para ese archivo xml.
Echemos un vistazo a la configuración basada en XML agregando un nuevo archivo xml al proyecto NHibernateDemoApp y llamémoslo hibernate.cfg.xml.
Ingrese la siguiente información en el archivo hibernate.cfg.xml.
<?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>
Como puede ver en el archivo xml anterior, hemos especificado la misma configuración que se menciona en C #.
Ahora comentemos esta configuración desde el archivo Program.cs y simplemente llamemos al Configure() método, que cargará el hibernate.cfg.xml archivo como se muestra a continuación.
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();
}
}
}
}
Ejecutemos su aplicación nuevamente y verá el mismo resultado.
Fetch the complete list again
1 Allan Bommer Excellent
2 Jerry Lewis Good
En este capítulo, cubriremos cómo anular la configuración de NHibernate. Hay algunas cosas que debe tener en cuenta.
En primer lugar, la configuración en NHibernate es aditiva.
Por lo tanto, no solo tiene que usar un solo archivo xml o no solo tiene que usar la configuración basada en código o NHibernate fluido.
Puede mezclar y combinar todos estos métodos dependiendo de cómo desee configurar su aplicación.
El punto importante a recordar es que, por último, la configuración gana.
En el siguiente ejemplo, puede ver que creamos nuestro objeto de configuración, lo configuramos usando la configuración basada en código y finalmente llamamos al cfg.configure() , que carga el archivo hibernate.cfg.xml.
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();
Entonces, cualquier cosa dentro de un hibernate.cfg.xml anula la configuración establecida por la configuración basada en código.
Al invertir estos dos procesos, podemos tener los valores predeterminados dentro de hibernate.cfg.xml y luego hacer nuestras anulaciones dentro de una configuración basada en código.
No hay nada que excluya si está utilizando una configuración basada en código y tampoco hay nada que le impida utilizar el archivo hibernate.cfg.xml.
Echemos un vistazo a un ejemplo simple en el que anularemos la configuración utilizando una combinación de configuración basada en código xml.
Muevamos también la cadena de conexión al app.config archivo usando el siguiente código.
<?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>
La cadena de conexión se encuentra en algunos app.configarchivo con un nombre predeterminado. Ahora, debemos mencionar el nombre predeterminado en el archivo hibernate.cfg.xml en lugar de la cadena de conexión.
<?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>
Comentemos sobre la parte de la cadena de conexión, el controlador y la parte del dialecto de la configuración basada en código, porque el programa lo leerá del archivo hibernate.cfg.xml y el LogSqlInConsole parte permanecerá en la configuración basada en código.
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();
}
}
}
}
Ahora, cuando ejecute la aplicación, verá que el programa ha leído el registro de la configuración basada en código y otra configuración del archivo hibernate.cfg.xml.
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
Así que ahora tenemos parte de nuestra configuración dentro de nuestro hibernate.cfg.xml archivo, parte de él está dentro de la configuración basada en código y dependiendo del orden de llamada basada en código versus configure(), podemos cambiar cuál de ellos anula al otro.
En este capítulo, cubriremos la actualización del tamaño del lote. El tamaño del lote le permitecontrol the number of updates que salen en un solo viaje de ida y vuelta a su base de datos para las bases de datos compatibles.
El tamaño del lote de actualización se ha predeterminado a partir de NHibernate 3.2.
Pero si está utilizando una versión anterior o necesita ajustar su aplicación NHibernate, debería mirar el tamaño del lote de actualización, que es un parámetro muy útil que puede usarse para ajustar el rendimiento de NHibernate.
En realidad, el tamaño del lote controla cuántas inserciones enviar de un grupo a una base de datos.
Por el momento, solo SQL Server y Oracle admiten esta opción porque el proveedor de la base de datos subyacente debe admitir el procesamiento por lotes de consultas.
Echemos un vistazo a un ejemplo simple en el que hemos establecido el tamaño del lote en 10 que insertará 10 registros en un conjunto.
cfg.DataBaseIntegration(x => {
x.ConnectionString = "default";
x.Driver<SqlClientDriver>();
x.Dialect<MsSql2008Dialect>();
x.LogSqlInConsole = true;
x.BatchSize = 10;
});
Aquí está la implementación completa en la que se agregarán 25 registros a la base de datos.
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();
}
}
}
}
Ahora ejecutemos su aplicación y verá que todas esas actualizaciones están saltando al generador de perfiles de NHibernate. Tenemos 26 viajes de ida y vuelta individuales a la base de datos, 25 para la inserción y uno para recuperar la lista de estudiantes.
Ahora, ¿por qué es eso? La razón es que NHibernate necesita hacer unaselect scope identity ya que estamos usando la estrategia de generación de identificadores nativos en el archivo de mapeo para ID como se muestra en el siguiente código.
<?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>
Así que necesitamos usar un método diferente como el guid.combmétodo. Si vamos a ir a guid.comb, debemos acercarnos a nuestro cliente y cambiar esto a unguid. Entonces eso funcionará bien. Ahora cambiemos del nativo a guid.comb usando el siguiente código.
<?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>
Entonces, es la base de datos la responsable de generar esos ID. La única forma en que NHibernate puede averiguar qué ID se generó fue seleccionarlo inmediatamente después. De lo contrario, si hemos creado un lote de estudiantes, no podrá coincidir con la identificación del estudiante que se creó.
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
}
}
Solo necesitamos actualizar nuestra base de datos. Dejemos caer la tabla de estudiantes y creemos una nueva tabla especificando la siguiente consulta, así que vaya al Explorador de objetos de SQL Server y haga clic derecho en la base de datos y seleccione elNew Query… opción.
Abrirá el editor de consultas y luego especificará la siguiente consulta.
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)
);
Esta consulta primero eliminará la tabla de estudiantes existente y luego creará una nueva tabla. Como puede ver, hemos utilizadoUNIQUEIDENTIFIER en lugar de utilizar una clave primaria entera como ID.
Ejecute esta consulta y luego vaya al Designer view y verá que ahora la ID se crea con un identificador único como se muestra en la siguiente imagen.
Ahora necesitamos eliminar la ID del archivo program.cs, mientras insertamos datos, porque ahora generará el guids para ello automáticamente.
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();
}
}
}
}
Ahora ejecute la aplicación nuevamente y eche un vistazo al generador de perfiles de NHibernate. Ahora, el generador de perfiles NHibernate, en lugar de realizar 26 viajes de ida y vuelta, solo hará cuatro.
Se insertan diez filas en la tabla, luego otras diez filas y luego las cinco restantes. Y después de la confirmación, ha insertado uno más para recuperar todos los registros.
Así que lo dividió en grupos de diez, lo mejor que pudo.
Por lo tanto, si está haciendo muchas inserciones, esto puede mejorar drásticamente el rendimiento de la inserción en su aplicación, porque puede agruparlas.
Esto se debe a que NHibernate asigna esas guías por sí mismo utilizando la guid.comb algoritmo, y no tiene que depender de la base de datos para hacer esto.
Entonces, usar el tamaño del lote es una excelente manera de ajustarlo.
En este capítulo, cubriremos cómo el cachingfunciona en aplicaciones NHibernate. Tiene soporte incorporado para almacenamiento en caché. Parece una característica simple, pero en realidad, es una de las características más complejas. Comenzaremos con la caché de primer nivel.
Caché de primer nivel
Este mecanismo de caché está habilitado por defecto en NHibernate y no necesitamos hacer nada para trabajar con el caché. Para entender esto, echemos un vistazo a un ejemplo simple, como puede ver que tenemos dos registros en nuestra base de datos.
Ahora, en este ejemplo, recuperaremos el estudiante cuyo ID es 1 y usaremos la misma consulta de sesión dos veces como se muestra en el siguiente código.
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();
}
}
}
}
Ahora ejecutemos esta aplicación y veamos el resultado en NHibernate Profiler.
Se sorprenderá al ver que NHibernate solo activa una consulta. Así es como NHibernate usa la caché de primer nivel. Cuando se ejecuta la primera consulta, NHibernate almacena en caché al Estudiante con ID = 1 en su caché de primer nivel.
Entonces, cuando se ejecuta la segunda consulta, NHibernate primero busca la entidad Student de caché de primer nivel con ID = 1, si encuentra esa entidad, entonces NHibernate sabe que no hay necesidad de disparar otra consulta para recuperar el mismo objeto de empleado nuevamente. .
En este capítulo, hablaremos de componentes de mapeo. En NHibernate,component is a value object. No tiene una identidad propia.
Un ejemplo de esto sería un objeto de dinero, un bolso o una billetera pueden tener dinero, pero la identidad exacta de ese dinero es irrelevante.
No tiene su propia clave principal, pero los componentes en sí son persistentes en la misma tabla que el objeto propietario.
Echemos un vistazo a un ejemplo sencillo en el que un alumno tiene una dirección, que es un objeto de Location class asociado a ello.
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
}
}
Ahora, también necesitamos actualizar la base de datos ejecutando la siguiente consulta, que primero eliminará la tabla de Estudiantes y luego creará una nueva tabla que también contendrá una columna para la clase Ubicación.
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)
);
Ahora, para mapear aquellas columnas que no forman parte directamente de la clase de estudiante, pero son propiedades de la clase de ubicación y el objeto de clase de ubicación se define en la clase de estudiante. Necesitamos un componente para mapearlo correctamente. Creemos un componente enstudent.hbm.xml archivo como se muestra en el siguiente código.
<?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>
Este componente es la Dirección y tiene estas propiedades diferentes. Con esta información, NHibernate ahora tiene suficiente para poder mapear esto.
Ahora aquí está el archivo Program.cs en el que se crea e inicializa un nuevo objeto de estudiante y luego se guarda en la base de datos. Luego recuperará la lista de la base de datos.
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();
}
}
}
}
Ahora podemos ejecutar esta aplicación y NHibernate puede guardar esos valores en la base de datos. Cuando ejecute la aplicación, verá el siguiente resultado.
Fetch the complete list again
2 Allan Bommer Poor 123 Street Lahore Punjab Pakistan
Aquí están los valores en la base de datos.
Los componentes nos permiten separar columnas que están en una tabla de base de datos en su propia clase separada.
La otra cosa a tener en cuenta aquí es porque la Ubicación es una clase, no es una entidad.
Es un objeto de tipo valor y no tiene su propia clave primaria.
Se guarda en la misma tabla que el Estudiante que lo contiene.
Es por eso que usamos el componente aquí.
Esto permite mucha flexibilidad para cambiar nuestra capa de clase, cómo se definen nuestras clases versus cómo se distribuye nuestra base de datos.
En este capítulo, veremos las relaciones en NHibernate. Dirijamos nuestra atención a cómo podemos entender las relaciones en NHibernate. La forma más sencilla es pensar en las relaciones desde la perspectiva de la base de datos.
Primero crearemos una nueva aplicación en la que crearemos algunas relaciones entre el cliente y las entidades de pedido.
La primera relación que veremos es una relación de colección clásica.
Tenemos un cliente con recogida de pedidos.
Esta es una relación de uno a muchos y está representada en la base de datos mediante 2 tablas y hay un ID de cliente en la tabla de pedidos y tenemos una relación de clave externa con el cliente.
Primero necesitamos crear una base de datos y dos tablas Cliente y Pedido. Puede crear esto especificando la siguiente consulta en el Explorador de SQL Server.
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
Creará dos tablas en la base de datos. La siguiente imagen muestra la tabla de clientes.
La siguiente imagen muestra la tabla de pedidos en la que puede ver la relación de la clave externa con el cliente.
Necesitamos definir la cadena de conexión en el app.config archivo, aquí está la implementación del archivo 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>
Para instalar NHibernate en su aplicación, ejecute el siguiente comando en la ventana de NuGet Manager Console.
install-package NHibernate
Para configurar la configuración de NHibernate, necesitamos definir la configuración en hibernate.cfg.xml archivo como se muestra en el siguiente código.
<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>
En este ejemplo, trabajaremos en dos clases de dominio, Cliente y Pedido.
Aquí está la implementación del archivo Customer.cs en la que tenemos dos clases, una es la clase Customer y otra es la clase Location en la que el objeto se usa como dirección en la clase Customer.
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
}
}
Aquí está el archivo de mapeo Customer.hbm.xml en el que la clase Cliente se asigna a la tabla Cliente.
<?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>
También tenemos una clase de orden y aquí está la implementación de Order.cs archivo.
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);
}
}
}
Relación de varios a uno
También necesitamos mapear la clase Order a la tabla Order en la base de datos, así que aquí está la implementación del Order.hbm.xml archivo.
<?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>
Relación uno a muchos
Aquí, vamos a echar un vistazo a una relación de uno a varios, en este caso, entre el cliente y los pedidos. Tenemos a nuestro cliente aquí, estamos creando uno nuevo y puede ver que la colección se inicializa con el siguiente par de pedidos.
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;
}
Entonces crearemos un nuevo cliente y luego lo guardaremos, luego de guardarlo, encontraremos el ID y luego lo recargaremos en otra sesión en el método Main como se muestra en el siguiente programa.
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();
}
Aquí está el completo Program.cs implementación de archivos.
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;
}
}
}
Cuando ejecute esta aplicación, verá el siguiente resultado.
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...
Como ves que inicialmente el cliente tiene 2 pedidos, pero cuando lo recargamos, no hay pedidos a la vista. Si mirascustomer.hbm.xmlarchivo, puede ver aquí que no mapeamos la colección de pedidos real. Entonces NHibernate no sabe nada al respecto. Sigamos adelante y agreguemos.
<?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>
Este es un conjunto y el nombre de esta colección es 'Pedidos', que se almacena en una tabla llamada orden. Necesitamos especificar una clave que es el nombre de la clave externa o para encontrar pedidos. Estos pedidos se identifican o pertenecen a un cliente a través del ID de cliente. Y luego debo señalar que esta es una relación de uno a muchos y es con la clase de orden.
También necesitamos cambiar ligeramente el método Main guardando los nuevos pedidos de los clientes en la base de datos, como se muestra en el siguiente programa.
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();
}
También hemos especificado qué cliente pidió ese producto en particular. Por lo tanto, debemos crear una relación de muchos a uno para relacionar ese pedido con ese cliente.
Así que vayamos al Order.hbm.xml archivo y agregue varios a uno, y luego nombre el campo del cliente y la columna con el ID de cliente.
<?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>
Ejecutemos esta aplicación nuevamente y ahora verá el siguiente resultado.
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...
En este capítulo, cubriremos cómo representar colecciones. Hay diferentes tipos de colecciones que podemos usar dentro de NHibernate como:
- Lists
- Sets
- Bags
Ahora, desde la perspectiva de .NET, generalmente tratamos con listas o como estructuras de datos muy simples, listas, diccionarios. .NET no tiene una amplia variedad de tipos de colecciones diferentes. Entonces, ¿por qué NHibernate necesita todos estos tipos diferentes? Realmente vuelve a la base de datos.
Lista
Una lista es una colección ordenada de elementos que no son necesariamente únicos.
Podemos mapear esto usando el IList <T>.
Entonces, aunque convencionalmente podríamos tener una lista de direcciones, y desde el punto de vista de la aplicación sabemos que los elementos son únicos, nada en la lista nos impide insertar elementos duplicados en esa lista.
Conjunto
Un conjunto es una colección desordenada de elementos únicos. Si intenta insertar 2 elementos duplicados en un conjunto, generará una excepción.
No hay nada específico en NHibernate al respecto.
Es solo una forma conveniente de tener una implementación de conjunto genérico. Si está en .NET 4, puede usar el nuevoHashSet <T> para representarlos, pero en la mayoría de las aplicaciones de NHibernate, representamos que se trata de un ISet.
No está ordenado, si extrae una lista de direcciones de una base de datos o una lista de pedidos, no sabe en qué orden vienen a menos que ingrese una cláusula específica de Orden por.
Entonces, en general, los datos que extrae de una base de datos son conjuntos.
Son colecciones únicas de elementos que no están ordenados.
Bolso
Otra colección común que veremos en el mundo de las bases de datos es una bolsa, que es como un conjunto, excepto que puede tener elementos duplicados.
En el mundo .NET, representamos esto mediante un IList.
Los conjuntos son probablemente los más comunes, pero también verá listas y bolsas según su aplicación. Echemos un vistazo a abajocustomer.hbm.xml Archivo del último capítulo en el que se definen las órdenes de Set.
<?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>
Como puede ver, hemos mapeado la colección de pedidos como un conjunto. Recuerde que un conjunto es una colección desordenada de elementos únicos.
Ahora, si observa la clase Cliente, verá que la propiedad Pedidos está definida con un ISet como se muestra en el siguiente programa.
public virtual ISet<Order> Orders { get; set; }
Ahora, cuando se ejecute esta aplicación, verá el siguiente resultado.
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...
Si los elementos de la colección no necesitaban ser únicos, si pudiera tener varios pedidos con la misma clave principal que ocurriera varias veces en esta colección, entonces sería mejor mapearlo como una bolsa como se muestra en el siguiente programa.
<bag name = "Orders" table = "`Order`">
<key column = "CustomerId"/>
<one-to-many class = "Order"/>
</bag>
Ahora, si ejecuta esta aplicación, obtendrá una excepción porque si echamos un vistazo a la clase de cliente, notará que los pedidos están marcados como ISet en el código C #.
Así que también necesitaremos cambiar esto a un IList y luego aquí, necesitaríamos cambiar de HashSet a List en el 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();
}
}
Cuando ejecute la aplicación, verá el mismo comportamiento. Pero ahora podemos tener un orden que ocurre varias veces en la misma colección.
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...
En este capítulo, cubriremos cómo utilizar la función Cascade. Si tiene un conjunto o colección de artículos o una relación entre dos clases, como nuestro cliente y pedido, y tiene una relación de clave externa. Si borramos el cliente por defecto, NHibernate no hace nada a los objetos hijo, por lo que los que pertenecen a ese cliente y nosotros podríamos ser pedidos huérfanos.
También podríamos estar violando restricciones de clave externa, por lo que podemos usar la noción de cascadas.
Por defecto, NHibernate no conecta operaciones en cascada a objetos secundarios.
La razón de esto es que puede tener relaciones como un cliente que tiene una dirección de envío predeterminada y esa dirección de envío se comparte con muchos clientes diferentes.
Por lo tanto, no querrá que esa relación se convierta en cascada necesariamente porque otros clientes todavía se refieren a ella.
Entonces, toda la noción de cascadas es decirle a NHibernate cómo manejar sus entidades secundarias.
Hay diferentes opciones para la conexión en cascada, que son las siguientes:
none - que es el predeterminado y significa que no hay cascada.
all - que va a guardar, actualizar y eliminar en cascada.
save-update - Se pondrá en cascada, guardará y actualizará.
delete - Se eliminará en cascada.
all-delete-orphan - Es uno especial que se usa con bastante frecuencia y es el mismo que Todos excepto, si encuentra Eliminar filas huérfanas, las eliminará también.
Puede especificar el valor predeterminado en su hbm.xml file, por lo que puede proporcionar una cascada predeterminada en ese elemento de mapeo de Hibernate o también puede especificarlo para colecciones y relaciones específicas, como la de muchos a uno.
Echemos un vistazo a cascadas de ejemplo simples, solucionemos el problema en el programa, donde tenemos que poner en cascada manualmente el guardado a los pedidos como se muestra en el siguiente código.
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();
}
En el fragmento de código anterior, puede ver que estamos guardando manualmente todos los pedidos para el cliente. Ahora eliminemos el código en cascada manual en el que se guardan todos los pedidos.
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();
}
Necesitamos especificar la opción de cascada en 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>
Ahora, los pedidos pertenecen totalmente al cliente. Entonces, si los clientes fueran eliminados de la base de datos, nuestra aplicación aquí querría eliminar todos esos pedidos, incluidos los que podrían haber quedado huérfanos.
Terminará haciendo una eliminación. Con eso, dirá eliminar de la tabla de pedidos, donde el ID de cliente es igual al cliente que está eliminando.
Por lo tanto, puede poner en cascada estas eliminaciones. Entonces con elAll, guardará, actualizará y eliminará.
Ahora, cuando ejecute esta aplicación, verá el siguiente resultado.
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...
Como puede ver, hemos eliminado el código del programa que se conectó en cascada manualmente y nuestra aplicación sigue funcionando.
Entonces, dependiendo de su relación, es posible que desee convertirlos en cascada. Ahora, echemos un vistazo a una relación en cascada diferente. Vamos a ir a laOrder.hbm.xml archivo y podemos conectar en cascada esa relación de varios a uno.
<?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>
Entonces, si creamos un nuevo pedido y hay un nuevo cliente adjunto al mismo y decimos, guarde ese pedido, es posible que deseemos ponerlo en cascada. Pero una cosa que probablemente no querríamos hacer es si se elimina un pedido para eliminar al cliente correspondiente.
Entonces, aquí, querríamos hacer una actualización guardada, por lo que al usar una actualización guardada, enviará en cascada cualquier guardado o actualización a ese cliente. Entonces, si obtenemos un nuevo cliente o si cambiamos de cliente, eso se producirá en cascada. Si es una eliminación, no la eliminará de la base de datos.
Entonces, al ejecutar nuestra aplicación nuevamente, todo sigue funcionando como se esperaba.
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...
Ahora debe echar un vistazo a su aplicación, recuerde que el valor predeterminado es Ninguno y debe pensar en sus entidades y sus relaciones entre ellas para determinar las cascadas apropiadas para cada una de sus entidades, así como cada una de sus relaciones en esa base de datos.
En este capítulo, cubriremos la función de carga diferida. Es un concepto completamente diferente por defecto y NHibernate no tiene carga diferida, por ejemplo, si carga un cliente, no cargará todos los pedidos.
La recogida de pedidos se cargará bajo demanda.
Cualquier asociación, ya sea de varios a uno o de una colección, se carga de forma diferida de forma predeterminada, requiere un Open ISession.
Si ha cerrado su sesión, o si ha confirmado su transacción, puede obtener una excepción de carga diferida que no puede extraer esos objetos adicionales.
Debe tener cuidado con la carga diferida y la cantidad de datos que realmente necesita.
Puede desactivar la carga diferida para una asociación completa o puede poner lazy es igual a falso o también puede especificar una estrategia de búsqueda.
Aquí está el Program.cs implementación de archivos.
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;
}
}
}
Para entender esto, ejecutemos la aplicación y echemos un vistazo a NHibernate Profiler.
Como puede ver, tenemos el Seleccionar de cliente, dado un ID de cliente en particular y luego también tenemos otra tabla Seleccionar de pedidos, cuando realmente accede a la colección de ese cliente.
Entonces tenemos 2 viajes de ida y vuelta a la base de datos. Ahora, a veces, querríamos optimizar esto. Para hacer esto, vayamos alcustomer.hbm.xml file y agregue una estrategia de búsqueda y pídale que realice una búsqueda de combinación.
<?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>
Como puede ver, no hemos cambiado ningún código en nuestra aplicación, acabamos de agregar una estrategia de búsqueda en el customer.hbm.xml. Ejecutemos esta aplicación nuevamente, todavía se comporta exactamente de la misma manera. Veamos NHibernate Profiler.
Antes, el programa tenía dos viajes de ida y vuelta a la base de datos, ahora solo tiene uno y eso es porque está haciendo una combinación externa izquierda aquí.
Podemos ver que está haciendo una unión externa izquierda entre la tabla de clientes y la tabla de pedidos en función del ID del cliente y, por lo tanto, puede cargar toda esa información a la vez.
Hemos guardado 1 viaje de ida y vuelta en la base de datos.
La desventaja es que la información del cliente se duplicará en ambas líneas y esa es la forma en que funciona una combinación externa izquierda de SQL.
Entonces, con la estrategia de búsqueda, estamos extrayendo un poco más de datos y estamos ahorrando un viaje de ida y vuelta.
También puede hacer esto en el nivel de consulta, así que vayamos al Program.cs archivo y mire el ejemplo recargado más simple.
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();
}
Aquí, estamos haciendo una carga por parte del cliente. Ahora cambiemos a una consulta y usaremos una consulta de enlace como se muestra en el siguiente código.
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();
}
Eliminemos también la estrategia de búsqueda del customer.hbm.xml archivo.
<?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>
Ejecutemos esta aplicación de nuevo y verá el siguiente resultado.
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...
Ahora echemos un vistazo a NHibernate Profiler, puede ver que tenemos esta búsqueda de unión ansiosa sucediendo una vez más, pero esta vez, se basa en la consulta.
En este capítulo, cubriremos otra característica que son las relaciones inversas. Es una opción divertida que verá en la colección que es inversamente igual a verdadero y también confunde a muchos desarrolladores. Así que hablemos de esta opción. Para entender esto, realmente debes pensar en el modelo relacional. Supongamos que tiene asociaciones bidireccionales que utilizan una única clave externa.
Desde un punto de vista relacional, tiene una clave externa y representa tanto al cliente al pedido como a los pedidos al cliente.
Desde el modelo OO, tiene asociaciones unidireccionales utilizando estas referencias.
No hay nada que diga que dos asociaciones unidireccionales representan la misma asociación bidireccional en la base de datos.
El problema aquí es que NHibernate no tiene suficiente información para saber que customer.orders y order.customer representan la misma relación en la base de datos.
Necesitamos proporcionar inverse equals true como sugerencia, se debe a que las asociaciones unidireccionales están utilizando los mismos datos.
Si intentamos guardar estas relaciones que tienen 2 referencias a ellas, NHibernate intentará actualizar esa referencia dos veces.
En realidad, hará un viaje de ida y vuelta adicional a la base de datos y también tendrá 2 actualizaciones para esa clave externa.
La inversa es igual a verdadero le dice a NHibernate qué lado de la relación ignorar.
Cuando lo aplica al lado de la colección, NHibernate siempre actualizará la clave externa desde el otro lado, desde el lado del objeto secundario.
Entonces solo tenemos una actualización para esa clave externa y no tenemos actualizaciones adicionales para esos datos.
Esto nos permite evitar estas actualizaciones duplicadas de la clave externa y también nos ayuda a evitar violaciones de la clave externa.
Echemos un vistazo al customer.cs archivo en el que verá el AddOrdery la idea aquí es que ahora tenemos este puntero de retroceso desde el pedido hasta el cliente y debe configurarse. Entonces, cuando se agrega un pedido a un cliente, se establece el puntero de retroceso de ese cliente; de lo contrario, sería nulo, por lo que necesitamos esto para mantener esto conectado correctamente en el gráfico de objetos.
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
}
}
Aquí está el Program.cs implementación de archivos.
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;
}
}
}
Lo guardará en la base de datos y luego lo volverá a cargar. Ahora ejecutemos su aplicación y abramos NHibernate Profiler y veamos cómo realmente la guardó.
Notarás que tenemos 3 grupos de declaraciones. El primero insertará al cliente, y el ID de ese cliente es el Guid, que está resaltado. La segunda declaración se inserta en la tabla de pedidos.
Notará que la misma Guía de identificación de cliente está configurada allí, así que tenga configurada esa clave externa. La última declaración es la actualización, que actualizará la clave externa al mismo ID de cliente una vez más.
Ahora el problema es que el cliente tiene los pedidos y los pedidos tienen el cliente, no hay forma de que no le hayamos dicho a NHibernate que en realidad es la misma relación. La forma en que hacemos esto es con inversa igual a verdadero.
Así que vayamos a nuestro customer.hbm.xml archivo de mapeo y establezca el inverso igual a verdadero como se muestra en el siguiente código.
<?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>
Al guardar los pedidos, establecerá esa clave externa desde el lado del pedido. Ahora ejecutemos esta aplicación nuevamente y abramos el generador de perfiles NHibernate.
Si miramos cómo se insertan, obtenemos la inserción en el cliente y la inserción en los pedidos, pero no tenemos esa actualización duplicada de la clave externa porque se actualiza cuando se guardan los pedidos.
Ahora, debe tener en cuenta que si solo tiene una asociación unidireccional y es el conjunto el que mantiene esta relación, entonces si cambia el inverso es igual a verdadero, esa clave externa nunca se establecerá y esos elementos nunca tendrán su claves externas establecidas en la base de datos.
Si observa la relación de muchos a uno en el Order.hbm.xml archivo y busca inverso, en realidad no tiene un atributo inverso.
Siempre se configura desde el elemento secundario, pero si tiene una colección de varios a muchos, puede configurarlo desde cualquier lado.
En este capítulo, cubriremos cómo funcionan las funciones Load y Get y cómo podemos usarlas. Estas son dos API muy similares proporcionadas porISession para cargar un objeto por clave primaria.
Get - devolverá el objeto o un nulo.
Load - devolverá el objeto o lanzará un ObjectNotFoundException.
Ahora bien, ¿por qué tenemos estas dos API diferentes?
Carga
Es porque Load puede optimizar los viajes de ida y vuelta de la base de datos de manera mucho más eficiente.
Load realmente devuelve un objeto proxy y no necesita acceder a la base de datos justo cuando emite esa llamada Load.
Cuando accede a ese proxy, el objeto no está en la base de datos, puede lanzar una ObjectNotFoundException en ese punto.
Obtener
Por el contrario, con Get debido a las limitaciones de CLR o Common Language Runtime y NHibernate debe ir a la base de datos inmediatamente, verificar si los objetos están allí y devolver nulo, si no está presente.
No tiene la opción de objeto de retrasar esa búsqueda, ese viaje de ida y vuelta a la base de datos a un momento posterior porque no puede devolver un objeto proxy y eso cambió ese objeto proxy por un valor nulo, cuando el usuario realmente accede a él.
Echemos un vistazo a un ejemplo simple en el que verá cómo se utilizan realmente y la diferencia entre Get y Load. Continuaremos con las mismas clases de dominioCustomers y Orders e igualmente los mismos archivos de mapeo del último capítulo.
En este ejemplo, primero usaremos Get como se muestra en el siguiente programa.
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;
}
}
}
Como puedes ver que tenemos dos GuidIdentificación, la primera es una buena identificación, es la identificación de un cliente que sabemos que está en la base de datos. Mientras que el segundo ID no está presente en la base de datos. Ambos ID se pasan como parámetro aGet() método y luego el resultado se imprime en la consola.
Cuando se compile y ejecute el código anterior, verá el siguiente resultado.
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...
Como puede ver, los datos de Customer1 se imprimen pero los datos de Customer2 están vacíos, esto se debe a que el registro de Customer2 no está disponible en la base de datos.
Cuando vuelva a ejecutar su aplicación, podemos insertar un punto de interrupción antes de la declaración de confirmación y luego veamos a ambos clientes en la ventana Inspección.
Como puede ver, los datos de Customer1 están disponibles, mientras que Customer2 es nulo y el tipo es NHibernateDemo.Customer para ambos.
Ahora usemos el método Load en lugar de Get en el mismo ejemplo que se muestra en el siguiente código.
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;
}
}
}
Ahora ejecutemos este ejemplo y verá que se lanza la siguiente excepción como se ve en la captura de pantalla.
Ahora, si observa la ventana Inspección, verá que el tipo es proxy de cliente para ambos objetos. Y también verá los mismos datos para Customer1 en la ventana de la consola.
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
En este capítulo, cubriremos otra API común que la gente usará es el proveedor NHibernate LINQ. Su acceso a través de un método de extensión en ISession y la firma es unQuery <T>. Hay dos tipos de sintaxis al usar LINQ:
- Sintaxis de encadenamiento de consultas
- Sintaxis de comprensión de consultas
Sintaxis de encadenamiento de consultas
Puede acceder a cualquier registro de la base de datos utilizando la sintaxis de la cadena de métodos como se muestra en el siguiente programa.
var customer = session.Query<Customer>() .Where(c => c.FirstName == "Laverne")
Puede ver que tenemos una consulta y también una cláusula WHERE, puede tener cláusulas WHERE adicionales y una cláusula de selección similar.
Esta es una sintaxis de cadena de métodos estándar que puede utilizar en LINQ normal.
LINQ to Objects o LINQ to SQL, cualquiera de los otros proveedores de LINQ con los que esté familiarizado.
Echemos un vistazo a un ejemplo simple en el que recuperaremos al cliente cuyo nombre es Laverne. Ahora es posible que tengamos más de un cliente cuyo nombre de pila sea Laverne, por lo que recuperaremos solo el primero.
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;
}
}
}
Ahora, cuando se compile y ejecute el código anterior, verá el siguiente resultado.
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...
Sintaxis de comprensión de consultas
También está la sintaxis de comprensión de consultas, que se parece mucho más a SQL usando las palabras clave from, where y select.
Así que echemos un vistazo al mismo ejemplo, pero esta vez usamos la sintaxis de comprensión LINQ, que se parece mucho más a SQL como se muestra en el siguiente programa.
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;
}
}
}
Ahora ejecutemos esta aplicación nuevamente y verá el siguiente resultado.
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...
Echemos un vistazo a otro ejemplo en el que recuperaremos a todos aquellos clientes, cuyo Nombre comienza con la letra H.
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;
}
}
}
De manera similar, la sintaxis de comprensión de consultas se verá como el siguiente programa.
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;
}
}
}
Ejecutemos esta aplicación nuevamente y verá a todos los clientes, cuyo nombre comience con el alfabeto H.
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...
En este capítulo, cubriremos Hibernate Query Language. HQL se comparte entre Hibernate y NHibernate de Java.
Es el mecanismo de consulta más antiguo junto con Criteria.
Se implementó muy temprano y es una consulta basada en cadenas. API.
Lo accedes a través de ISession CreateQueryy es casi similar a SQL.
Utiliza muchas de las mismas palabras clave, pero tiene una sintaxis simplificada.
Es uno de los ejemplos más comunes, si está buscando cómo realizar una consulta, a menudo encontrará ejemplos de HQL.
El siguiente es un ejemplo simple de HQL:
var customers = session.CreateQuery("select c from Customer c where c.FirstName = 'Laverne'");
Aquí puede ver que seleccionan C del cliente, se parece mucho a SQL. Esta es una cadena opaca en lo que respecta a NHibernate, por lo que no sabe si es un HQL válido hasta el tiempo de ejecución, que es una de las desventajas.
Una de las fortalezas del proveedor LINQ es que puede compilar el soporte de tiempo.
Pero HQL es uno de los mecanismos de consulta más flexibles que se utilizan con frecuencia. Se dice que, si no hay otra forma de hacerlo, entonces hay una forma de hacerlo en HQL.
Echemos un vistazo a un ejemplo de Simpe en el que recrearemos nuestras consultas LINQ utilizando HQL en su lugar. Puede obtener acceso a HQL llamando alsession.CreateQuery y pasar como parámetro usando una cadena HQL.
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;
}
}
}
Esta cadena HQL se parece mucho a SQL, la principal diferencia es que FirstName es el nombre de la propiedad y no el nombre de la columna.
Entonces, si hay una discrepancia entre los dos, usa el nombre de la propiedad. Lo mismo, parece un nombre de tabla, pero en realidad es el nombre de la clase de la que estamos seleccionando.
Si la tabla de back-end fuera nombrada como Clientes, todavía usaríamos Cliente en nuestra consulta HQL.
Ejecutemos esta aplicación y verá el siguiente resultado.
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...
Echemos un vistazo a otro ejemplo simple en el que recuperaremos todos aquellos clientes cuyo Nombre comienza con la letra H usando HQL.
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;
}
}
}
Ejecutemos su aplicación nuevamente y verá que todos los clientes cuyo nombre comienza con H son devueltos por esta consulta.
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...
Podemos hacer cosas más complicadas, como querer todos los pedidos donde los clientes con un recuento de pedidos es mayor que 9. A continuación se muestra la consulta HQL para el mismo.
var customers = session.CreateQuery("select c from Customer c
where size(c.Orders) > 9");
foreach (var customer in customers.List<Customer>()) {
Console.WriteLine(customer);
}
También debemos indicar que necesitamos un tamaño aquí o un recuento o una longitud. En HQL, tenemos la opción de usar el método de tamaño especial como se muestra arriba.
La otra forma de escribir esto, si lo prefiere, es c.Orders.size, y esto tiene el efecto exacto.
var customers = session.CreateQuery("select c from Customer c
where c.Orders.size > 9");
foreach (var customer in customers.List<Customer>()) {
Console.WriteLine(customer);
}
Ejecutemos esta aplicación.
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...
Puede ver que todos los clientes que tienen más de 9 pedidos se recuperan de la base de datos.
En este capítulo, cubriremos el mecanismo de consultas de criterios. losNHibernate Query by Criteria API le permite crear una consulta manipulando objetos de criterios en tiempo de ejecución.
Este enfoque le permite especificar restricciones dinámicamente sin manipulaciones directas de cadenas, pero no pierde gran parte de la flexibilidad o el poder de HQL.
Por otro lado, las consultas expresadas como criterios suelen ser menos legibles que las consultas expresadas en HQL.
La sintaxis de criterios clásica es una API de consulta basada en objetos, como se muestra en el siguiente programa.
var customers = session.CreateCriteria<Customer>().Add(Restrictions.Like("FirstName", "H%"));
Como puede ver, estamos haciendo una sesión para crear criterios en el cliente y ahora estamos agregando un objeto de restricción a esa consulta.
Esto es útil para páginas de consulta donde los usuarios pueden seleccionar ciertas opciones, pero no otras.
Es más fácil construir la consulta como un tipo de estructura de consulta similar a un árbol en lugar de en HQL o LINQ, donde puede usar la cláusula AND u OR en WHERE.
Es más fácil simplemente agregar restricciones adicionales utilizando estos objetos de criterios.
Echemos un vistazo a un ejemplo simple en el que crearemos una consulta y obtendremos acceso a la API de criterios a través de createCriteria y luego agregue una restricción de que el nombre comience con H.
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;
}
}
}
Cuando se compile y ejecute el código anterior, verá el siguiente resultado.
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…
Echemos un vistazo a otro ejemplo simple en el que recuperaremos al cliente cuyo nombre es igual a "Laverne".
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;
}
}
}
Ejecutemos esta aplicación de nuevo y verá el siguiente resultado.
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...
Ahora, una de las principales desventajas de la API de criterios son estas cadenas opacas en los nombres de las propiedades. Por lo tanto, si el nombre se refactorizara para que fuera otra cosa, la herramienta de refactorización no necesariamente recogería la cadena opaca.
En este capítulo, cubriremos las consultas QueryOver. Es una nueva sintaxis que se parece más a LINQ usando la sintaxis de cadena de métodos como se muestra en la siguiente consulta.
var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "Laverne");
Sigue siendo un criterio oculto, pero ahora nuestras consultas están fuertemente tipadas.
Como hemos visto en la consulta de criterios, el primer nombre es solo una cadena opaca, ahora estamos usando un x.FirstName, por lo que el primer nombre se refactoriza y cambia de nombre que se cambia en la consulta de criterios de estilo de enlace usando la consulta over.
Todavía podemos hacer muchas cosas similares, pero no puede usar la sintaxis de comprensión de consultas con query over, tiene que usar la sintaxis de la cadena de métodos y no puede mezclar y combinar el enlace y los criterios.
Para muchas consultas, la consulta sobre API es muy útil y proporciona una sintaxis de objeto mucho más fácil de comprender que usar Criteria directamente.
Echemos un vistazo a un ejemplo simple en el que recuperaremos un cliente cuyo nombre es Laverne mediante una consulta sobre.
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;
}
}
}
Como puede ver, sigue siendo Criteria debajo de las cubiertas, pero es una sintaxis más agradable.
Cuando se compile y ejecute el código anterior, verá el siguiente resultado.
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...
Una de las desventajas es que, digamos que queremos decir que FirstName.StartsWith(“A”) como se muestra en el siguiente programa.
var customers = session.QueryOver<Customer>() .Where(x => x.FirstName.StartsWith("A"));
foreach (var customer in customers.List()) {
Console.WriteLine(customer);
}
tx.Commit();
Ahora ejecutemos la aplicación nuevamente y verá que este no es un proveedor LINQ ya que no sabe qué StartsWith método es, por lo que obtendrá un RunTime exception.
La excepción dice llamada de método no reconocida. Aquí estamos haciendo lo obvio, pero no necesariamente funciona.
Probemos algo más, como FirstName es igual a "A%" como se muestra en el siguiente código.
var customers = session.QueryOver<Customer>() .Where(x => x.FirstName == "A%");
foreach (var customer in customers.List()) {
Console.WriteLine(customer);
}
Ejecutemos esto una vez más y verá que no obtendremos ningún resultado como se muestra a continuación.
Press <ENTER> to exit...
Para entender por qué no estamos obteniendo ningún resultado, echemos un vistazo al generador de perfiles de NHibernate.
Como puede ver, el nombre es igual a A% que no lo es. Un% se usa en SQL con el operador like. Ahora necesitamos crear una restricción en la cláusula WHERE como se muestra en el siguiente programa.
var customers = session.QueryOver<Customer>()
.Where(Restrictions.On<Customer>(c => c.FirstName).IsLike("A%"));
foreach (var customer in customers.List()) {
Console.WriteLine(customer);
}
Ejecutemos su aplicación de nuevo y verá que todos los clientes se recuperan con el nombre que comienza con A.
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
Funciona de la misma manera que antes, excepto que usa este nuevo QueryOversintaxis. Muchos desarrolladores encuentran que la sintaxis LINQ es más accesible y, a menudo, hace lo correcto.
Si LINQ no puede manejarlo, entonces comenzará a buscar en HQL o Criteria para ver si eso va a ser más adecuado.
Simplemente le brinda una sintaxis diferente, por lo que Criteria, tanto el criterio de creación como el QueryOver le brindan otro mecanismo de consulta que le permite extraer datos de la base de datos usando NHibernate.
En este capítulo, cubriremos cómo usar las consultas SQL nativas en NHibernate. Si ha estado utilizando SQL escrito a mano durante varios años, es posible que le preocupe que ORM le quite algo de la expresividad y flexibilidad a las que está acostumbrado.
Las potentes funciones de consulta de NHibernate le permiten hacer casi todo lo que haría en SQL y, en algunos casos, más.
Para los casos excepcionales en los que no puede hacer que las funciones de consulta de NHibernate hagan exactamente lo que desea.
NHibernate le permite recuperar objetos usando el dialecto SQL nativo de su base de datos.
Echemos un vistazo a un ejemplo simple de las consultas SQL nativas en NHibernate.
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;
}
}
}
El ejemplo anterior usa CreateSQLQuery() para obtener una lista de objetos, y también notará que el tipo de entidad raíz que desea que devuelva la consulta se especifica como Cliente.
Ejecutemos su aplicación y verá que todos los clientes se recuperan de la base de datos.
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
Aquí hay otra forma de escribir consultas SQL nativas como se muestra a continuación.
IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER")
.AddScalar("Id", NHibernateUtil.Guid)
.AddScalar("FirstName", NHibernateUtil.String)
.AddScalar("LastName", NHibernateUtil.String) .List<Customer>();
Como puede ver, la consulta anterior especificó la cadena de consulta SQL y las columnas y tipos a devolver.
Esto devolverá una IList of Object arrays con valores escalares para cada columna en la tabla Customer.
Solo se devolverán estas tres columnas, aunque la consulta esté usando * y podría devolver más de las tres columnas enumeradas.
Echemos un vistazo a otro ejemplo sencillo.
IList<Customer> customers = session.CreateSQLQuery("SELECT * FROM CUSTOMER WHERE
FirstName = 'Laverne'")
.AddEntity(typeof(Customer)) .List<Customer>();
foreach (var customer in customers) {
Console.WriteLine(customer);
}
Ejecutemos su aplicación nuevamente y verá el siguiente resultado.
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...
Del mismo modo, puede especificar cualquier tipo de consulta SQL para recuperar datos de la base de datos.
En este capítulo, cubriremos NHibernate fluido. Fluent NHibernate es otra forma de mapeo o puede decir que es una alternativa a los archivos de mapeo XML estándar de NHibernate. En lugar de escribir XML(.hbm.xml files)documentos. Con la ayuda de Fluent NHibernate, puede escribir asignaciones en código C # fuertemente tipado.
En Fluent NHibernate, las asignaciones se compilan junto con el resto de su aplicación.
Puede cambiar fácilmente sus asignaciones al igual que el código de su aplicación y el compilador fallará en cualquier error tipográfico.
Tiene un sistema de configuración convencional, donde puede especificar patrones para anular las convenciones de nomenclatura y muchas otras cosas.
También puede establecer cómo se deben nombrar las cosas una vez, luego Fluent NHibernate hace el resto.
Echemos un vistazo a un ejemplo sencillo creando un nuevo proyecto de consola. En este capítulo, utilizaremos una base de datos simple en la que tenemos una tabla de Clientes simple como se muestra en la siguiente imagen.
Instalar Fluent NHibernate
El primer paso para iniciar Fluent NHibernate es instalar el paquete Fluent NHibernate. Así que abre elNuGet Package Manager Console e ingrese el siguiente comando.
PM> install-package FluentNHibernate
Una vez que se haya instalado correctamente, verá el siguiente mensaje.
Agreguemos una clase de modelo simple de Cliente y el siguiente programa muestra la implementación de la clase de Cliente.
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; }
}
}
Ahora necesitamos crear mapeos usando NHibernate fluido, así que agregue una clase más CustomerMapen su proyecto. Aquí está la implementación de la clase CustomerMap.
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");
}
}
}
Agreguemos otra clase NHibernateHelper en el que estableceremos diferentes ajustes de configuración.
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();
}
}
}
Ahora pasemos al Program.cs archivo en el que iniciaremos una sesión y luego crearemos un nuevo cliente y guardaremos ese cliente en la base de datos como se muestra a continuación.
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();
}
}
}
}
Ejecutemos su aplicación y verá el siguiente resultado.
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
Como puede ver, se crea el nuevo cliente. Para ver el registro del cliente, vayamos a la base de datos y veamos Ver datos y verá que se agrega 1 Cliente.