martes, 23 de abril de 2013

How to... Como comenzar un proyecto con NHibernate y PostgreSQL en C#. Ejemplo práctico

Desde hace unos días que me toca lidiar con C# para un desarrollo de escritorio y estamos comenzando con ello. Durante el desarrollo queremos usar NHibernate con Persistence ya que simplifica enormemente el uso de bases de datos y en otros proyectos lo he empleado extensivamente, aunque siempre con Java.
Antes de entrar en materia, para aquellos que no tengan experiencia previa en NHibernate les recomiendo leer estas dos entradas (esta y esta) para conocer un poco sobre su funcionamiento. 
Lo que voy a hacer es un ejemplo muy básico, empleando una clase que llamaremos Empleado.cs con varios atributos de tipo string. Además mapearé la clase con un fichero empleado.hbm.xml. Como base de datos usaré PostgreSQL sobre Windows. Podéis consultar en esta entrada del blog como instalarlo y ponerlo en marcha.
Lo primero de todo es la creación del proyecto con Visual Studio 2012. Para ello en File-New Project vamos a elegir Windows Form Application y le llamaremos Ejemplo:
 
Vamos primero a añadir los paquetes que necesitamos para usar NHibernate. Para ello haciendo click con el botón derecho sobre las References de nuestro proyecto, abriremos el Manage NuGet Packages.
Buscamos NHibernate y lo instalamos. Empleando el NuGet nos ahorramos tener que bajarnos las librerías y agregarlas al proyecto, haciéndolo NuGet por nosotros.
 
Además necesitamos el driver de PosgreSQL de igual forma que hicimos con NHibernate. Para ello instalaremos el paquete Npgsql.
 
 Ahora vamos a crearnos nuestra primera clase que llamaremos Entity. Para ello vamos a crearnos una carpeta y situaremos la clase en su interior. 
Nos crearemos esta clase con el fin de que todas las clases que nos crearemos extiendan de esta y así tener unos parámetros comunes. Estos parámetros no son más que el Id y la Versión. El contenido de esta clase será:  
namespace Ejemplo.Entities
{
    public abstract class Entity<TId>
    {
        public virtual TId Id { get; protected set; }
        protected virtual int Version { get; set; }

        public override bool Equals(object obj)
        {
            return Equals(obj as Entity<TId>);
        }

        private static bool IsTransient(Entity<TId> obj)
        {
            return obj != null &&
                   Equals(obj.Id, default(TId));
        }

        private Type GetUnproxiedType()
        {
            return GetType();
        }

        public virtual bool Equals(Entity<TId> other)
        {
            if (other == null)
                return false;
            if (ReferenceEquals(this, other))
                return true;
            if (!IsTransient(this) &&
                !IsTransient(other) &&
                Equals(Id, other.Id))
            {
                var otherType = other.GetUnproxiedType();
                var thisType = GetUnproxiedType();
  return thisType.IsAssignableFrom(otherType) ||
   otherType.IsAssignableFrom(thisType);           }
            return false;
        }

        public override int GetHashCode()
        {
            if (Equals(Id, default(TId)))
                return base.GetHashCode();
            return Id.GetHashCode();
        }
    }
    public abstract class Entity : Entity<Guid>
    {  }
}
Una vez que hemos hecho esto ya podemos crearnos nuestra entity Empleado.cs. A esta clase le vamos a meter cuatro atributos tipo string muy sencilllos:
namespace Ejemplo.Entities
{
    class Empleado : Entity
    {
        public virtual string Nombre { get; set; }
        public virtual string Direccion { get; set; }
        public virtual string Telefono { get; set; }
        public virtual string Fax { get; set; }

    }
}
Ahora tenemos que crearnos el fichero de mapeo de estas clases. Se tiene que llamar Empleado.hbm.xml y con el siguiente contenido:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
    assembly="Ejemplo"
    namespace="Ejemplo.Entities">
  <class name="Empleado">
    <id name="Id">
      <generator class="guid.comb" />
    </id>
    <natural-id mutable="true">
      <property name="Nombre" not-null="true" />
    </natural-id>
    <version name="Version" />
    <property name="Direccion" />
    <property name="Telefono" />
    <property name="Fax" />
  </class>
</hibernate-mapping>
Si nos fijamos en el código anterior, vemos como como hemos mapeado con una etiqueta del tipo property cada uno de los atributos de la clase. Además nos fijaremos en como se define la clave primaria con el atributo generator y como seleccionamos el nombre como natural-id.
Debemos de tener en cuenta que en las propiedades de este archivo se ha de cambiar el "Build Action" a Embedded Resource.
Una vez que hemos hecho esto nos crearemos el fichero de conexión con la base de datos. Este fichero se tiene que llamar de forma obligatoria Hibernate.cfg.xml. Como hemos dicho, vamos a usar la conexión con PostgreSQL por lo que su contenido debe de ser el siguiente:
<?xml version="1.0" encoding="utf-8" ?>
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2" >
<session-factory name="NHibernate.Test">
<property name="connection.driver_class"> NHibernate.Driver.NpgsqlDriver </property>
<property name="connection.connection_string">
 Server=localhost; Database=database; User ID=postgres; Password=password;</property>

