Configuración
El secreto para la sorprendente flexibilidad de NHibernate radica en su configurabilidad, Inicialmente el proceso de configurarlo puede ser más bien desalentador, pero después de un proyecto de prueba se vuelve natural. El primer paso es configurar el propio NHibernate. LA configuración más simple, que debe ser agregada a tu app.config o web.config se ve así:
<configuration>
<configSections>
<section name="hibernate-configuration"
type="NHibernate.Cfg.ConfigurationSectionHandler, NHibernate" />
</configSections>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
<session-factory>
<property name="hibernate.dialect">
NHibernate.Dialect.MsSql2005Dialect
</property>
<property name="hibernate.connection.provider">
NHibernate.Connection.DriverConnectionProvider
</property>
<property name="hibernate.connection.connection_string">
Server=SERVER;Initial Catalog=DB;User Id=USER;Password=PASSWORD;
</property>
<mapping assembly="CodeBetter.Foundations" />
</session-factory>
</hibernate-configuration>
</configuration>
Recuerda que nuestro objetivo es ampliar nuestra base de conocimientos viendo diferentes formas de construir sistemas con el fin de proveer un mayor valor a nuestros clientes. Mientras que podríamos hacerlo hablando específicamente de NHibernate, el objetivo es introducir el concepto de Definiciones de conversión Entidad relacional / Objeto, y tratar de corregir la fe ciega que tienen puesta los desarrolladores de .NET en los procedimientos almacenados y ADO.NET
De los 4 valores, dialect es el más interesante, este le dice a NHibernate que lenguaje específico habla nuestra base de datos. Sí, en nuestro código, le pedimos a NHibernate que regrese un resultado paginado de Cars y nuestro dialecto está configurado para SQL Server 2005, NHibernate emitirá una sentencia de SELECT utilizando la función de ranking ROW_NUMBER (). Sin embargo, si el dialecto está configurado a MySql, NHibernate emitirá el SELECT con LIMIT. En la mayoría de los casos, configurarás esto una sola vez y te olvidarás del tema. Pero esto proporciona algunos conocimientos sobre las capacidades provistas por la capa que genera todo tu código de acceso a datos.
En nuestra configuración, también le dijimos a NHibernate que nuestros archivos de definición de conversión estaban localizados en el ensamblado CodeBetter.Foundations. Los archivos de definición son archivos incrustados de XML que le dicen a NHibernate como debe ser persistida cada clase. Con esta información, NHibernate es capaz de regresar un objeto de tipo Car (automóvil) cuando tú se lo solicites, así como salvarlo. La convención general es tener un archivo de definición de conversión por cada objeto de dominio, y que estos se localicen dentro de la carpeta Mappings. El archivo de definición para nuestro objeto Model (Modelo), llamado Model.hbm.xml, se ve de esta forma:
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="CodeBetter.Foundations"
namespace="CodeBetter.Foundations">
<class name="Model" table="Models" lazy="true" proxy="Model">
<id name="Id" column="Id" type="int" access="field.lowercase-underscore">
<generator class="native" />
</id>
<property name="Name" column="Name"
type="string" not-null="true" length="64" />
<property name="Description" column="Description"
type="string" not-null="true" />
<property name="Price" column="Price"
type="double" not-null="true" />
</class>
</hibernate-mapping>
(Es importante asegurarse de que el parámetro Build Action para todos los archivos de definición de conversiones se configuren como Embedded Resources)
Este archivo le dice a NHibernate que la clase Model se refiere a registros en la tabla Models, y que las 4 propiedades Id, Name, Description y Price se refieren a las columnas Id, Name, Description, y Price. La información extra alrededor de la propiedad Id especifica que el valor es generado por la base de datos (en contraposición a NHibernate (para soluciones residentes en clústeres o grupos de servidores), o nuestro propio algoritmo) y que no hay configurador, así que deberá ingresar a través del campo con la convención de nombres especificada (proveemos Id como el nombre y la estrategia de nomenclatura con minúsculas y guión bajo (lowercase-underscore), para que use el campo llamado _id.
Con el archivo de definición de conversiones configurado, podemos comenzar a interactuar con la base de datos:
private static ISessionFactory _sessionFactory;
public void Sample()
{
//Let's add a new car model
Model model = new Model();
model.Name = "Hummbee";
model.Description = "Great handling, built-in GPS to always find your
way back home, Hummbee2Hummbe(tm) communication";
model.Price = 50000.00;
ISession session = _sessionFactory.OpenSession();
session.Save(model);
//Let's discount the x149 model
IQuery query = session.CreateQuery("from Model model where model.Name = ?");
Model model = query.SetString(0, "X149").UniqueResult<Model>();
model.Price -= 5000;
ISession session = _sessionFactory.OpenSession();
session.Update(model);
}
El ejemplo de arriba muestra lo fácil que es persistir nuevos objetos a la base de datos, extraerlos y actualizarlos – todo esto sin el uso directo de ADO.NET o SQL.
Tal vez te estés preguntando de donde viene el objeto _sessionFactory, y que es exactamente un ISession. _sessionFactory (que implementa la interfaz ISessionFactory) es un objeto global seguro para el uso en hilos de ejecución el cual es muy probable que crees en el inicio de la aplicación. Típicamente necesitaras uno por cada base de datos que tu aplicación este usando (lo que significa que típicamente necesitaras solo uno), y su trabajo, como con la mayoría de las fabricas de objetos, es crear un objeto pre configurado: un objeto ISession no tiene equivalente en ADO.NET, pero si se relaciona con un bajo nivel de cohesión a una conexión de base de datos. Sin embargo, la creación de un ISession no necesariamente abre una conexión, En lugar de eso, el objeto ISession administra de forma inteligente los objetos de tipo conexión y comando por ti. A diferencia de las conexiones que deben ser abiertas tarde y cerradas de manera temprana, no tienes que preocuparte por tener objetos ISession alrededor por un rato (aún cuando estas no sean seguras para su procesamiento en hilos de ejecución). Si estas construyendo una aplicación ASP.NET, puedes abrir de forma segura un objeto que implemente ISession en el método BeginRequest y cerrarlo en el método EndRequest (o mejor aún, cargar de forma diferida en caso de que la solicitud específica no requiera un ISession).
La interfaz ITransaction es otra pieza del rompecabezas que es creada llamando el método BeginTransaction en un ISession. Es común para los desarrolladores de .NET ignorar la necesidad de usar transacciones dentro de sus aplicaciones. Esto es desafortunado ya que puede llevarnos a estados inestables e incluso irrecuperables de los datos. Un ITransaction es usado para mantener el rastro de la unidad de trabajo - rastrear que cambio, o que ha sido agregado o borrado, averiguar qué y cómo aplicarlo a la base de datos, y proveer la capacidad de deshacer en caso de que un paso individual falle.