RSS

Mono + NHibernate + SQLite

martes, 4 de noviembre de 2008


Después de usar por tanto tiempo Gentle.NET, un ORM que ha sido discontinuado, he decidido al fin comenzar a estudiar NHibernate. Los ejemplos que dan en los manuales y páginas son para Windows (con MS .NET) y SQL Server. Trabajando en GNU/Linux, hice las pruebas con SQLite.

El problema es que el driver utilizado por NHibernate para esta base de datos utiliza un binding desactualizado. Además Mono trae una implementación mejor, que soporta el standard ADO.NET 2.0, que es Mono.Data.Sqlite, disponible a partir de la versión 1.2.4.

El post es especialmente útil para aquellos que quieran utilizar en GNU/Linux (yo uso Ubuntu Gutsy), con Mono, NHibernate y SQLite, utilizando el binding Mono.Data.Sqlite. Si bien puede parecer esto tan fácil como seguir el documento QuickStart y cambiar las opciones correspondientes, no lo es si se intenta utilizar SQLite. Por eso, en este post no voy a explicar todas las cosas, ya que no me interesa y se pueden aprender en la documentación, sino que voy a desarrollar un ejemplo muy sencillo y mostrar cómo utilizar SQLite, ya que hay que solucionar unos problemas no muy triviales.

Cabe aclarar que, si bien todavía no lo he probado, los archivos de mapeo (hbm.xml) y el schema SQL se pueden autogenerar.

Primero creo la tabla que almacenerá los objetos en un archivo llamado, por ejemplo, data.db.sql:

DROP TABLE IF EXISTS personas;

CREATE TABLE personas (
id integer PRIMARY KEY,
nombre varchar(20),
apellido varchar(20)
);

Para el ejemplo utilicé la última versión de SQLite. Para crear la base de datos en el archivo data.db, ejecutamos:

$ sqlite3 data.db <>

La clase de ejemplo (Persona.cs):

using System;

namespace PruebaNHibernate
{
public class Persona
{
private int id;
private string nombre;
private string apellido;

public Persona()
{
}

public int Id {
get { return id; }
set { this.id = value; }
}

public string Nombre {
get { return nombre; }
set { this.nombre = value; }
}

public string Apellido { get { return apellido; } set { this.apellido = value; } } } }

Luego hay que escribir el archivo de mapeo, que es un XML.
version="1.0" encoding="utf-8" ?>
xmlns="urn:nhibernate-mapping-2.2"
namespace="PruebaNHibernate" assembly="PruebaNHibernate">


name="Persona" table="personas">
name="Id" column="id" type="Int32">
class="increment" />
>

name="Nombre" column="nombre" />
name="Apellido" column="apellido" />
>
>

Este archivo se debe agregar como recurso en MonoDevelop.

Luego escribimos el archivo de configuración para nuestra aplicación (app.config):

<?xml version="1.0" encoding="utf-8" ?>
>
>
name="nhibernate"
type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0,Culture=neutral, PublicKeyToken=b77a5c561934e089" />

>

>

key="hibernate.connection.provider"
value="NHibernate.Connection.DriverConnectionProvider"
/>


key="hibernate.dialect"
value="NHibernate.Dialect.SQLiteDialect"
/>


key="hibernate.connection.driver_class"
value="PruebaNHibernate.MonoDataSqliteDriver, PruebaNHibernate"
/>


key="hibernate.connection.connection_string"
value="Data Source=data.db,version=3"
/>


key="hibernate.query.substitutions"
value="true=1;false=0"
/>


key="hibernate.use_prox y_validator"
value="false"
/>

>
>

Nota: Debido a problemas con el hosting no puedo colocar, arriba, el string correcto en la última parte. La palabra prohibida “p r o x y” (sin espacios) tiene un espacio de más entre la “x” y la “y”. En fin, deshabilito en esas lineas la comprobación de clases p r o x y.

Luego en un archivo como Main.cs agrego código en el método principal Main para jugar con todo esto:

using System;
using NHibernate;
using NHibernate.Cfg;

namespace PruebaNHibernate
{
class MainClass
{
public static void Main (string[] args)
{
Configuration conf = new Configuration();
/* Con la siguiente línea leo todo los archivos con extensión
* hbm.xml embebidos como recurso en el assembly especificado */

conf.AddAssembly("PruebaNHibernate");

ISessionFactory sessionFactory = conf.BuildSessionFactory();

ISession s = sessionFactory.OpenSession();

ITransaction tx = s.BeginTransaction();

Persona p = new Persona();
p.Nombre = "Milton";
p.Apellido = "Pividori";
s.Save(p);
tx.Commit();

Console.WriteLine("p - Id: " + p.Id);
Console.WriteLine("p - Nombre: " + p.Nombre);
Console.WriteLine("p - Apellido: " + p.Apellido);
Console.WriteLine();

Persona p2 = (Persona)s.Load(typeof(Persona), p.Id);

Console.WriteLine("p2 - Id: " + p2.Id);
Console.WriteLine("p2 - Nombre: " + p2.Nombre);
Console.WriteLine("p2 - Apellido: " + p2.Apellido);
Console.WriteLine();

/* Verifico si son el mismo objeto p y p2, ya que estamos
* dentro de la misma sesión */

if (p == p2)
Console.WriteLine("p y p2 son el mismo objeto");

p2.Apellido = "Paduán";

s.Flush();

s.Close();
}
}
}

