Generando con AjGenesis desde los assemblies

AjGenesis es una herramienta de generación de código a partir de templates y un modelo libre.

Bien vayamos a esta frase, se dice rápido… pero analicemosla: "…a partir de templates…", esto no tiene nada de nuevo, MyGeneration lo hace, y millones de generadores de código que andan sueltos por ahí también. Pero el punto más que interesante está: "a partir de un modelo libre", y esto que quiere decir ? Significa que no hay restricciones sobre como debe ser el modelo. Si bien debe ser escrito en Xml, esta es una forma de representar el modelo para que lo analice AjGenesis, esto no puede ser considerado una restricción.

Ahora bien, el modelo de AjGenesis puede ser generado inclusive desde la base de Datos, existen unos ejemplos de ello disponible junto con la distribución de AjGenesis.

Hace un tiempo salió la inquietud de que AjG podría generar el modelo a partir de los hbm de NHibernate, la gente que usa NHibernate estaría contenta incluyendome. Pero estuve pensando que los xml se deberían sobrecargar con muchos datos que por ahora los defaults de NHibernate los consideran dejando el mapping más limpio y fácil de leer. Por ejemplo, mis mappings files no tienen el tipo de dato (salvo excepciones), NHibernate hace el trabajo, sin esta información, el modelo que se genere ó el código que se genere junto a los templates es casi inutil sin los tipos de datos.

Otra opción para generar el modelo, es a partir de las clases disponibles en los ensamblados de la aplicación que estemos usando. Hace un tiempo escribí esta herramienta que ahora decidí cambiarla un poco. Ahora escribí ésta herramienta que la pueden descargar aquí.

Como funciona AjGenesis.FromAssembly? Facil,  es una librería que itera sobre un ensamblado especificado y devuelve una lista de Objetos que representan la estructura de las clases:

<#
AjGenesisFromAssemblyPath = "..\..\bin\AjGenesis.FromAssembly.dll"
function GetEntities(path)

	AssemblyManager.LoadFrom(AjGenesisFromAssemblyPath)
	obj = new AjGenesis.FromAssembly.Collector()
	list = obj.GetEntities(path,null)
	return list

end function

AssemblyManager es la clase encargada de cargar el assembly, una vez hecho esto podemos trabajar con él.

Para obtener la listar las clases en el assembly usamos la clase Collector, y la función GetEntities, especificando la ruta del ensamblado.

El template para generar la configuración de IoC en el xml, podría ser similar a este:

<#
nsServices = "MyApp.Service"
assemblyName = "MyApp.Service"
moneySign = "$"
oBraces = "{"
cBraces = "}"
include	"${TemplateDir}\Functions.tpl"
#>


  
<#
	for each Entity in Entities
#>


		<${Entity.Name}Dao>${moneySign}${oBraces}${Entity.Name}.Dao${cBraces}
		
	
    
<#
	end for
#>
	

Algo interesante al generar código que a su vez tiene instrucciones de un lenguaje “tipo” template, es decir con comandos de esta forma ${variable}, es que debemos usar caracteres en el código generado y en el codigo para que lo interprete el generador de código. AjGenesis en los templates y Windsor en la configuración, usan los caracteres $, {, }. De modo que para no que AjGenesis no se confunda en el parseo, creé variables que contienen estos símbolos.


Descargar Aquí

Existen muchas cosas que se me ocurrieron para mejorarlo, pero la base ya está, ahora hay que empezar a retocarla. Se me planteó la inquietud crear atributos (annotations) para las clases de modo que se pueda enriquecer o restringir el modelo generado. Pero supongo que sería la primera herramienta de generación de código intrusiva que se pueda configurar por medio de annotaciones y quizas se convertiría en overhead para la aplicación.

Charla de Generación de código en Corrientes

El miercoles con unos amigos tuvimos la oportunidad de estar en una charla de el queridísimo Angel "Java" Lopez en Corrientes capital.

La charla estuvo buena, habló de la realidad del software, un poco sobre la evolución de los sistemas y la complejidad que debemos enfrentar hoy día los desarrolladores. Para poder enfrentar esta complejidad, podemos hacer uso de herramientas de generación de código y facilitarnos un poco la vida. El materia de la charla lo pueden descargar aquí. Angel no podía dejar de mostrar a su bebé ;)

Esta es una de las imagenés de la presentación que expuso, y que muestra la realidad de nuestras aplicaciones, existe mucho código que se puede generar y otro código lo debemos escribir nosotros, y quizás este código pueda ser encapsulado en un framework para nuestro bien y del que lea el código.

 

Quizás la generación de código no sea "la solución" pero nos hace la vida más fácil y se encarga del código "aburrido". Miremos nuestro código y seguramente encontraremos código que podemos generar. Esto es un primer paso a la factoría de Software.

Generando aplicaciones con AjGenesis

Este es el titulo del tutorial que nos deja Angel en su blog acerca de su querido AjGenesis.

Dos puntos que destaca Angel son:

- El modelo del que parte es totalmente definible por el usuario

- Las tareas y plantillas a aplicar son totalmente programables y controlables

Y de estos 2 puntos me quedo con el primero que me alcanza y sobra para decir cuan flexible es AjGenesis. Otras herramientas de generación de código muy útiles (vale la redundancia: todas las herramientas de generación de código son útiles) como MyGeneration, permiten la generación de código a partir de un modelo de datos. Esta es una gran diferencia con AjGenesis, esta permite la generación a partir de un modelo libre, no hay restricciones. El modelo lo podemos obtener desde una base de datos, a partir de nuestras clases, o podemos ir desarrollandolo de manera artesanal (a mano).

