Angel "Java" Lopez

NET, Java, PHP y Desarrollo de Software

This Blog

Syndication

Search

Tags

Community

Email Notifications

Archives

.NET

ASP.NET

Windows Form

VB.NET

C#

Sitios

Blogs

Generando aplicaciones con AjGenesis

En estos días, estuve escribiendo unos ejemplos de AjGenesis

http://www.ajlopez.com/ajgenesis

mi proyecto de código abierto de generación de código, para producir, desde un modelo, aplicaciones tanto en ASP.NET 1.x/2.x, como en JSP, tanto usando SQL Server en el primer caso, como con MySql en Java, y tanto con ADO.NET, como con NHibernate, Hibernate, ya sea con arquitectura de capas, como en capas a la Domain-Driven Design de Evans.

Sigo escribiendo esos ejemplos, pero ya hay algo publicado en

AjGenesisExamples3.zip

Usaremos la versión del proyecto que está actualmente en desarrollo:

AjGenesis-0.4.3.zip

Quisiera en este artículo, comentar cómo funciona y se construye, uno de esos ejemplos. Permítanme primero repasar algo sobre el proyecto. Primero, "the big picture":

Está escrito en Visual Basic .NET 1.x, y es código abierto, pues viene con una licencia tipo BSD, que permite utilizarlo en cualquier proyecto que quieran. Se puede usar como librería, invocado desde nuestro proyecto, se puede invocar desde la línea de comando, o puede ser utilizado desde el poderoso NAnt (esto último me permitió organizar mejor las tareas que realiza el generador de código).

En la página del proyecto (y en el proyecto mismo) hay varios ejemplos, que generan código desde un modelo, usando plantillas. Los ejemplos generan código PHP, Java, JSP, VB.NET, C#, ASP.NET, y hasta scripts de base de datos y procedimientos almacenados. Quisiera destacar dos puntos:


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


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


Esto lo diferencia de otros generadores. Podemos construirnos nuestro propio modelo, y sus propias plantillas, para generar los artefactos de texto que prefiera. Otros sistemas parten de la base de datos, y sólo generan un grupo de artefactos de textos predefinidos (por ejemplo, POJOs, plain old java objects, o DAOs, Data Access Objects). Pero con AjGenesis puede generar el artefacto de texto que se nos ocurra.

Para comprender mejor que el modelo se puede construir y consumir como uno lo disponga, ver un artículo anterior:

Generando Código- Hello World con AjGenesis

Ahí se describen los pasos iniciales, y un modelo que es totalmente libre.

Creando una aplicación


Encaremos hoy algo más completo. Necesitamos armar una solución sencilla, con dos tablas, sobre una base de datos SQL Server, con código VB.NET 2.0, con interface web, capa de servicios, capa de datos, entidades y componentes de negocio a la Microsoft. Queremos generar la solución, los proyectos, los scripts de creación de la base, y procedimientos almacenados. Este ejemplo está incluido en los ejemplos AjGenesisExamples3.zip. Primer paso: escribir el modelo.

El proyecto

En un directorio de proyectos de los ejemplos que acompañan este artículo, hay un directorio Projects/AjFirstExample.

 

En ese directorio está el archivo Project.xml que contiene el modelo.

 

<Project>
  <Name>AjFirstExample</Name>
  <Description>First Example using AjGenesis</Description>
  <Prefix>AjFE</Prefix>
  <Domain>com.ajlopez</Domain>
  <CompanyName>ajlopez</CompanyName>
  <Model>
    <Entities>
      <Entity Source="Entities/Customer.xml"/>
      <Entity Source="Entities/Supplier.xml"/>
    </Entities>
  </Model>
</Project>

 

Recordemos: el modelo es libre. Acá definimos, para los templates que vamos a usar, las entidades de nuestro modelo: customers y suppliers.

Las entidades

Para que un archivo XML no resulte terriblemente largo, AjGenesis permite que cualquier nodo del modelo se especifique en un archivo aparte. Es un criterio que he usado para definir cómo se escribe el modelo: el XML resultante no debe herir a la vista, debe ser entendible y abarcable en una lectura.

