April 2011 - Posts
Próximo post
Anterior post
Implementé algunos ejemplos simples usando DLR. Mostré cómo crear y evaluar expresiones. Cada Expression tiene algunas propiedades y métodos expuestos. Algunas de las más importantes:

Noten que cada objeto Expression tiene un NodeType, y Type separado. Exploremos esos valores:
BinaryExpression expr = Expression.Add(Expression.Constant(10),
Expression.Constant(20));
Console.WriteLine(expr.GetType().FullName);
Console.WriteLine(expr.Type);
Console.WriteLine(expr.Left.Type);
Console.WriteLine(expr.Right.Type);
Console.WriteLine(expr.NodeType);
Console.WriteLine(expr.Left.NodeType);
Console.WriteLine(expr.Right.NodeType);
Veamos Expression.Add. Es una expresión binaria. Cada una de esas expresiones tiene dos ramas:

Obtengo la salida:

Noten que el .GetType() (el tipo .NET) de la Expression creada, es SimpleBinaryExpression: no es sólo un objeto Expression. Tampoco hay un AddExpression como clase en DLR. El expr.NodeType indica que la expresión creada es un Add. Los creadores de DLR decidieron no tener una clase dedicada para los Expression.Add.
Un BinaryExpression tiene Type: System.Int32, un entero de 32 bits. Este es el tipo que indica qué clase de valor retornará esta expresión. Se deriva de la acción de la expresión binaria (sumar) y los tipos de sus ramas izquierda y derecha.
En C#, podemos combinar expresiones binarias:
// double result = (3d + 2d) / 7d;
// Console.WriteLine(result);
BinaryExpression expr2 = Expression.Divide(
Expression.Add(Expression.Constant(3d),
Expression.Constant(2d)),
Expression.Constant(7d)
);
Func<double> binaryDelegate = Expression.Lambda<Func<double>>(expr2).Compile();
Console.WriteLine(binaryDelegate.Invoke());
Principal fuente consultada para este ejemplo: “Pro DLR in .NET 4” de Chaur Wu. El código de esta serie de post se mantiene en http://code.google.com/p/ajcodekatas/ en el directorio trunk/Dlr.
Próximos pasos: expresiones IfThenElse, expresiones switch, qué es un visitor, y qué es Reduce.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Próximo Post
Anterior Post
En mi anterior post escribí un simple programa “Hello, world”. Esta vez, quiero comenzar a explorar las Expression de DLR. El código de esta serie de posts se mantiene en
http://code.google.com/p/ajcodekatas/ en el directorio trunk/Dlr.
Primero, una Expression en DLR no es una System.Linq.Expression (uno de mis primeros errores, en este ejemplo, fue usar la version Expression de Linq, en vez de la de DLR). DLR tiene su propia implementación de Expression, con más metodos. Esta implementación está en el namespace Microsoft.Scripting.Ast. AST viene de Abstract Syntax Tree: cada expresión, en DLR como en Linq, es una estructura de datos en memoria, que representa algo como 4, 5+a, llamadas a métodos, etc…
La clase Expression de DLR class tiene métodos estáticos para crear objetos Expression. Por ejemplo
var expr = Expression.Constant(“I’m a string”);
es la forma de crear una expresión constante. Pero necesitamos no solamente constantes, sino también variables:
var expr = Expression.Variable(typeof(string), “foo”);
Este comando crea una expresión variable, que representa una variable con nombre “foo” y tipo string.
¿Cómo invocar una expresión? Recordemos, los objetos Expression son árboles, datos en memoria, no código compilado. La magia de DLR (y de Linq también) es ser capaz de COMPILAR la expresión a un delegado, y entonces, poder invocarlo:
expr.Compile().Invoke(); // you can use Invoke with parameters, too
(Generalmente en mis intérpretes, no llego a compilar, recorro y ejecuto las acciones de los nodos del árbol de comandos y expresiones)
Entonces, para reproducir el “Hello, World”, escribí este código de ejemplo:
// Console.WriteLine("Hello, Method!");
MethodInfo method = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
Expression callExpression = Expression.Call(method, Expression.Constant("Hello Method!"));
Expression.Lambda<Action>(callExpression).Compile().Invoke();
Precedí el código con un comentario que contiene el código C# equivalente.
Nota: necesitamos un Expression.Lambda (en este caso, un lambda Action) para crear un delegado alrededor de una expresión. Una acción es algo para invocar, sin retornar un valor (una rutina, en vez de una función). Un Action<string> es una rutina que recibe un parámetro string. Un Action sin nada más, no tendría parámetros.
¿Cómo usamos una variable y le asignamos un valor? Veamos un ejemplo:
// string x = "Hello, Assign!";
// Console.WriteLine(x);
ParameterExpression x = Expression.Variable(typeof(string), "x");
Expression blockExpression = Expression.Block(
new ParameterExpression[] { x },
Expression.Assign(x, Expression.Constant("Hello, Assign!")),
Expression.Call(method, x));
Expression.Lambda<Action>(blockExpression).Compile().Invoke();
Noten que el Expression.Block recibe un enumerable de ParameterExpressions, y luego un param array (una cantidad variable de argumentos) con Expressions que constituyen el cuerpo del bloque que vamos a ejecutar.
En el próximo ejemplo, implementé un código C# (descripto en el comentario, pero código inválido), que luego implemento en DLR: un condicional que retorna un valor:
// x = "Hello";
// if (true)
// x.ToLower();
MethodInfo toLowerMethod = typeof(string).GetMethod("ToLower", new Type[] {});
ParameterExpression y = Expression.Variable(typeof(string), "y");
ParameterExpression z = Expression.Variable(typeof(string), "z");
Expression blockExpression2 = Expression.Block(
new ParameterExpression[] { x, y},
Expression.Assign(x, Expression.Constant("Hello")),
Expression.Condition(Expression.Constant(true),
Expression.Call(x, toLowerMethod),
Expression.Default(typeof(string)),
typeof(string))
);
string result = Expression.Lambda<Func<string>>(blockExpression2).Compile().Invoke();
Console.WriteLine(result);
La primera expresión en el bloque es la asignación de la constante “Hello” a la variable x. Noten que cualquier variable es inyectada como parámetro en un bloque. Esta es la forma de declarar variables en bloques de expresiones DLR. Hay una Expression.Condition que toma como parámetros a una expresión booleana, una Expression que se evalúa si la condición es verdadera, una Expression que se evalúa si la condición es falsa, y el tipo del valor a retorna. Este tipo de condición es similar al operador ternario ?: de C#.
Principal fuente consultada para este ejemplo: “Pro DLR in .NET 4” de Chaur Wu.
Próximos paso: compilar expresiones más complejas, expresiones binarias, expresiones IfThenElse, y más.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Anterior post
Siguiente post
En los anteriores posts (Tabla por Jerarquía, Tabla por Clase Concreta), implementé dos estrategias de mapeo sobre el mismo modelo:
Ahora, quiero mapear CADA tabla a una tabla:
La solución tiene dos proyectos, uno del proyecto de consola, y el otro es una librería de clase:
Pueden bajar el código desde NHibernate3ItemsTablePerClass.zip. Necesitan agregar las librerías NHibernate (ver post anterior para más detalles). La base de datos puede crearse ejecutando ExecuteAll.cmd en el directorio Sql. El comando acepta un parámetro (como ExecuteAll (local)) que indique el servidor de base de datos. El valor asumido es .\SQLEXPRESS.
El mapeo de Item:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ItemsTablePerClass.Domain"
namespace="ItemsTablePerClass.Domain">
<class name="Item" table="Items">
<id name="Id">
<generator class="guid.comb" />
</id>
<property name="Title" not-null="true" />
<property name="Description" not-null="true" />
</class>
</hibernate-mapping>
Hay un atributo table, y ya no hay valor de discrimiator. Lo nuevo está en el mapeo de Note:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ItemsTablePerClass.Domain"
namespace="ItemsTablePerClass.Domain">
<joined-subclass name="Note" extends="Item" table="Notes">
<key column="Id" />
<property name="Content" />
</joined-subclass>
</hibernate-mapping>
El nuevo elemento es joined-subclass. Hay un atributo de table para este mapeo, también. Y no hay elemento id. Fue reemplazado por el elemento key que uno ambas tablas.
Cambios similares en el mapeo de Page:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ItemsTablePerClass.Domain"
namespace="ItemsTablePerClass.Domain">
<joined-subclass name="Page" extends="Item" table="Pages">
<key column="Id" />
<property name="Url"/>
</joined-subclass>
</hibernate-mapping>
Notablemente, el código principa es el mismo que en los anteriores ejemplos. Y las clases del dominio siguen sin cambios.
Los próximos pasos: comenzar a investigar mapeos uno-a-varios, otras alternativas de mapeo (como Fluent NHibernate, ConfORM, y la nueva API de NHibernate 3.2).
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Anterior Post
Siguiente Post
Quiero usar el mismo dominio que en mi anterior post:

but this time, I want two tables: one for Notes, another for Pages:

La solución tiene dos proyectos: una librería de clases conteniendo el dominio, y un proyecto de consola:

El código está en mi AjCodeKatas Google code project, en el directorio trunk/NHibernate/ItemsTablePerConcreteClass. El código no incluye las librerías de NHibernate, hay que agregarlas a las referencias de la solución. Como en el ejemplo previo, agregué como referencias al proyecto de consola: NHibernate.dll del directorio de NHibernate llamado Required_Bins y NHibernate.ByteCode.Castle del directorio Required_For_LazyLoading\Castle. Agregué también los archivos .xsd (los esquemas XML para soportar el manejo de intellisense cuando se escriben los archivos de mapeo) desde el directorio de NHibernate llamado Required_Bins.
Pueden bajar el código fuente directamente desde NHibernate3ItemsTablePerConcreteClass.zip, si no quieren bajarlo con un cliente de SVN desde el proyecto de Google Code. Si necesitan las librerías de NHibernate 3.x, puede visitar http://nhforge.org o bajar las que utilicé en mi primer ejemplo NHibernate3SimpleMapping.zip, ahí están incluidas. Pueden ver mi primer post para otros detalles (por ejemplo, no olviden poner los archivos de mapeao como recursos embebidos, si van a construir su propia solución).
Noten que las clases del dominio siguen siendo las mismas: no cambiaron desde el anterior ejemplo. Lo que cambia es la forma de mapeo, los archivos de mapeo, no el código. Esta es una prueba de la independencia del dominio de nuestra estrategia de mapeo.
Este es el Item.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ItemsTablePerConcreteClass.Domain"
namespace="ItemsTablePerConcreteClass.Domain">
<class name="Item" abstract="true">
<id name="Id">
<generator class="guid.comb" />
</id>
<property name="Title" not-null="true" />
<property name="Description" not-null="true" />
</class>
</hibernate-mapping>
Primero removí la columna discriminator. No hay tal columna, porque no hay ahora un tabla de Items. Agregué el atributp table. I added the attribute abstract=”true”. Si olvidan agregarlo, NHibernate “piensa” que tienen una tabla concreta para guardar los objetos de esta clase. Pueden removerla, si tiene Items que no son Notas ni Páginas. No es el caso de este ejemplo que quiero implementar: los únicos items “reales” son Notas o Páginas. Item es una abstracción del dominio.
El Note.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ItemsTablePerConcreteClass.Domain"
namespace="ItemsTablePerConcreteClass.Domain">
<union-subclass name="Note" extends="Item" table="Notes">
<property name="Content" />
</union-subclass>
</hibernate-mapping>
Agregué el atributo table=”Notes”. Y el nuevo elemento que se usa es union-subclass, en vez de subclass como en el anterior ejemplo. Este nuevo elemento “dice”: “la tabla de esta clase CONTIENE las columnas a grabar con los valores de la clase padre”. Esto es: las columnas de la tabla son la unión de las propiedades de la clase padre y de esta clase.
Cambios similares en Page.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ItemsTablePerConcreteClass.Domain"
namespace="ItemsTablePerConcreteClass.Domain">
<union-subclass name="Page" extends="Item" table="Pages">
<property name="Url"/>
</union-subclass>
</hibernate-mapping>
El código principal es prácticamente el mismo que en el anterior ejemplo:
ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
using (ISession session = sessionFactory.OpenSession())
{
using (ITransaction tx = session.BeginTransaction())
{
Page page1 = new Page();
page1.Title = "Technical Blob";
page1.Description = "My Personal Technical Blob in English";
page1.Url = "http://ajlopez.wordpress.com";
Page page2 = new Page();
page2.Title = "My Personal Blob";
page2.Description = "My Personal Blob in Spanish";
page2.Url = "http://ajlopez.zoomblog.com";
Note note1 = new Note();
note1.Title = "To Do";
note1.Description = "My To Do List";
note1.Content = "Practice NHibernate";
session.Save(page1);
session.Save(page2);
session.Save(note1);
var items = session.Query<Item>(); ;
foreach (Item item in items)
System.Console.WriteLine(string.Format("Item {0}", item.Title));
tx.Commit();
session.Close();
}
}
excepto que esta vez, comencé a usar Session.Query<T>, un nuevo método de extensión para las sesiones de NHibernate, que retorna un IQueryable<T> que soporta Linq contra NHibernate y la base de datos de persistencia. Para usar ese método HAY QUE AGREGAR:
using NHibernate.Linq;
sino, no aparecerá como disponible. Recuerden: es un método de extensión, no es un método “natural” de ISession.
Mis fuentes de información:
El libro: Jason Dentler’s NHibernate 3.0 Cookbook
http://www.nhforge.org/doc/nh/en/index.html#mapping-declaration-unionsubclass
http://www.nhforge.org/doc/nh/en/index.html#inheritance
Próximos pasos: implementar Tabla por Clase, explorar relaciones uno-a-varios, colecciones “lazy”, etc…
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Hace un poco más de un año escribí:
Yo y mi tía Carlota
Gracias a todos los que comentaron (debe ser uno de los posts de este blog que más comentarios generó). Pero luego de meses de haber escrito ese texto, me ha servido para comprobar más o menos lo que ahí afirmaba: prácticamente nadie de los desarrolladores con los que trabajo cada semana (el año pasado y éste), ha leído, ha comentado, o me ha comentado personalmente nada sobre ese post. Es como si no hubiera existido.
Igualmente, habrán visto que mi conducta de post y trabajo no ha cambiado: solamente quería conseguir evidencia sobre lo que sospechaba: mis posts los leen gente con la que no trabajo directamente. Me gusta escribir post y código: es forma de compartir lo que uno va descubriendo, estudiando y explorando. Lo que no consigo escribir, igual queda agregado en mi delicious:
http://delicious.com/ajlopez
y también en enlaces que comparto en Twitter.
Escribir código y post sobre un tema, es para mí, un entregable: lo que pone de manifiesto, da evidencia de lo que uno investiga y estudia. Si yo estudiara el tema X y no lo comentara, sentiría como que falta algo en esa actividad de estudio: uno no estudia para uno, estudia para compartir con los demás. Cada uno de nosotros es limitado: en tiempo, en recursos, en vida. El compartir permite hacer “leverage”, apalancamiento de cada tema interesante. Y compartir mediante código y posts, permite que dejar algo que está disponible 24 horas, siete días a la semana. Desde hace un año más o menos, también he dejado de dar cursos presenciales: no me parecen que dejen nada permanente, se llega a pocas personas, no en el mejor ambiente o disposición, apenas si queda entregable compartible con los que no pudieron asistir, etc.
Pero también quisiera fomentar que otros también publiquen código, posts, enlaces. Hay muchos y muy buenos ejemplos de esa actitud. Vamos! No cuesta mucho. Basta con escribir sobre algo que conozcan, o que comienzan a estudiar, los enlaces que consultaron, los libros que les parecieron buenos.
Mientras, yo sigo por acá, siempre disponible, para mi tía Carlota! ;-)
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Anterior post
Siguiente post
En mi anterior post de esta serie, implementé un mapeo simple: una clase, una tabla.
Ahora, tengo tres clases, usando herencia:
El Id es un System.Guid. Quiero diseñar una colección de items. Cada item puede ser una Nota (Note), con contenido de texto, o una página Web (Page) con una URL asociada. La solución que preparé tiene dos proyectos:
Estoy manteniendo este código en mi AjCodeKatas Google code project, en el directorio trunk/NHibernate/ItemsTablePerHierarchyMapping. El código no incluye las librerías de NHibernate, así que tuve que agregar referencias a la solución. Agregué como referencias al proyecto de consola: NHibernate.dll desde el directorio de NHibernate llamado Required_Bins, y la dll NHibernate.ByteCode.Castle del directorio de NHibernate llamado Required_For_LazyLoading\Castle.
Si son perezosos como yo, y quieren tener la solución ahora, pueden bajar mi anterior ejemplo que contiene las librerías de NHibernate que necesitamos para el presentet: NHibernate3SimpleMapping.zip. El código de este post está en: NHibernate3ItemsTablePerHierarchy.zip.
El proyecto Domain no tiene referencia a las librerías de NHibernate. TOdo el mapeo está en el proyecto de consola. Los archivos .hbm está ahí agregados como recursos embebidos (no se olviden de agregarlos así en sus proyectos; de la forma que planteo la configuración de NHibernate (ver más abajo) esta librería busca archivos hbm en el assembly que le indiquemos; no es la única forma de indicarle los mapeos: espero en próximos posts explorar otras alternativas).
Tengo 3 clases para mapear. Pero quiero almacenar todos los datos de cualquier item en una sola tabla:
Pueden generar la base de datos vacía ejecutando el comando ExecuteAll.cmd que he dejado en el código de ejemplo.
La tabla Items tiene una columna ItemType, que indica si un item es una Note o una Page. El archivo de mapeo Item.hbm.xml referencia esa columna como un discriminador:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ItemsTablePerHierarchyMapping.Domain"
namespace="ItemsTablePerHierarchyMapping.Domain">
<class name="Item" table="Items">
<id name="Id">
<generator class="guid.comb" />
</id>
<discriminator column="ItemType" />
<property name="Title" not-null="true" />
<property name="Description" not-null="true" />
</class>
</hibernate-mapping>
Noten que no tengo esa columna reflejada en una propiedad de las clases: el discriminador lo usa el NHibernate, no lo necesitamos nosotros en nuestro dominio.
Bueno, ahora necesito mapear los objetos Note. ¿Cómo escribir ese mapeo? Vean el Note.hbm.xml:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ItemsTablePerHierarchyMapping.Domain"
namespace="ItemsTablePerHierarchyMapping.Domain">
<subclass name="Note" extends="Item" discriminator-value="Note">
<property name="Content" />
</subclass>
</hibernate-mapping>
El nuevo elemento XML que encontramos es subclass. Tiene un atributo extends que referencia a la clase padre. El atributo discriminator-value tiene el valor a poner en la columna discriminadora cuando el Item es una Note. Si no especificamos el valor de discriminador, el nombre de la clase sería usada como valor asumido, en este caso ItemsTablePerHierarchyMappin.Domain.Note.
El mapeo de Page.hbm.xml es similar:
<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"
assembly="ItemsTablePerHierarchyMapping.Domain"
namespace="ItemsTablePerHierarchyMapping.Domain">
<subclass name="Page" extends="Item" discriminator-value="Page">
<property name="Url"/>
</subclass>
</hibernate-mapping>
Podemos ejecutar este código de consola:
ISessionFactory sessionFactory = new Configuration().Configure().BuildSessionFactory();
using (ISession session = sessionFactory.OpenSession())
{
using (ITransaction tx = session.BeginTransaction())
{
Page page1 = new Page();
page1.Title = "Technical Blob";
page1.Description = "My Personal Technical Blob in English";
page1.Url = "http://ajlopez.wordpress.com";
Page page2 = new Page();
page2.Title = "My Personal Blob";
page2.Description = "My Personal Blob in Spanish";
page2.Url = "http://ajlopez.zoomblog.com";
Note note1 = new Note();
note1.Title = "To Do";
note1.Description = "My To Do List";
note1.Content = "Practice NHibernate";
session.Save(page1);
session.Save(page2);
session.Save(note1);
IQuery query = session.CreateQuery("from Item");
foreach (Item item in query.List<Item>())
System.Console.WriteLine(string.Format("Item {0}", item.Title));
tx.Commit();
session.Close();
}
}
System.Console.ReadKey();
para insertar y listar items.
Esta estrategia de mapeo (el viejo truco de grabar cada subclase en una misma table, usando discriminadores ;-) es llamada Table Per Hierarchy (Fowler la menciona como Single Table Inheritance). Acá la traduje como Tabla por Jerarquía.
Próximos pasos: implementar otras estrategias de mapeo: una tabla por clase concreta (en este caso, Pages y Notes); una tabla por clase (sería Items, Pages, Notes).
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez
Hoy, viernes 8 de Abril, comienzo mi primer semana sabática del año (abarca hasta el lunes 18 de Abril, inclusive). Desde 2009 que no tenía una semana dedicada a mis proyectos, estudios y desarrollos. Escribí un un post declarando mis compromisos no técnicos (mucho estudio) listando mis entregables esperados (posts).
Ahora, es tiempo de declarar mis actividades técnicas planeadas. Bien, esta es la lista de mis entregables (es más fácil describir los “outcomes” que el proceso, pasos, lecturas, diseño, etc…). Al final, lo que importa son los entregables:
- Escribir dos posts y código sobre NHibernate 3.x
- Escribir un post sobre mi trabajo usando AjScript (intérprete tipo Javascript escrito en C#)
- Escribir dos posts y código de mi serie Escribiendo una Aplicación usando TDD
- Escribir dos posts y código usando AjGenesis para cumplir con la Language Workbench Competition
- Escribir un post sobre AjLisp (primero de una serie)
- Escribir un post sobre implementar una máquina virtual tipo Smalltalk, usando AjTalk como caso de estudio (primero de una serie)
Como es usual, seguiré publicando un post diario (técnico o no técnico). Pero no todos los posts de arriba serán publicados durante esta semana sabática. Serán escritos durante esta semana sabática, y serán publicados algunos en estos días, el resto luego de esta semana.
Nos leemos!
Angel “Java” Lopez
http://www.ajlopez.com
http://twitter.com/ajlopez