using NHibernate.Tool.hbm2ddl

Una herramienta muy importante de NHibernate, a la vez, deseable por los ORM, es la generación de código.

Para generar el DDL de la base de datos, nos podemos valer de la información del esquema, que nos brindan los archivos de mapeo ó mapping files: hbm.xml.

Un vez que tenemos bien configurados estos archivos, podemos generar las tablas con solamente incluir un par de sentencias en .Net y configurar un archivo xml. Tambien se tiene que tener referenciado a NHibernate.

Código en C#:

using System;
using NHibernate.Cfg;
using  NHibernate.Tool.hbm2ddl;
public class MyClass
{
    public static void Main()
    {
            Configuration config = new Configuration();
            config.Configure();
            SchemaExport exporter = new SchemaExport(config);
            //exporter.SetOutputFile(@"c:testDDL.sql");
            exporter.Drop(true, true);
            exporter.Create(true, true);
    }
}

Archivo hibernate.cfg.xml (debe ir copiado en el directorio de salida):

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-configuration  xmlns="urn:nhibernate-configuration-2.0" >
 <session-factory name="NHibernate">
	<property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
	<property name="connection.driver_class">NHibernate.Driver.FirebirdClientDriver</property>
	<property name="connection.connection_string">
	ServerType=1;
	User=sysdba;password=masterkey;Database=C:ruraldata.fdb;
	Pooling=false
	</property>
	<property name="show_sql">true</property>
	<property name="dialect">NHibernate.Dialect.FirebirdDialect</property>
	<property name="use_outer_join">true</property>
	<property name="query.substitutions">true 1, false 0, yes 'Y', no 'N'</property>

	<mapping assembly="RuralSolution.Entities" />

 </session-factory>
</hibernate-configuration>

En este caso es la exportación del schema de una base de datos Firebird embebida.

Esta herramienta me ha sido de mucha utilidad. Estoy desarrollando una aplicación en SQL Server 2005 y NHibernate, pero, me dí cuenta que la aplicación debía ser portable, de modo que tuve que migrar la base de datos a una portable, y como Firebird está pasando todos los test de NH, la elegí. Ya tenía los hbm, así que no requirió trabajo demás.

Importante para la generación con Firebird: en el directorio de salida entonces tendriamos que tener: fbembed.dll, FirebirdSql.Data.FirebirdClient.dll, y hibernate.cfg.xml.

Si quieren saber como trabajar con MyGeneration y Firebird embedded léanse este post de mi amigo Matias.

Reflexion + Generación de código = Modelo para AjGenesis

Estuve viendo AjGenesis, que es un generador de codigo escrito por Angel “Java” Lopez, la verdad muy útil. Se puede generar código para cualquier lenguaje que estén escritas las plantillas, y sino están escritas, las podés escribir con mucha facilidad, ya que vienen algunas plantillas de ejemplo como para ir conociendo el lenguaje AjGenesis. Para poder generar código se requiere, como todo generador de cógido: un modelo. Este modelo está escrito en XML.

Por ahora, AjGenesis, no cuenta con una interfaz amigable para un usuario final, pero funciona y genera de lo lindo. Para comenzar a usarlo se haría así:

#AjGenesis.Console.exe ModelsModel.xml Tasksgenerate.ajg 

Donde Model.xml contiene los links a los otros archivos xml que contienen las descripciones de las entidades. Generate.ajg es el archivo de tareas que el generador vá a usar, vendría a ser como el archivo main para generar, él se encargará de buscar los templates. Por lo general el generate.ajg, tendrá un loop que iterará entre todas las entidades de negocio y así ir generando los artefactos en el lenguaje en que esten trabajando.

Ahora bien, como hago para obtener de forma automática el modelo?

En un proyecto que empecé con db4o, y el diseño de las business entities lo hacía con el Class Designer del Visual Studio 2005, se me ocurrió que el modelo de clases lo podía obtener del assembly donde están las clases de negocio. Es decir, tengo un proyecto que tiene adentro solo las clases de negocio, como podrían ser: Clientes, Factura, LineaFactura, etc.

Así nació GenerateFromAssembly, que es una herramienta muy sencilla, que via Reflexion, itera entre las clases de la dll, y obtiene sus propiedades con sus respectivos tipos de datos.

Actualmente la estoy usando en este proyecto, ya no con db4o, estoy usando NHibernate contra un Sql Server, pero la herramienta funciona igual, y genera el modelo como lo debe hacer.

Cabe destacar que la idea de la herramienta es: A partir del diseño de clases generar un assembly de .Net, y a partir de él, generar el modelo, y con el modelo y los templates de AjGenesis: obtengo mis artefactos, ya sean: Web Services, artefactos de capa de negocio, de capa de acceso a datos, o formularios web/windows.

Existe un issue: los tipos de datos genericos. Por ejemplo, si tengo IList<Cliente>, el CLR los trata como si fueran IList´1[Cliente], es una cuestión de nombres, nada más. De modo que en el código final generado con AjGenesis obtendremos los tipos de datos con este último esquema. Se podría hacer otra herramienta, que al codigo final generado, lo masajee con algunas expresiones regulares y exprese los generic tipes segun el lenguaje .Net que estemos usando.