En el Project.xml, eso aparece en el caso de las entidades, con el atributo Source. Examinemos una entidad, escrita en Entities/Customer.xml:


<Entity>
  <Name>Customer</Name>
  <Description>Customer Entity</Description>
  <SetName>Customers</SetName>
  <Descriptor>Customer</Descriptor>
  <SetDescriptor>Customers</SetDescriptor>
  <SqlTable>customers</SqlTable>
  <Properties>
    <Property>
      <Name>Id</Name>
      <Type>Id</Type>
    </Property>
    <Property>
      <Name>Name</Name>
      <Type>Text</Type>
      <SqlType>varchar(200)</SqlType>
    </Property>
    <Property>
      <Name>Address</Name>
      <Type>Text</Type>
      <SqlType>text</SqlType>
    </Property>
    <Property>
      <Name>Notes</Name>
      <Type>Text</Type>
      <SqlType>text</SqlType>
    </Property>
  </Properties>
</Entity>


Hay atributos de la entidad, como su nombre y descripción, en singular y plural, que sirve para nombrarlas en las páginas resultantes, o dentro del código. Las propiedades son los campos a mantener en cada entidad. Vemos que en este ejemplo, no hay más que datos dentro de una entidad.

Aparte de las entidades, en otro directorio, Technologies, se especifica el modelo dependiente de la tecnología, como VbNet2:

<Technology>
  <Programming>
    <Dialect>VbNet2</Dialect>
  </Programming>
  <Database>
    <Dialect>MsSql</Dialect>
    <Name>AjFirstExample</Name>
    <Username>sa</Username>
    <Prefix>ajfe_</Prefix>
    <Host>(local)</Host>
  </Database>
</Technology>

Las plantillas

En el directorio Templates/VbNet2 encontramos

Son las plantillas para generación de código VB.Net 2.0. También encontraremos plantillas para C# 1.x y 2, Vb.NET 1, Java. Hay plantillas para usar Nhibernate, Hibernate, JSP, MySql, y conceptos de Domain-Driven Design. Todo desde el mismo modelo. Tomemos como muestra una plantilla, la que genera la entidad en Visual Basic, EntityVb.tpl:


<#
message "Generating Entity ${Entity.Name}"
include "Templates/VbNet2/VbFunctions.tpl"
include "Templates/VbNet2/Prologue.tpl"
#>
'
' Project ${Project.Name}
' ${Project.Description}
' Entity ${Entity.Name}
' ${Entity.Description}
'
'
Public Class ${Entity.Name}

' Private Fields

<# for each Property in Entity.Properties
  message "Procesando Campo ${Property.Name}"
#>
Private m${Property.Name} as ${VbType(Property)}
<#
  end for
#>
' Default Constructor

Public Sub New()
End Sub


' Public Properties

<#
  for each Property in Entity.Properties
    message "Procesando Propiedad ${Property.Name}"
#>
Public Property ${Property.Name}() as ${VbType(Property)}
  Get
    Return m${Property.Name}
  End Get
  Set(ByVal Value As ${VbType(Property)})
    m${Property.Name} = Value
  End Set
End Property
<#
end for
#>

End Class

Como antes, se usan estructuras de control, y recorrido de una entidad del modelo. No se maneja el XML. El formato XML es la forma de serialización del modelo. Durante el proceso de la plantilla, el modelo ya está en memoria, accesible desde variables dinámicas.

Los pasos

Ahora tenemos más archivos a generar: desde las páginas ASPX, y su código asociado, los proyectos de fachada de servicio, entidades, acceso a datos, el archivo de solución, y más. Para automatizar esta generación, el ejemplo tiene varios archivos de tareas, en el directorio Tasks, donde se describen los pasos a ejecutar. Hay dos grandes tareas: los pasos a ejecutar independientemente de la tecnología elegida, como completar el modelo, revisarlo, y las dependientes de la tecnología, como generar tal archivo JSP o ASPX, dependiendo de si queremos Java o .NET.

La tarea de completar el modelo está a cargo de Tasks\BuildProject.ajg, que comienza con:

'
' Build Project
' Complete the Project Data
' Project must be loaded in global variable Project
'

PrintLine "Completing Project ${Project.Name}"