<property name="dialect">NHibernate.Dialect.PostgreSQL82Dialect </property>
 <mapping assembly="Empleado"/>
  </session-factory>
</hibernate-configuration>
Vemos como hemos indicado en el connection_string los parámetros de conexión con nuestra base de datos, estos deben de ser cambiados en función de nuestra configuración. Además debemos de cambiar en las propiedades del archivo el Copy to Output Directory de "Do not copy" a "Copy if never"
 

Ahora vamos a modificar el formulario añadiendo 4 Label con 4 TextBox. En cada uno de los EditText vamos a recoger los parámetros para la creación de un nuevo empleado cuando se pulse un botón. Además nos crearemos otro botón para leer los empleados que hayamos introducido. El aspecto que tendrá nuestro formulario será el siguiente:
Dentro de evento click de Crear Empleado tenemos que crearnos un objeto de la clase Empleado y almacenarlo en la base de datos. Para ello usaremos el siguiente código: 
private void buttonCrearEmpleado_Click(object sender, EventArgs e)
 {
     Empleado empleado = new Empleado()
     {
         Nombre = textBoxNombre.Text.ToString(),
         Direccion = textBoxDireccion.Text.ToString(),
         Telefono = textBoxTelefono.Text.ToString(),
         Fax = textBoxFax.Text.ToString()
     };

   var hibernateConfiguration = new Configuration().Configure();
 var sessionFactory = hibernateConfiguration.BuildSessionFactory();
   var session = sessionFactory.OpenSession();
   var tx = session.BeginTransaction();
   session.Save(empleado);
   tx.Commit();
}
Si repasamos el código anterior vemos como una vez creado el objeto Empleado nos creamos un sessionFactory, abrimos la conexión con OpenSession, después BeginTransaction, hacemos un Save del objeto y ya solamente nos queda el Commit para que los cambios se realicen en ese mismo momento.
Una vez que ya tenemos implementados el método para guardar en base de datos nuestros empleados nos falta el método para leerlos. Para ello en el evento click del botón leer tenemos que escribir lo siguiente: private void buttonLeerEmpleados_Click(object sender, EventArgs e)
{
    var nhConfig = new Configuration().Configure();
    var sessionFactory = nhConfig.BuildSessionFactory();
    var session = sessionFactory.OpenSession();
    var tx = session.BeginTransaction();
    var empleados = GetEmpleados(session);
    foreach (Empleado empleado in empleados)
    {
    Console.WriteLine(empleado.Nombre + " " + empleado.Direccion
    + " " + empleado.Telefono + " " + empleado.Fax + "\n");
    }}

   static IList<Empleado> GetEmpleados(ISession session)
   {
      var query = session.CreateCriteria<Empleado>()
        .AddOrder(Order.Asc("Nombre"));
       return query.List<Empleado>();
   }
Si repasamos el código, vemos que es similar al anterior, tenemos que abrir la conexión, luego BeginTransaction y llamamos al método GetEmpleados que nos hemos implementado abajo. Este método mediante un Criteria nos traemos todos los empleados y los devolvemos en una lista. Esta lista la recorremos para imprimir en consola cada uno de los empleados. Por último, debemos de añadir lo siguiente en la clase Program.cs justo después del Main.
try
{
 var hibernateConfiguration = new Configuration().Configure();
 var sessionFactory = hibernateConfiguration.BuildSessionFactory();
 var schemaExport = new SchemaExport(hibernateConfiguration);
schemaExport.Create(false, true);
}
catch (Exception err)
{   }
Lo que estamos haciendo con este código es la creación directamente de las tablas que necesitamos en nuestra base de datos. Esta característica nos permite olvidarnos de nuestra base base de datos, crearnos nuestro modelo mediante los archivos de mapeo .xml y crear todas las tablas necesarias. Tenemos que tener en cuenta que cada vez que se abra la aplicación se van a borrar las tablas que existían y se crearán nuevas por lo que perderemos lo que existe. Por lo tanto debemos de borrar estas líneas una vez que tengamos el modelo de datos creado.
Si creamos un nuevo elemento tal y como vemos en la imagen siguiente y le hacemos click en el botón crear empleado:
 
Si abrimos pgAdminIII veremos como se ha creado la tabla y tenemos el registro en su interior. 
Si ahora pulsamos el botón de leer empleados veremos en la consola los empleados que hemos creado, en este caso solamente uno.

Esto sería una primera aproximación al uso de NHibernate con PostgreSQL. Iré añadiendo más tutoriales a medida que avance. Todo lo relacionado con este tipo de desarrollos lo puedes encontrar en la siguiente página del blog (aquí)
Espero que os sirva y si tenéis cualquier duda o problema emplear los comentarios para preguntar.


2 comentarios :

  1. Pongo aquí el enlace al repositorio desde donde se puede descargar el código empleado en este ejemplo https://github.com/josealopezpastor/NHibernate-Example

    ResponderEliminar