En el proyecto agregué estas referencias:

  • NHibernate.dll
  • System

Ahora vamos a lo que nos interesa. En el archivo app.config (que al compilar se traducirá a .exe.config en el directorio de salida), observemos estas lineas:


key="hibernate.connection.driver_class"
value="PruebaNHibernate.MonoDataSqliteDriver, PruebaNHibernate"
/>

NHibernate trae un driver para SQLite, como dije al principio, pero que utiliza un provider que fue discontinuado. Por lo tanto, para utilizar Mono.Data.Sqlite, cree una clase en mi assembly (PruebaNHibernate) con el nombre que se indica allí (MonoDataSqliteDriver):

using System;
using NHibernate.Driver;

namespace PruebaNHibernate
{
public class MonoDataSqliteDriver : ReflectionBasedDriver
{
public MonoDataSqliteDriver()
: base("Mono.Data.Sqlite",
"Mono.Data.Sqlite.SqliteConnection",
"Mono.Data.Sqlite.SqliteCommand")
{
}

public override bool UseNamedPrefixInSql
{
get { return true; }
}

public override bool UseNamedPrefixInParameter
{
get { return true; }
}

public override string NamedPrefix
{
get { return "@"; }
}

public override bool SupportsMultipleOpenReaders
{
get { return false; }
}
}
}

En el constructor de la misma le paso a mi clase padre los argumentos que necesita: nombre del assembly (en este caso “Mono.Data.Sqlite”), clase para crear la conexión y ejecutar comandos. Desconozco los detalles de SQLite, así que copié los métodos sobreescritos del driver viejo.

No pude hacer que NHibernate cargue el assembly “Mono.Data.Sqlite” desde el GAC, así que hay que copiarlo al directorio del ejecutable. Es necesario copiar otros archivos, como las dependencias de NHibernate y la base de datos (data.db). Se pueden bajar el proyecto completo para MonoDevelop aquí. El archivo obviamente no es un png, pero sino no lo puedo subir :)

Ahora bien, hay un problemita… el archivo System.Data.dll que viene con Mono 1.2.4 en Gutsy tiene un bug que hará que el ejemplo no funcione. El mismo ya fue solucionado en la versión 1.2.6. Así que, como workaround, pueden bajarse el paquete de Debian libmono-system-data2.0-cil correspondiente a la versión 1.2.6 de Mono y sobreeescribir ese archivo, que se ubica en /usr/lib/mono/gac/System.Data/2.0.0.0__b77a5c561934e089/. Reporté el problema en Ubuntu, así que quizá lo actualicen dentro de poco.

Con esto podremos utilizar SQLite con NHibernate, bajo Mono, con un binding actualizado.

Autor : Milton Pividori

FUENTE DEL ARTICULO : http://www.miltonpividori.com.ar/2007/12/30/mono-nhibernate-sqlite/


SQLite y Gambas2

domingo, 2 de noviembre de 2008

Gambas es otro de los tantos hacks recursivos que se usan, en si el significado es "Gambas Almost Means Basic", pensado como herramienta RAD y con Basic como lenguaje base, pero mejorado, vemos que tambien es multiprosito, en esta ocasión vamos a ver como hacer una conexión a una base de datos SQLite desde Gambas2.

Lo primero que debemos tener disponible para trabajar, ademas de SQLite, es Gambas2, en Debian y derivados:

#apt-get install gambas2

para otras distros podemos encontrar las fuentes en http://gambas.sourceforge.net/
la parte mas importante esta en saber cuales son las herramientas que nos provee el languaje, y los datos de conexión de almacenan en un solo botón, el codigo es el siguiente:

'Instanciamos un objeto de clase Connection
PRIVATE $hConn AS Connection

'Boton de conexión a la base de datos
PUBLIC SUB btnC
onnect_Click()

DIM sName AS String
DIM hTable AS Table

TRY $hConn.Close



sName = txtName.Text

IF sName <> "" THEN

'Asignamos valores a las pr
opiedades que vamos a usar
WITH $hConn

'Definimos el tipo de base de datos
.Type = "sqlite"


'Definimos el nombre del host
.Host = txtHost.Text

END WITH


'Asignamos el nombre de la conexion
$hConn.Name = sName

'Abrimos la
conexion para poder trabajar
$hConn.Open

'Habilitamos los otros formularios
frmDatabase.Enabled = TRUE
frmRequest.Enabled = TRUE

btnConnect.Enabled = FALSE
btnClose.Enabled
= TRUE
CATCH

Message.Error(DConv(Error.Text))
ENDIF

END
A continuación capturas de la aplicacion terminada y en diseño.



Antes de terminar debo aclarar que la base de datos debe estar creada, en caso contrario no se realiza la conexión, ademas se debe dar la ruta completa de la ubicación del archivo que contiene dicha base de datos. La aplicación completa para su descarga en el siquiente link:
Descarga