include "Templates/EntityFunctions.tpl"
include "Templates/Utilities.tpl"

if not Project.Title then
  Project.Title = Project.Name
end if

if not Project.Version then
  Project.Version = "1.0.*"
end if

if not Project.SystemName then
  Project.SystemName = Project.Name
end if

Acá incluye algunas funciones auxiliares, y luego comienza a completar el modelo que reside en la variable Project. Ejemplo: si falta Project.Title le coloca como título el Project.Name. Prosigue:

 

for each Entity in Project.Model.Entities
  PrintLine "Entity " + Entity.Name

  for each Property in Entity.Properties
    PrintLine "Property " & Property.Name


   
if not Property.Description then
      Property.Description = Property.Name
    end if


    if not Property.Title then
      Property.Title = Property.Description
    end if


    if Property.Type="Id" and not Property.SqlType then
      Property.SqlType="int"
    end if


    if Property.SqlType and not Property.SqlColumn then
      Property.SqlColumn = Property.Name
    end if


    if Property.Type="Id" and not Entity.IdProperty then
      Entity.IdProperty = Property
    end if


    if Property.Reference then

...

Mas adelante, se ejecutan las tareas de tecnología. Como ejemplo, veamos un fragmento de Tasks\BuildVbNet2.ajg:

<#
include "Templates/Utilities.tpl"
include "Templates/VbNet2/UtilitiesVb.tpl"

message "Creating Directories..."

FileManager.CreateDirectory(Project.BuildDir)
FileManager.CreateDirectory("${Project.BuildDir}/Sql")
FileManager.CreateDirectory("${Project.BuildDir}/Src/${Project.Name}.Entities")
FileManager.CreateDirectory("${Project.BuildDir}/Src/${Project.Name}.Entities/My Project")
FileManager.CreateDirectory("${Project.BuildDir}/Src/${Project.Name}.Data")
FileManager.CreateDirectory("${Project.BuildDir}/Src/${Project.Name}.Data/My Project")
FileManager.CreateDirectory("${Project.BuildDir}/Src/${Project.Name}.Services")

En este fragmento, se crean los directorios necesarios para albergar la solución. El nombre del directorio se extrae del modelo desde ${Project.BuildDir}.

Generando la solución

Podríamos lanzar las tareas desde la línea de comando, pero tenemos un build armado para el Nant, uno para cada tecnología. Ejecutamos las tareas build, buildsql, y deploysql de AjFirstExampleVbNet2.build:

 


En el directorio Build/AjFirstExample/VbNet2/Sql quedan los scripts de creación de la base y procedimientos almacenados. Y en el directorio hermano Src, sorpresa, tenemos la solución ya preparada:

Encontramos varios proyectos armados. Si levantamos la solución en el Visual Studio 2005, aparece todo el código generado:



Con otro archive build AjFirstExampleCSharp2.build, generamos la misma solución en CSharp:


Encontrarán otros proyectos y ejemplos de .build, que usan NHibernate, Hibernate, JSP, y conceptos de DDD.

Reflexiones

Claro, no todo se puede generar automáticamente. Es importante tener siempre presente esto. Pero en el día a día, reconozcamos que tenemos cantidad de texto repetitivo, tareas que bien podemos encargar al propio software.

Recordemos siempre: el modelo es libre. Los ejemplos presentados son solamente ejemplos: podemos general el modelo que queremos, y escribir las plantillas que necesitamos. Es importante escribir las plantillas de forma que el código generado sea similar al que hubiéramos generado nosotros. Si no nos sentimos cómodos con el código generado, si no tiene nuestro estilo, nuestra experiencia, terminamos generando algo que no entendemos.

Otra reflexión: el modelo debe ser independiente de la tecnología. En el ejemplo final, hemos visto cómo, desde el mismo modelo, se puede generar la solución para VB.NET2, y para CSharp. Encontrarán las plantillas para generarla con Nhibernate, con DDD, con Hibernate, con Java y JSP, y podemos escribir la que necesitamos.

