jueves, mayo 25, 2006

MiguelMadero.com

Estrenando nueva direccion para el blog: miguelmadero.com esta ya disponible.
Un pequeño capricho para mi ego por sólo $9.90 dlls anuales. Dije si Martin Fowler y Ron Jacobs tienen lo propio tal como muchos otros, dije, bueno y porque no? Aprovechar antes de que algún homónimo me lo ganara.

Así que mi sugerencia para ustedes, si todavía no hay jaimevelazquez.com tomenlo antes de que les paso lo que a christianperez.com

jueves, mayo 11, 2006

Atributos para Tiempo de Diseño para Dispositivos Móviles

Está es la breve historia de cómo batallamos por la siguiente línea de código:
smartPartPlaceHolder.SmartPart = null;
Sin duda el diseñador de VS es una herramienta súper útil, pero en más de una ocasión (deberás no exagero, pero ahora sólo recuerdo esta), suele hacer cosas extrañas. Muchas veces no es por culpa de VS sino de quien programa los controles, pero al parecer en este caso fue un poco más allá y todo resulto ser problema de un
known bug que será atendido en el SP1 que será liberado el tercer cuarto de este año. En este caso el diseñador escribía la línea anterior cada vez que modificábamos la vista desde VS, por lo que dejaba de funcionar ya que esa propiedad no debía ser null.
Empecemos por quien diseña los controles. Con los atributos en tiempo de diseño tienes realmente mucha flexibilidad, le puedes especificar a una propiedad su valor de default, si quieres que se muestra en la lista de IntelliSense (no veo porque querríamos deshabilitarlo, pero está disponible) y lo más importante (al menos en este caso), le podemos decir si alguna propiedad deberá ser manejada por el diseñador y si no es una propiedad muy simple (por ejemplo un objeto, una Colección de Columnas e.g. DataGrid.Colums) muy seguramente querremos un diseñador personalizado.
El único detalle es que en Mobile no podemos usarlo, o al menos no todos. El CF (compact framework) no los incluye y no lo podemos compilar usando alguna clase del framework normal, sin embargo, VS si podría tomar Assemblys de ambos.
La solución más obvia, aunque no tan sencilla, era la que teníamos en 2003,
compilar un dll similar para el tiempo de diseño. Realmente no era tan sencillo, se resume en seis pasos:
Crear un proyecto de C# de Class Library (no de CF, para escritorio).
Agregamos como links todos los archivos del proyecto de Controles.
Agregamos un atributo de compilación al proyecto creado en el paso 1.
Ponemos en condionantes de compilación los atributos
#If NETDESIGNTIM
[System.ComponentModel.Browsable(false)
#endif
public object SmartPart
{
///
}
Este es un punto clave y no tan sencillo, necesitas agregar el siguiente atributo al AssemblyInfo.cs
#if NETCFDESIGNTIME
[assembly: System.CF.Design.RuntimeAssembly("ElDllQueTieneTodoEnRuntime ")]
#endif
Lo agregamos al GAC para que VS pueda reverenciarlo.

El atributo en el paso 5 le dice que es lo que deberá de usar en tiempo de ejecución, sin embargo, este dll de C# es lo que agregamos a la barra de herramientas y es con el que trabajaremos en VS.

Ok, es medio sencillo como para haber batallado tanto.
Ahora el nuevo modelo es más prometedor (Debería ser así de sencillo como en
este ejemplo). Que es lo que necesitamos otro dll, pero ahora se generará automáticamente por genasm.exe usando los atributos que se especifiquen en el archivo XML. Aunque XML es muy sencillo, su poder reside cuando no lo tenemos que escribir nosotros, es ahí donde entran los diagramas de clases. Desde el diagrama de clases le especificamos los atributos a cada propiedad y este genera el archivo que después es compilado por genasm.exe. Ver el ejemplo para más detalle.
Ok, pareció aún más sencillo, pero no termina aquí, genasm.exe tiene un
know bug aunque nos dan una solución de cómo darle la vuelta mientras lo corrigen, realmente está muy lejos de ser lo que queríamos. El proyecto sobre el cual lo apliques no puede usar Generics, así que tienes que mover tus clases a otros proyectos, heredar de clases generics para hacerlas específicas y usar estás en lugar de las generics y listo (esto último puede ser lo más difícil), esa es la solución que nos da Microsoft.
En este caso no podíamos hacer eso ya que todo el proyecto dependía muchísimo de generics, lo que tuvimos que hacer fue crear otro nuevo proyecto que contuviera exclusivamente lo que requeríamos, remover los pocos generics que quedaron dentro de esa clase y luego simplemente compilar. El único lugar donde el SmartPartPlaceHolder usaba generics era en el EventHandler<>, así que tuvimos que crear el delegado específico y usar ese en lugar del EventHandler genérico.
public delegate void SmartPartShownEventHandler(object o, SmartPartPlaceHolderEventArgs e);
public event SmartPartShownEventHandler SmartPartShown;
en lugar de
public event EventHandler SmartPartShown;
Hablo más sobre generics en
otro post.
Ok, ok, no queríamos tener otra clase con el mismo nombre (SmartPartPlaceHolder) que estuviera en otro nombre y luego cambiarla cuando corrigieran el problema, esto era precisamente lo que
sugería microsoft, aparte todos los EventHandlers tendrían que cambiar. Encontramos una maña:
Simplemente renombramos el dll generado:
Microsoft.Practices.Mobile.CompositeUI.WinForms.SmartPartPlaceHolder.PocketPC.asmmeta.dll
a:
Microsoft.Practices.Mobile.CompositeUI.WinForms.PocketPC.asmmeta.dll
Y copiamos esto al mismo folder donde se encuentra Microsoft.Practices.Mobile.CompositeUI.WinForms.dll
Para hacer automático todo agregamos el siguiente PostBuildEvent
copy /Y /B /V SmartPartPlaceHolder.PocketPC.asmmeta.dll

..\Microsoft.Practices.Mobile.CompositeUI.WinForms.PocketPC.asmmeta.dll

Fue todo, muy simple al final, pero batallamos mientras lo encontramos al igual que más gente que ha tenido el
mismo problema.

miércoles, mayo 10, 2006

Ineta Latam

Es para mí un honor que hayan publicado mi blog en la sección de “Nuestros Blogs” del Ineta Latam junto a otros blogs muy interesantes como el de Vicente y Enrique respecto a DSL Tools. No he tenido mucha oportunidad de revisar los demás blogs, pero les sugiero revisen la lista, está puede ser un muy buen punto de partida para encontrar información interesante en su idioma.

Para los que no sepan que es la Ineta, la describo, como siempre hago con todo, brevemente, ya que pueden ver “¿Que es Ineta?”. Es un grupo de grupos, bueno una asociación de comunidades .NET. Seguramente encontraran algún grupo cerca de ustedes. A propósito del tema de comunidades .NET, les comento que estamos por iniciar la comunidad en Torreón, espero tengamos buenos resultados.

Ya tocando el tema de comunidades y blogs, les paso el link del de un buen amigo Christian Strevel.

lunes, mayo 01, 2006

public abstract class DataAccess


Los Genercis son una funcionalidad nueva de .NET 2005 y por ahí trae algo similar Java en su nueva versión (no sé si ya lo hayan liberado).

Para los que han programado en C++ es algo similar a las clases parametrizadas o las TemplateCollections, no recuerdo bien como llamaban, pero seguramente recuerdan algo como:

List<Albums> list = new List<Albums>(); // Tipico de ejemplos en C++

Bueno en C# es muy similar la sintaxis y ahora estamos explotando mucho eso.

Cuando alguien piensa en generics, se le viene a la mente Collecciones.

Antes haríamos algo como:

AlbumCollection : ICollection

{

// Aquí teníamos un montón de métodos para implementar ICollection (bueno no son tantos), pero era puro código repetitivo.

}

Lo enfadoso de esto es que seguramente íbamos a necesitar más de una colección con tipo, probablemente más de 10 o 30 y no sólo para nuestros BusinessEntities. Este tipo de colecciones aún se usan y pueden verlo cuando escriban datagrid1.Rows. Ahora podríamos simplemente decir.

List<Album> list = new List<Album>();

Mmm se parece a C++?

Ok, ok, las colecciones son la parte sencilla seguramente quieren ver algo más interesante. Bueno el nuevo generis EventHandler es un buen ejemplo. Para los que han tenido que crear eventos específicos y para poder cumplir con los guidelines de Microsoft, necesitamos algo como:

public class prueba

{

Prueba()

{

// Do something cool

}

public event DelegadoEspecifico evento;

}

Por lo que necesitaban crear un delegado específico:

public delegate void DelegadoEspecifico(object o, AlgoHeredandoDeEventArgs e);

Y necesitaban tener su clase AlgoHeredandoDeEventArgs

public class AlgoHeredandoDeEventArgs : EventArgs

{

AlgoHeredandoDeEventArgs(string x, string y)

{

// Do something cooler

}

// Write some useful properties here.

}

Ok, ok, es algo sencillo, pero se puede hacer más simple sin la necesidad de tener un montón de delegados.

Digamos que necesitas otro evento, pero que ahora reciba otros EventArgs distintos, porque, porque es lo único que cambia, así que ahora es:

public event DelegadoEspecifico2 evento;

y crear otra clase public class AlgoHeredandoDeEventArgs : EventArgs {}

Bueno luego usarla: public event DelegadoEspecifico2 evento2;

Bueno los genéricos nos permiten crear delegados específicos:

public event EventHandler<AlgoHeredandoDeEventArgs> evento;

public event EventHandler<AlgoHeredandoDeEventArgs2> evento;

Realmente lo único que nos ahorramos es la declaración de los delegados, pero ya es algo.

No es el único uso. Object Builder utiliza una de las formas más interesantes de aplicar Generics. Es muy complejo para describirlo aquí, pero la parte simple la pueden ver en Enterprise Library and Object Builder.

Recientemente hemos trabajando en un DataAccessLayer/DataMappingLayer (ver Table Data GateWay y Fowler’s EAA patterns) para Windows Mobile 5.0 y el uso de generics combinado con la herencia y polimorfismo fácilitaron enormemente las cosas. De manera resumida está es la arquitectura:

(Ver diagrama).

Tenemos nuestra clase base (abstracta) DataAccess, que implemente la interfaz principal IDataAccess (en caso de que necesitemos cambiar la implementación, por ejemplo para usar otra base de datos)

Todo se vuelve bastante sencillo con Generics. Usando template methods podemos ir encapsulando la funcionalidad genérica en las tablas de más arriba en la cadena de herencia, por ejemplo DataAccess es el único lugar donde se construyen los querys para la base de datos y se ejecutan los Comandos, etc. Aparte como lo tenemos todo bien encapsulado podemos destinar más tiempo a optimizar esa parte y hacerlo muy bien una sola vez. Realmente nadie quiere escribir el mismo código muchas veces (Abrir conección, execute reader, etc). Como en este caso muchas de las tablas usan Clave y descripción del mismo tipo, tenemos una segunda Clase Abstracta que se encarga de establer dichos valores y delegar el trabajo a un nuevo Template Method (el Donet en las clases concretas). De está forma cada DataAccess como VendedoresDataAccess y Lineas DataAccess son las únicas que conocen el nombre u orden de las columnas en la base de datos y saben como establecer las propiedades al Business Entitie que queremos crear.

Claro, está sólo una forma muy simple de hacer el Mapeo de Relacional a Objetos. Es algo similar a como lo explica Martin Fowler en su libro Patterns of Enterprise Applications Architecture la diferencia es que en la implementación de Fowler los metodos regresaban clases Padres, por ejemplo una clase del tipo DomainObject en lugar de una clase del tipo Persona.

Este es el código de Fowler (Java):

Class AbstractMapper{

Protected DomainObject GetDomainObject(ResultSet rs, long id){

// Do common stuff

DomainObject result = doLoad(id,rs);

// Do some other common stuff

return result;

}

}

abstract protected DomainObject doLoad(Long id, ResultSet rs);

class PersonMapper:AbstractMapper

{

protected DomainObject doLoad(Long id, ResultSet rs){

String lastNameArg = rs.getString(2);

// get other properties

return new Person(id,lastNameArg,etc);

}

}

Los usuarios de esas clases tendrán que hacer algo como:

Person p = (Person)personMapper.GetDomainObject(1);

Los dos graves problemas son:

  1. Corremos el riesgo de equivocarnos de tipo de objeto y detectarlo hasta el tiempo de ejecución en lugar de en tiempo de compilación.
  2. Es ineficiente estar haciendo tanto boxing (cuando regreso el objeto person como DomainObject) y unboxing (cuando hacemos el cast de DomainObject a Person).

Aparte algún error en el tipo de datos podría venir desde PersonMapper no sólo por el cliente.

En código (C#) y usando generics se puede hacer algo como lo siguiente:

Class AbstractMapper<TBO>

where TBO: DomainObject,new()

{

Protected TBO GetDomainObject(ResultSet rs, long id){

// Do common stuff

TBO result = doLoad(id,rs);

// Do some other common stuff

return result;

}

abstract protected TBO doLoad(Long id, ResultSet rs);

}

class PersonMapper: AbstractMapper<Person>

protected Person doLoad(Long id, ResultSet rs){

String lastNameArg = rs.getString(2);

// get other properties

return new Person(id,lastNameArg,etc);

}

Y ahora los clientes pueden usarlo de la siguiente forma:

Person p = personMapper.GetDomainObject(1);

No es necesario el cast ni el boxing, ya que siempre la clase AbstracMapper estará trabajando con el tipo de Objecto concreto en lugar de con DomainObject. Por otro lado el template method de PersonMapper (doLoad) para implementar la clase abstracta necesita forzosamente regresar un objeto del tipo Person.

Algo similar aplica también con Interfaces e inclusive es mucho muy útil ya que podemos tener ciertas Interfaces Genéricas y después implementarlas desde Interfaces específicas y obligar a las clases que deseen implementar a estás últimas a que cumplan con todos los metodos.

interface IGenericMapper<TBO>{

TBO Get();

}

interface IPersonMapper:IGenericMapper<Person>{}

public class PersonMapper:IPersonMapper{

// Must implement:

Person Get(){

// Do something

}

}

Claro que ahora como parte del framework que tenemos, ya está funcionalidad quedará en el DataAccessGenérico y será suficiente con que PersonMapper tenga lo siguiente:

public class PersonMapper:DataAcccess<Person>,IPersonMapper{

// Person Get() //Ya está implementada para la clase abstracta DataAccess.

// Ahora solo debemos implementer el metodo abstracto

protected Person DoFill(Person p, reader r)

{

p.Name = r[“Name”].ToString();

return p;

}

}

Pudiera parecer medio inutil el tener la interfaz IPersonMapper, pero es que en está queremos ahora dar de alta ciertos métodos específicos como GetByName, GetByLastName(string lastName), etc.