El software debe ayudarnos a generar software. Nuestra experiencia cuenta: lo que aprendimos de hacer aplicaciones, podemos volcarlo en esta especie de sistema experto, generador de código. Justamente, en el futuro, espero poder incorporar al proyecto, en las plantillas, más toma de decisiones: así como volcamos nuestra experiencia en escritura de aplicaciones, podemos incorporar nuestro conocimiento acumulado sobre patrones, arquitectura, estilos de programación.

Y al ser de código abierto, AjGenesis permite que lo extendamos, a nuestro gusto y necesidad.

Se aceptan sugerencias, historias de uso. Pueden escribir comentarios a este artículo, o escribirme. Desde ya, muchas gracias por cualquier "feedback".

Nos leemos!

Angel "Java" Lopez
http://www.ajlopez.com/

Published Mon, Jun 25 2007 9:20 by lopez

Comments

# re: Generando aplicaciones con AjGenesis@ Tuesday, July 03, 2007 11:53 AM

Angel, generé el codigo pero al compilar en este modulo no encuentra a AjFramework y los objetos DataParameter, DataService, ... no los reconoce.

¿De donde vinculo AjFramework al proyecto?

'

' File generated using AjGenesis

' www.ajlopez.com/ajgenesis

' www.ajlopez.net/ajgenesis

' Open Source Code Generation Engine

'

'

' Project AjFirstExample

' First Example using AjGenesis

' Entity Customer

' Customer Entity

'

'

Imports System.Collections.Generic

Imports AjFramework.Data

Imports AjFirstExample.Entities

Public Class CustomerData

Public Sub Insert(entity as Customer)

       Dim dpid As New DataParameter

dpid.Value = entity.Id

DataService.ExecuteNonQuery("CustomerInsert", CommandType.StoredProcedure, _

dpid, _

entity.Name, _

entity.Address, _

entity.Notes _

)

entity.Id = dpid.Value

End Sub

Public Sub Update(entity as Customer)

DataService.ExecuteNonQuery("CustomerUpdate", CommandType.StoredProcedure, _

entity.Id, _

entity.Name, _

entity.Address, _

entity.Notes _

)

End Sub

Public Sub Delete(id as Integer)

DataService.ExecuteNonQuery("CustomerDelete", CommandType.StoredProcedure, id)

End Sub

Public Function GetById(id as Integer) as Customer

Dim reader as IDataReader = Nothing

try

reader = DataService.ExecuteReader("CustomerGetById", CommandType.StoredProcedure, id)

if not reader.Read() then

return Nothing

end if

Dim entity as Customer

entity = Make(reader)

return entity

finally

reader.Close()

end try

End Function

Public Function GetAll() as List(of Customer)

Dim reader as IDataReader

Dim list as new List(of Customer)()

reader = DataService.ExecuteReader("CustomerGetAll", CommandType.StoredProcedure )

Dim entity as Customer

while reader.Read()

entity = Make(reader)

list.Add(entity)

end while

reader.Close()

return list

End Function

Public Function GetAllAsDs() as DataSet

return DataService.ExecuteDataSet("CustomerGetAll", CommandType.StoredProcedure )

End Function

Private Function Make(reader as IDataReader) as Customer

Dim entity as new Customer

if reader("Id") is System.DbNull.Value then

entity.Id = 0

else

entity.Id = CType(reader("Id"),Integer)

end if

if reader("Name") is System.DbNull.Value then

entity.Name = Nothing

else

entity.Name = CType(reader("Name"),String)

end if

if reader("Address") is System.DbNull.Value then

entity.Address = Nothing

else

entity.Address = CType(reader("Address"),String)

end if

if reader("Notes") is System.DbNull.Value then

entity.Notes = Nothing

else

entity.Notes = CType(reader("Notes"),String)

end if

return entity

End Function

End Class

Alejandro Gianazza

# re: Generando aplicaciones con AjGenesis@ Friday, July 13, 2007 7:52 PM

Angel:

Mil gracias por darnos un poco de vos.

Saludos cordiales.

Alejandro Nelis

alejandro nelis

# Recursos sobre Hibernate@ Friday, July 20, 2007 5:43 AM

En varios de los ejemplos que vemos en mis cursos de Java, terminamos usando Hibernate , la librería

Angel "Java" Lopez

# Generando código para NHibernate (Parte 1)@ Sunday, August 05, 2007 8:18 AM

Ya en algún &quot;post&quot; anterior, Generando aplicaciones con AjGenesis comenté sobre cómo el proyecto

Angel "Java" Lopez

# Generando aplicaciones con AjGenesis at Espacio de Dario Quintana@ Wednesday, August 08, 2007 11:06 AM

Pingback from  Generando aplicaciones con AjGenesis at Espacio de Dario Quintana

Generando aplicaciones con AjGenesis at Espacio de Dario Quintana

# re: Generando aplicaciones con AjGenesis@ Tuesday, September 04, 2007 9:34 PM

A ver... para vos los archivos de configuraciones de Spring son un infierno... y los tuyos??? ademas no te compares con una BESTIA como Spring, lo unico q haces ahi es manipular texto...

"vieron la luz" ...

Por favor. Solo en Argentina señoras y señores

Pablo

# re: Generando aplicaciones con AjGenesis@ Wednesday, September 05, 2007 3:58 AM

Hola gente!

Para Alejandro y otros que tengan ese problema: Tendría que incluir el ajframework dentro de los ejemplos. Es una dll y un código abierto, que uso como prueba de concepto, podrían usar Enterprise Library, si quisieran. Pueden obtener esa dll del ejemplo:

www.ajlopez.com/.../CodeDotNetArch.zip

El código de ese utilitario está en

www.ajlopez.com/.../AjFramework-0.1.zip

Nos leemos!

Angel "Java" Lopez

lopez

# re: Generando aplicaciones con AjGenesis@ Wednesday, September 05, 2007 4:02 AM

Para Pablo:

Ciertamente Spring es grandioso. Sigo las ideas de Rod Johnson desde antes de Spring, cuando tenía su propio framework en interface21.

Pero no es la idea de AjGenesis renegar de frameworks. Al contrario: adoptarlos, si se necesitan, o cambiar de framework, plataforma, tecnología, o lo que sea, a partir de un modelo libre.

Jeje... lo de "ver la luz" viene de las intentos de chistes, que hago en mis cursos. Una de las primeras apariciones de la frase fue hace años en:

msmvps.com/.../evangelizando-net.aspx

Nos leemos!

Angel "Java" Lopez

lopez

# re: Generando aplicaciones con AjGenesis@ Wednesday, November 07, 2007 4:24 AM

Es verdad lo unico que haces es manipular texto,

Como los poetas...

Muchas gracias por compartir tu trabajo con nosotros.

Saludos

Eduardo

# Preservando código en AjGenesis@ Sunday, November 11, 2007 2:21 AM

Uno de los temas a encarar cuando uno usa un generador de código, o de artefactos de texto en general

Angel "Java" Lopez

# Reunión de Generación de Sistemas con AjGenesis@ Sunday, November 18, 2007 5:04 PM

Ya había anunciado que el viernes pasado había desayuno de arquitectura , en el Microsoft User Group

Angel "Java" Lopez

# Desayuno de arquitectura con AjGenesis@ Sunday, November 18, 2007 5:06 PM

Gracias a la organización del Microsoft User Group de Argentina , habrá una reunión gratuita de arquitectura

Angel "Java" Lopez

# Sobre la generación de código@ Sunday, November 18, 2007 5:07 PM

Quisiera hoy tratar un tema, que de alguna manera lo trato en cada momento en que tengo oportunidad.

Angel "Java" Lopez

# Code Generation as a Service@ Friday, April 04, 2008 9:58 AM

Despues de un tiempo, vuelvo a escribir sobre mi proyecto preferido AjGenesis , un generador de código

Angel "Java" Lopez

# re: Generando aplicaciones con AjGenesis@ Thursday, July 24, 2008 10:31 AM

Angel, todo esta esta genial, decime es posible mediante AjGenesis generar una aplicaion(NHibernate) pero a partir de un esquema de dataset?

Luis

# re: Generando aplicaciones con AjGenesis@ Friday, August 07, 2009 7:08 AM

Para Oracle y C# 3.0 hay algo ? salu2

Espinete

Leave a Comment

(required) 
(required) 
(optional)
(required)