sábado, 17 de octubre de 2009

[ASP.NET] Pasar información entre User Control

 

Introducción


El desarrollo modular de aplicaciones web requiere la confección de componentes que permitan encapsular ciertos compartimientos que serán reutilizados, o que necesitan ser tratados de una forma unificada para facilitar la mantenibilidad del desarrollo.

Es por eso que en ciertas ocasiones el uso de User Controls es una buena práctica, aunque un aspecto no menor es la comunicación entre estos componentes encapsulados con el resto de la aplicación, o con otros componentes.

En este texto explicare como lograr la comunicación entre dos User Control, pasando datos de uno a otro de una forma desacoplada.

 

Diseño


El planteo se basará en la utilización de dos users control muy simples que estarán contenidos en una misma página web.

El primer user control encapsulará una grilla la cual cargara un listado de empleados.

El segundo contiene una serie de Textbox que serán utilizados para visualizar el registro seleccionado del primer user control.

 

User Control – Listado


Este estará formado por un control GridView.

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;

namespace WebUserControlsCommunicated
{
    public partial class ucGrid : System.Web.UI.UserControl
    {
        public delegate void GridSelectorCommandEventHandler(GridSelectorCommandEventArgs e);
        public event GridSelectorCommandEventHandler GridSelectorChanged;

        public class GridSelectorCommandEventArgs
        {
            public int Id { get; protected set; }
            public string Nombre { get; protected set; }
            public string Cargo { get; protected set; }

            public GridSelectorCommandEventArgs(int id, string nombre, string cargo)
            {
                this.Id = id;
                this.Nombre = nombre;
                this.Cargo = cargo;
            }
        }


        public DataTable DataSource { get; set; }


        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                GridView1.DataSource = this.DataSource;
                GridView1.DataBind();
            }
        }

        protected void GridView1_SelectedIndexChanged(object sender, EventArgs e)
        {
            int Id = Convert.ToInt32(GridView1.SelectedRow.Cells[1].Text);
            string nombre = Convert.ToString(GridView1.SelectedRow.Cells[2].Text);
            string cargo = Convert.ToString(GridView1.SelectedRow.Cells[3].Text);


            if (GridSelectorChanged != null)
                GridSelectorChanged(new GridSelectorCommandEventArgs(Id, nombre, cargo));

            
        }
    }
}

Los puntos importantes a destacar del código serian:

- línea 30, define la propiedad que permitirá asignar el origen de datos que se usará para cargar el GridView. Esta propiedad será utilizada en el evento Page_Load del user control, asignando el contenido.

- líneas 12 y 13, en las mismas se define el handler y evento que permitirán que la información  de los items seleccionados del GridView viajen desde el interior del user control hacia la página.

- líneas 15-27, definen el argumento del evento que será usado para realizar el pasaje de los datos seleccionados en la grilla. Es por medio de este argumento que se podrá recuperar los valores elegidos, ya que desde fuera del user control no se tendra acceso directo al GridView.

Habría una alternativas a esta opción, la cual consiste en dejar disponible en una propiedad los valores seleccionados, imitando un poco a las propiedades como ser el SelectedRow, o SelectedItems que poseen otro controles.

- líneas 42-53, estas defines el evento local al user control que se ejecutará al utilizar el botón de selección del GridView, básicamente será una especie de conversión de eventos, en donde un evento atrapado localmente, es transformado en un evento exterior, el cual es lanzado en la línea 50.

Como ser observara las primeras acciones son las de recuperar los valores de la columnas de la fila seleccionada, para poderlos usar como parámetros del argumento del evento que se lanzara.

Es muy importante cuando se va a lanzar un evento validar que este tenga algún método adjunto, ya que de no tener ninguno y ejecutarse este producirá un fallo, la validación por distinto de null evita este problema.

 

User Control – Listado de TextBox


Este user control contendrá una serie de TextBox, destinado a la visualización de la información seleccionada en el user control que contiene el GridView

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebUserControlsCommunicated
{
    public partial class ucTextView : System.Web.UI.UserControl
    {

        public int Id { get; set; }
        public string Nombre { get; set; }
        public string Cargo { get; set; }

        protected void Page_Load(object sender, EventArgs e)
        {

        }

        public void Refresh(int id, string nombre, string cargo)
        {
            this.Id = Id;
            this.Nombre = nombre;
            this.Cargo = cargo;

            this.Refresh();
        }

        public void Refresh()
        {
            txtId.Text = Convert.ToString(this.Id);
            txtNombre.Text = this.Nombre;
            txtCargo.Text = this.Cargo;
        }
    }
}

Este control tiene bastante menos lógica que implementar ya que su funcionalidad se reduce a recibir los valores y asignarlos a los TextBox que correspondan.

Como se observa cuenta con una serie de propiedades que representan cada atributo de la entidad.

Y un método Refresh() que será el encargado de realizar en concreto la asignación de cada propiedad con su respectivo control.

 

Integración – Default.aspx


Bien llego el momento de poner todo en conjunto a funcionar.

Para ello se hará uso del formulario web, Default.aspx, es allí donde se arrastrara cada user control en el diseñador de la pagina, haciendo uso de una tabla para dar algo de formato y ubicación.

using System;
using System.Collections.Generic;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Data;

namespace WebUserControlsCommunicated
{
    public partial class _Default : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            ucGrid1.GridSelectorChanged += new ucGrid.GridSelectorCommandEventHandler(ucGrid1_GridSelectorChanged);

            if (!IsPostBack)
            {
                ucGrid1.DataSource = CargarTabla();
            }
        }

        void ucGrid1_GridSelectorChanged(ucGrid.GridSelectorCommandEventArgs e)
        {
            ucTextView1.Id = e.Id;
            ucTextView1.Nombre = e.Nombre;
            ucTextView1.Cargo = e.Cargo;

            ucTextView1.Refresh(); 
        }



        private DataTable CargarTabla()
        {
            DataTable dt = new DataTable();
            dt.Columns.Add("Id");
            dt.Columns.Add("Nombre");
            dt.Columns.Add("Cargo");

            DataRow row = dt.NewRow();
            row["Id"] = 1;
            row["Nombre"] = "Andres";
            row["Cargo"] = "Developer";
            dt.Rows.Add(row);

            row = dt.NewRow();
            row["Id"] = 2;
            row["Nombre"] = "Federico";
            row["Cargo"] = "PM";
            dt.Rows.Add(row);

            row = dt.NewRow();
            row["Id"] = 3;
            row["Nombre"] = "Leonardo";
            row["Cargo"] = "Developer";
            dt.Rows.Add(row);

            return dt;

        }

    }
}

En la línea 14 es donde se define la utilización del evento expuesto por el user control que contiene la grilla, la asignación del handler requiere de un método declarado en las líneas 22-29.

Es en este método ucGrid1_GridSelectorChanged donde se hará uso de los parámetro del argumento del evento GridSelectorCommandEventArgs, para especificar los datos al segundo user control.

Como se habrá notado en este caso se asignan las propiedades directamente al segundo user control, para llamar por ultimo el método Refresh() que realizara la asignación de estos valores dentro del control; pero también se podría haber utilizado para tal fin el método Refresh() sobrecargado con los valores de cada propiedad necesaria para desplegar la información.

[C#] 
 

lunes, 12 de octubre de 2009

C# – Word – Utilización de Tablas

 

Introducción

El hacer uso de las librerías de Interop de Office puede llega a ser complejo en ciertas circunstancias, mas cuando se necesita hacer uso de posiciones relativas a otros elemento en un documento, es por ello que en este ejemplo intento reflejar como hacer uso de tablas en Word manipulándolas desde código.

Pasos previos

Se debe remarcar que para que estos ejemplos puedan ser ejecutados es necesario contar un Office instalado en el equipo, ya que las librerías de Interop requieres que se encuentren presentes los componente COM que utilizan, y es necesario que Word sea instalado para ello.

Notarán en el código en sus primeras líneas la inclusión de un alias en el using para poder referenciar a la funcionalidad de Office Word

using Office = Microsoft.Office.Interop.Word;
Introducir una tabla dentro de otra

La primera aproximación que tendremos con las tablas será para utilizarlas de forma anidadas, o sea una tabla dentro de otra.

Para ellos contaremos con el siguiente código:

        private void btnTabladentroTabla_Click(object sender, EventArgs e)
        {
            Office.ApplicationClass app = new Office.ApplicationClass();
            
            //
            // Agrego un nuevo documento a la aplicacion Word
            //
            object missing = System.Reflection.Missing.Value;

            Office.Document doc = app.Documents.Add(ref missing, ref missing,
                                 ref missing, ref missing);



            //
            // Adiciono una tabla sobre el documento
            //
            object DefaultTableBehavior = Office.WdDefaultTableBehavior.wdWord9TableBehavior;
            object WdAutoFitBehavior = Office.WdAutoFitBehavior.wdAutoFitWindow;

            Office.Table table1 = doc.Tables.Add(app.Selection.Range, 4, 3, ref DefaultTableBehavior,ref WdAutoFitBehavior);
            
            //
            // Inserto al segunda tabla, pero a esta le indicare que el range donde debe
            // incluirse esta dentro de la primer tabla, mas precisamente en la primer columna
            // segunda fila
            //
            Office.Table table2 = doc.Tables.Add(table1.Cell(2,1).Range, 2, 3, ref DefaultTableBehavior,ref WdAutoFitBehavior);

            //
            // Agrego una fila nuevo a la tabla
            //
            table2.Rows.Add(ref missing);

            //
            // Guardo el archivo
            //
            object fileNameSave = Path.Combine(Application.StartupPath, "ArchivoGenerado.doc");

            doc.SaveAs(ref fileNameSave, ref missing, ref missing, 
                        ref missing, ref missing, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref missing, 
                        ref missing, ref missing, ref missing, ref missing, ref missing);


            doc.Close(ref missing, ref missing, ref missing);

            MessageBox.Show("El documento se generó correctamente");

        }

El código de por si, al seguirlo paso a paso esta bastante claro en las operaciones que realiza, pero por ahí hay algunos puntos a remarcar.

Uno de ellos es la línea 23 se notará que el rango utilizado en la creación de la tabla incluye la selección de una celda de la tabla creada previamente, este es el punto clave para insertar una tabla dentro de otra, la sección correcta del lugar donde ubicarla.

Una aspecto bastante raro que me encontré cuando realizaba las pruebas es que por mas que hacia uso de los parámetros NumRows la tabla interna siempre aparecía con una sola fila, es por ellos que adicione una línea de código que inserta una fila adicional. Esto se visualiza en la línea 27 del código.

Insertar una tabla en medio de otras dos

Aquí también es importante jugar con una correcta selección de los objetos, y ubicar correctamente la posición para insertar la nueva tabla.

        private void btnTablaEntreTablas_Click(object sender, EventArgs e)
        {
            Office.ApplicationClass app = new Office.ApplicationClass();

            //
            // Realizo al apertura de un archivo existente, el cual contendra 4 tablas
            //
            object missing = System.Reflection.Missing.Value;
            object fileNameAbrir = Path.Combine(Application.StartupPath, "ArchivoOriginal.doc");

            Office.Document doc = app.Documents.Open(ref fileNameAbrir, ref missing, ref missing, ref missing, 
                                                    ref missing, ref missing, ref missing, 
                                                    ref missing, ref missing, ref missing, 
                                                    ref missing, ref missing, ref missing, 
                                                    ref missing, ref missing, ref missing);

            //
            // Selecciono la segunda tabla del documento
            //
            doc.Tables[2].Range.Select();
 
            //
            // de la seleccion anterio me desplazo un posicion hacia abajo
            //
            object moveUnit = Office.WdUnits.wdLine;
            object moveLength = 1;

            app.Selection.MoveDown(ref moveUnit, ref moveLength, ref missing);

            //
            // Agrego un espacio, o entrer, para generar la separacion para la proxima tabla
            //
            app.Selection.TypeParagraph();


            //
            // Inserto a la nueva tabla
            //
            object DefaultTableBehavior = Office.WdDefaultTableBehavior.wdWord9TableBehavior;
            object WdAutoFitBehavior = Office.WdAutoFitBehavior.wdAutoFitWindow;

            Office.Table table1 = doc.Tables.Add(app.Selection.Range, 4, 3, ref DefaultTableBehavior, ref WdAutoFitBehavior);

            Office.Range cellrange = table1.Cell(1, 1).Range;
            cellrange.Text = "Tabla Intermedia";

            //
            // Guardo el documento
            //
            object fileNameSave = Path.Combine(Application.StartupPath, "ArchivoGenerado.doc");

            doc.SaveAs(ref fileNameSave, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref missing,
                        ref missing, ref missing, ref missing, ref missing, ref missing);


            doc.Close(ref missing, ref missing, ref missing);

            MessageBox.Show("El documento se genero correctamente");

        }

En este ejemplo a diferencia del anterior, se parte de un documento ya existente, al cuál se le agregara una tabla intermedia entre otras dos tablas.

En la línea 17 se visualiza como se selecciona la tabla debajo de la cual se quiere insertar la nueva. Las siguientes líneas 21-27 son importantes para posicionar el cursor de forma correcta.

Debe remarcarse como se hace uso de la propiedad Selection del objeto Application, en este caso no se hace uso del Document. El objetivo, una vez que se tiene la tabla seleccionada, se baja una posición con el cursor, y luego agregar un enter o párrafo, para lograr separar las tablas.

Sino se agrega un párrafo intermedio entonces las tablas se crearán unidas, no logrando el efecto deseado.

[C#]
 

sábado, 3 de octubre de 2009

C# - Crystal Reports – Sumatoria Condicional (Conditional Sum)

 
Introducción

Cuando se requiere trabajar con Crystal en la creación de reportes un punto interesante es poder agregar condiciones a los campos de agregación, como se una sumatoria al final de la pagina, para ello tenemos herramientas en Crystal que utilizaremos en este ejemplo

Desarrollo

Para el ejemplo haremos uso de un ambiente simple en donde tendremos una lista de usuarios y sus asistencias a determinado evento.

Para ello ene le ejemplo se hará uso como origen de datos un DataSet Tipado, el cual era cargado en este caso de forma manual, pero bien podría hacerse accediendo a una base de datos

Como próximo paso se creara el reporte agregando a la sección de detalle los campos definidos en el DataSet.

Una vez que tenemos el reporte armado, se procederá a la creación del campo de sumatoria, para ello se realiza un click sobre el campos al cual se le quiere aplicar la suma y se selecciona Insert –> Running Total …

En el dialogo que se visualizara se podrá cambiar el nombre en este caso se utilizo “AsistenciasTotal”, y la parte mas importante será definir la formula que usara, para ello se deberá utilizar la opción que dice “Use a formula”

Al presionarla se visualizara otro cuadro en donde se introducirá la formula que es requerida en este caso filtraremos por un nombre, por ejemplo “Carlos”, por último se acepta el cuadro con el botón superior “Save and close”.

Cuando se acepte el ultimo cuadro seguramente los campos pueden quedar encimados, pera solucionarlo solo hace falta moverlos, ubicándolos donde sea requerido.

El reporte final, luego de ubicar correctamente los campos, quedara de esta forma:

Y al ejecutarlo el resultado será el siguiente:

Debe notarse además que si se visualiza el “Field Explorer” se encontrara el campo de sumatoria recién creado.

 

[C#]
 

sábado, 19 de septiembre de 2009

C# – AutoComplete ComboBox o TextBox

 

Introducción


Muchas veces es necesario exponer al usuario herramientas de búsqueda que le faciliten la interacción con la aplicación que desarrollamos.

Una de estas herramientas es precisamente el AutoComplete, por el cual el usuario podrá ir visualizando los ítems existentes a medida que se escribe en un control.

TextBox AutoComplete


Hacer uso de las opciones de autocomplete de estos controles es bastante simple, solo hace falta especificar un par de propiedades, pero hay que tener en cuenta algunos puntos.

Estas propiedades son:

AutoCompleteSource

AutoCompleteMode

AutoCompleteCustomSource

textBox1.AutoCompleteCustomSource = DataHelper.LoadAutoComplete();
textBox1.AutoCompleteMode = AutoCompleteMode.Suggest;
textBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;

En el ejemplo se visualiza como asignar estas propiedades, pero debe prestarse atención a la propiedad “AutoCompleteCustomSource” esta es clave, pues contendrá la lista de ítems.

Esta propiedad es justamente uno de los puntos a tener en cuenta, ya que requiere cargar una lista de ítems que provenga de una colección del tipo “AutoCompleteStringCollection”

Es por ello que en el ejemplo se visualiza la generación de los datos que cargaran esta colección

public static AutoCompleteStringCollection LoadAutoComplete()
{
    DataTable dt = LoadDataTable();

    AutoCompleteStringCollection stringCol = new AutoCompleteStringCollection();

    foreach (DataRow row in dt.Rows)
    {
        stringCol.Add(Convert.ToString(row["Nombre"]));
    }

    return stringCol;
}

El código es bastante simple de entender, se obtiene los datos desde la db y como siguiente paso los recorre cargando la lista de ítems del autocomplete.

ComboBox AutoComplete


La utilización de las opciones de autocomplete de un control Combobox son idénticas a las de un TextBox, solo difiere en que el combo requiere cargar sus ítems previamente para la selección por parte del usuario.

O sea el combobox requiere dos listas para bindear

- una normal que se asignara al DataSource, y en donde se especificara tanto el valor a desplegar como el valor de la key

- una especial con la lista de descripciones para el autocomplete

//
// Cargo los datos del combobox
//
comboBox1.DataSource = DataHelper.LoadDataTable();
comboBox1.DisplayMember = "Nombre";
comboBox1.ValueMember = "Id";

//
// cargo la lista de items para el autocomplete
//
comboBox1.AutoCompleteCustomSource = DataHelper.LoadAutoComplete();
comboBox1.AutoCompleteMode = AutoCompleteMode.Suggest;
comboBox1.AutoCompleteSource = AutoCompleteSource.CustomSource;

Conclusión


El autocomplete es una excelente opción para ayudar al usuario en la interacción con la aplicación brindándole un fácil acceso mientras escribe la búsqueda.

 

[C#] 
[VB.NET] 

sábado, 12 de septiembre de 2009

[Winform] Realizar tareas antes de inicializar aplicación

 

Introducción


Algunas veces en las aplicaciones es necesarios desplegar una ventana de login, o un formulario de inicio, o ambos, esto genera un gran problema cuando la aplicación esta iniciada, ya que la ventana principal del formulario se encuentra visible. La idea es poder realizar operaciones previas a la visualización de la pantalla definida como principal.

Es por ello que existe un área (o método) previa al inicio, en donde se puede realizar operaciones previas al inicio de la aplicación.

Esta función tiene el nombre de Main()

 

C# – Main


Cuando se crea una “Windows Applicacion”, se podrá apreciar una clase de nombre: Program.cs

Esta clase es clave para poder realizar trabajos previos.

De forma inicial encontrara código similar al siguiente:

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);
    Application.Run(new Form1());
}

Las aplicaciones Windows en C#, siempre inician en un método Main.

Por supuesto podrá acomodarse para que realice algunas tareas previas, como ser, el mostrar un cuadro de login:

[STAThread]
static void Main()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(false);

    //
    // Realizo la apertura del formulario para validar el login
    // esta tarea es previa al inicio de la aplicacion
    //
    frmLogin login = new frmLogin();
    login.ShowDialog();

    //
    // Si el login es correcto, procedo con la apetura normal
    // de la aplicacion
    //
    if (login.DialogResult == DialogResult.OK)
        Application.Run(new frmPrincipal());

}

Como se observa en el código en las líneas 11 y 12, se incia un nuevo formulario, que tendrá la responsabilidad de de la autenticación de usuario.

Dependiendo del resultado de la operación previa es que se dará inicio, o no, al hilo principal de la aplicación.

Algunos puntos interesantes a destacar:

  • Es necesario que los formulario previos realicen una pausa en la ejecución de método Main(), es por eso que se hace uso del ShowDialog() para mostrar el formulario de login. En este caso el simple Show() no podrá ser utilizado ya que este no produce un stop, sino que deja al método Main() continuar su ejecución
  • El código de las líneas 4 y 5 siempre serán las primeras líneas del método  Main(), ya que estas deben ejecutarse previas al despliegue de cualquier formulario
  • Como se observara en ningún momento se hizo uso de alguna funcionalidad de exit, o quit que cerrara la aplicación, simplemente con evitar pasara por la asignación de un formulario en Application.Run(), fue suficiente.

 

VB.NET – Sub Main


Si bien la explicación de este método en C# es bastante similar a la de VB.NET, deben remarcarse algunas diferencias que son fundamentales para lleva a cabo la tarea de forma correcta.

En principio al crear la Windows Application en VB.NET, esta no creara el archivo de clase “Program”, o similar, como si se hace en C# de forma automática. Es por eso que la creación de la clase deberá hacerse de forma manual.

En el ejemplo se visualizará una clase de nombre “SubMain.vb”, y dentro de esta el siguiente código:

<STAThread()> _
Shared Sub Main()

    Application.EnableVisualStyles()
    Application.SetCompatibleTextRenderingDefault(False)

    Dim login As frmLogin = New frmLogin
    login.ShowDialog()

    If (login.DialogResult = DialogResult.OK) Then

        Application.Run(New frmPrincipal)

    End If

End Sub

Como verán es prácticamente idéntico método Main() codificado en C#.

Bien hasta aquí todo perfecto, pero falta un paso mas que tal vez no sea tan obvio como parece.

Resulta que para que la aplicación VB.NET pueda tomar el método Main() como inicio hay que desmarcar la el check de nombre “Enable Application framework”

VB.NET SubMain Startup

Como se observa en la imagen, este check esta desmarcado y es en ese momento en donde se puede cambiar en el combo “Startup object” al método Sub Main.

En este link: Cómo: Cambiar el objeto inicial de una aplicación (Visual Basic)

Se encontrará una explicación mas amplia del tema.

 

Conclusión


Haciendo uso de método Main() se puede realizar operaciones previas a la carga del formulario principal de la aplicación de una forma simple y prolija.

 

[C#] 
[VB.NET] 

domingo, 6 de septiembre de 2009

Comunicar formularios MDI

 

Introducción


Este post representa la continuación de Comunicar formularios de forma desacoplada

Pero a diferencia del anterior en esta oportunidad se trabajara con formularios MDI

 

Diferencias


Al hacerse uso de formularios MDI, algo que ya no podrá ser utilizado es el parámetro Owner en el método Show(), al realizar la apertura del formulario hijo.

Es por ello que será necesito hacer uso de una propiedad en el formulario hijo para salvar este inconveniente, y poder así determinar que formulario esta ejecutando la acción de apertura.

 

Primer Paso – Definición de la interfaz

A diferencia el ejemplo anterior en este oportunidad se hará uso de un valor algo mas complejo que simple texto en la comunicación, es por ello el uso de un objeto DataTable como medio de transporte de datos entre los formularios.

Aunque nada impediría que se utilices clases custom creadas por uno.

public interface IForm
{
    bool LoadDataGridView(DataTable dataTableParam);
}

En este caso la interfaz también retorna un valor que indicará si el procesamiento se realizó correctamente.

 

Segundo Paso – Definición del formulario padre

Al igual que en el anterior post el formulario padre deberá implementar una interfaz, la cual permitirá desacoplar la dependencia durante la comunicación.

public partial class Form1 : Form, IForm
{
    public Form1()
    {
        InitializeComponent();
    }

    #region IForm Members

    public bool LoadDataGridView(DataTable dataTableParam)
    {
        DataGridView1.DataSource = dataTableParam;

        return true;
    }

    #endregion


    private void Button1_Click(object sender, EventArgs e)
    {
        Form2 form2 = new Form2();

        form2.MdiParent = this.MdiParent;

        form2.Opener = this;

        form2.Show();
    }

}

Debe remarcarse algunos detalles, como es en este caso el uso de la propiedad MdiParent, que por supuesto siempre hará referencia al FormPrincipal, y que este ha sido declarado como MdiContainer

También hay que marcar la línea 26, en donde se visualiza el uso de la propiedad adicional, en este caso llamada Opener, es en esta donde se asignara el form que realizo la apertura del formulario.

 

Tercer paso – Definición del formulario hijo

Este formulario simplemente tendrá un botón que realizará el cierre de si mismo, pero durante esta operación se generarán los datos que serán pasados al formulario que se registro en la propiedad Opener.

Si bien este es un ejemplo, la misma técnica podría ser utilizada para realizar distintas acciones y pasaje de datos entre formularios.

public partial class Form2 : Form
{
    public IForm Opener { get; set; }

    public Form2()
    {
        InitializeComponent();
    }

    private DataTable LoadDataTable()
    {

        DataTable dt = new DataTable();

        dt.Columns.Add("Id");
        dt.Columns.Add("Nombre");

        for(int i=0 ; i <= 4; i++){
            DataRow row = dt.NewRow();

            row["Id"] = i;
            row["Nombre"] = String.Format("Nombre {0}", i);

            dt.Rows.Add(row);

        }

        return dt;
    }

    private void Button1_Click(object sender, EventArgs e)
    {
        this.Close();
    }

    private void Form2_FormClosing(object sender, FormClosingEventArgs e)
    {
        DataTable dataTable = LoadDataTable();

        bool estadoOperacion = this.Opener.LoadDataGridView(dataTable);

        e.Cancel = !estadoOperacion;
    }

}

La línea 3 define la propiedad Opener cuya utilización se visualizo en el código del Form1, la declaración de la misma utiliza el concepto de propiedades autoimplementadas.

La interacción entre formularios retorna un resultado que el formulario hijo utilizara para saber si debe proseguir con el cierre del formulario o cancelarlo.

En este simple ejemplo el formulario padre retorna siempre un estado satisfactorio de la operación, pero podría ser utilizado para capturar errores e informarlo al formulario hijo de esta situación.

 

Conclusión


La interacción entre formulario en un entorno MDI requiere de ciertas modificaciones al uso normal de iteración entre estos, igualmente la idea básica no es afectada.

Solo la imposibilidad de definir un formulario como owner añadió cierta complejidad al ejemplo, pero fue salvada con el uso de propiedades.

 

[C#] 
[VB.NET] 

Comunicar formularios de forma desacoplada

Introducción


El objetivo de esta guía es demostrar como posibilitar la comunicación de formulario de una forma optima, haciendo uso de buenas practicas, y además bajando al mínimo el acoplamiento entre los formulario.

Para cumplir el objetivo es que se hará uso de interfaces, las cuales permitirán desacoplar la comunicación entre formulario.

 

Comunicación simple entre formularios


En este ejemplo se confeccionara una utilidad simple, básicamente se procederá a la apertura de un formulario, y la escritura cuadros de texto.

 

Primer Paso – Creación de los formularios

Lo primero que será necesario crear son los dos formulario los cuales intervendrán en la comunicación, esta operación se hace de forma simple como seguramente ya es conocida por todos. Los mismos contaran con sus respectivos cuadros de texto, y botones que permitirán las acciones entre ellos

 

Segundo Paso - Definición de la interfaz

En al interfaz se definirá la acción que será llevada a cabo en la comunicación, en este caso sera algo simple como copiar el contenido del formulario hijo al padre

Para ello se ha creado un archivo de nombre IForm.cs, el cual contendrá dicha definición.

public interface IForm
{
	void ChangeTextBoxText(string text);
}

como podrá preciarse es muy simple solo define el contrato que permitirá luego desacoplar las interfaces

 

Tercer Paso – Implementación de la interfaz, llamada al formulario hijo

La interfaz anteriormente creada será implementada por el formulario del cual se quiere recibir la acción, en este caso será el form principal, o sea quien realiza la llamada.

public partial class Form1 : Form, IForm
{
    public Form1()
    {
        InitializeComponent();
    }

    #region IForm Members

    public void ChangeTextBoxText(string text)
    {
        TextBox1.Text = text;
    }

    #endregion

    private void Button1_Click(object sender, EventArgs e)
    {
        Form2 form2 = new Form2();
        form2.Show(this);
    }
}

Hay que resaltar varias partes en esta sección de código:

- se notara en la primer línea como el formulario define la implementación de la interfaz

- la región establecida en el código, denota la implementación concreta de la misma, entre las líneas 8 al 15

También hay que remarcar como se realiza la apertura del formulario hijo, debe notarse el uso del parámetro en el método Show(), es allí donde se especifica quien será el owner, en este caso se hace uso de this ya que el formulario donde estamos en ese momento será quien realiza la apertura.

 

Cuarto Paso – Comunicación desde el formulario hijo

En esta ultima sección se visualizara como el formulario hijo envía datos al padre.

public partial class Form2 : Form
{
    public Form2()
    {
        InitializeComponent();
    }

    private void Button1_Click(object sender, EventArgs e)
    {
        IForm formInterface = this.Owner as IForm;

        if(formInterface != null)
            formInterface.ChangeTextBoxText(TextBox1.Text);
    }

}

En el código se puede apreciar claramente el uso de la propiedad Owner junto al this que en este caso representa al Form2.

Es interesante además remarcar la necesidad de castear la propiedad, en este caso particular se realiza mediante el uso de “as”
Es muy útil hacer uso de este ya que ante una imposibilidad o error de casteo la aplicación no producirá un error, en este caso simplemente la variable obtendrá el valor null, es por ello que se hace uso del “if” para preguntar si pudo realizar la conversión de tipos.

 

Conclusión


Bueno después de analizar el ejemplo verán como por medio de un simple interfaz se logra desacoplar las dependencias entre formulario facilitando la comunicación entre ellos, la simple definición de un contrato asegura que el formulario hijo pueda interactuar ya no solo con un forma padre único, sino con cualquier otro formulario que lo utilice e implemente la interfaz.

[C#]
[VB.NET]

domingo, 7 de junio de 2009

C# - jqModal – Alternar popup en un mismo link

 

En el desarrollo de este ejemplo se pretenderá abordad dos situaciones que fueron encontradas en jqModal:

  • Solucionar un problema que se produce en los cuadro de popup al renderizar por segunda vez una grilla que era generada de forma dinámica.
  • Teniendo un link permitir alterar de cuadro de dialogo, pero siempre haciendo uso del mismo link
Prueba 1 – Problema trigger popup jqModal y grilla desde $.ajax

Esta prueba la encontraran en el ejemplo bajo la carpeta “jqModal1”.

La pagina por defecto es Principal.aspx, la cual posee simplemente un botón que carga una grilla que es obtenida mediante la funcionalidad $.ajax.

Cuando esta grilla es cargada por primera vez, al presionar sobre los botones que esta renderiza, se podrá observar el despliegue de popup.

Pero el problema se presenta si se vuelve a recargar la grilla, lo cual produciendo que el evento definido en el trigger del popup de jqModal deje de funcionar.

Se debe notar que al recargar la grilla esta pasa nuevamente por el método javascript registerPopUp(), pero esto no parece tener efecto.

Prueba 2 – Solución al problema del trigger de jqModal

En este segundo ejemplo se encontró la solución al problema planteado en la primera prueba.

Simplemente se reemplazo la forma en cómo el trigger es registrado, haciendo uso de método de jqModal: jqmAddTrigger

En esta ocasión por más que se realicen recargas de la grilla, los mensajes popup seguirán funcionando.

Según se observa parece ser que este método de jqModal fuerza la actualización del registro de los cuadro de popup.

Prueba 3 – Alternar popup en el mismo link

La tercera prueba va un poco as mas allá en el uso de popup de jqModal, en esta se pretende lograr que cada link pueda alternar  el popup que utilice, después de cada llamada.

El objetivo es tener un link (que en este caso será una imagen), en donde al presionarlo la primera vez muestre un cuadro de dialogo, pero al presionarlo por segunda vez muestre otro distinto, es mas al cerrar este debería volver al mostrar el original, o sea alternar de cuadro de dialogo entre cada pulsación del link.

En la implementación se realizar la apertura de los cuadros sin hacer uso de triggers, este es un detalle a remarcar, ya que la asignación de evento de apertura del cuadro se realizara mediante evento click del link.

Este ejemplo lo encontraran en la carpeta “jqModal3”, deberán especificar la pagina “Principal.aspx” como default en la ejecución.

Observaran como se registra los eventos mediante las líneas:

var itemselected = null;
var clickPopUp1 = function() {   
	itemselected = $(this);   
	$('#popup-info1').jqmShow();
}
var clickPopUp2 = function() {   
	itemselected = $(this);   
	$('#popup-info2').jqmShow();
}

Se debe puntualizar en la utilización de la variable “itemselected”, esta es muy importante ya que permite detectar cual es el objeto especifico que realiza la llamada.

Cuando se hace uso de triggers, o el método jqmAddTrigger es el propio jqModal el que guarda el ítem que lanzo el evento bajo la variable “t”, que es retornada en los eventos.

Cuando se hace uso de los callback de evento del propio jqModal se cuenta con varios objetos que ayudan a controlar el dialogo:

  • w: (jQuery object) The dialog element
  • c: (object) The config object (dialog's parameters)
  • o: (jQuery object) The overlay
  • t: (DOM object) The triggering element

Se podrían hacer algo como lo siguiente:

onHide: function(h) {      
	h.o.remove();     
	$(h.t).removeClass("linkpopup1");     
	$(h.t).toggleClass("linkpopup2");     
	$(h.t).unbind("click").click(clickPopUp2);     
	$(h.t).children("img").attr("src", "../appimages/ok.ico");   
	
	h.w.hide();
},

Es aquí en donde el “h.t” representa el objeto que lanzo el evento, pero en este caso por hacer uso de eventos por fuera de “jqm” (por no definir un “trigger”) es necesario mantener el objeto que lanzo el evento en una variable por separado, en este caso “itemselected”, ya que “h.t” tendrá el valor “undefined”.

Se debe analizar además como se hace la conversión de los cuadros en el link:

var closePopup1 = function(hash) {    
	hash.o.remove();  
	
	$(itemselected).removeClass("linkpopup1");    
	$(itemselected).toggleClass("linkpopup2");    
	$(itemselected).unbind("click").click(clickPopUp2);    
	$(itemselected).children("img").attr("src", "../appimages/ok.ico"); 
	
	hash.w.hide();
}

La línea  hash.o.remove(); que deber quitar el “overlay”. La línea hash.w.hide(); oculta el cuadro de dialogo.

El resto de las líneas trabajan sobre el ítem que lanzo el evento:

  • cambiando la clase que determina el tipo de cuadro despliegan
  • cambia el evento que debe lanzar, primeramente eliminando actual y luego registrando el nuevo.
  • cambia la imagen asignada al ítem, debe recordarse que el objeto que lanza el evento es el link.

En este caso no se aplico pero se podría consulta cual es la clase de un ítem para determinar si cambiar de evento o no, haciendo uso de estas líneas:

if (!($(itemselected).is('.linkpopup1')))   
	$(itemselected).unbind("click").click(clickPopUp2);
Conclusion

Como se ha observado en las distintas pruebas se ha resuelto un problema que se presentaba en jqModal cuadro se recarga la información de forma dinámica.

Y implemento la alteración de de los cuadros de dialogo afectando solo a un link, permitiendo esto hacer nuestras aplicaciones mucho más dinámicas.

[Ejemplo]
[Documento]

domingo, 31 de mayo de 2009

C# - JqModal – Contenido dinámico utilizando “User Controls”

 

Como bien habrán observado la documentación provista por JqModal, este no posee demasiadas alternativas a la hora de desplegar contenido dinámico dentro el popup. Observaran  en la documentación que puede cargarse contenido proveniente de páginas mediante la utilización del parámetro “ajax” de jqModal, pero en todos los ejemplos solo es contenido estático proveniente de paginas htm.

Una alternativa que he evaluado es hacer uso de páginas .aspx como contenido del popup, pero esto tuvo que ser descartado ya que no era soportado por de forma eficiente. Se realizaron pruebas pero si mucho éxito, si bien el contenido se desplegaba sin errores, este destruía los bordes y títulos del popup, además el contenido no era completamente renderizado, sino solo una parte del mismo.

Es por ellos que se opto la utilización de “User Control” para darle dinamismo al contenido, además de poder hacer uso de controles asp.net.

En el ejemplo provisto podrán notar como en la región de tag <div> que define al popup, se hace uso de un .ascx, como contenido.

Notaran además que se hizo uso del control updatePanel dentro del “User Control”, el objetivo del mismo era evitar que toda la página se recargue al presionar un botón de asp.net dentro del popup de jqModal. Este efecto se produce porque en realidad el user control es renderizado dentro de la página que lo contiene, por eso forma parte de la misma.

Se debe aclarar que en las pruebas además de hacer uso de un popup de jqModal, debía cargarse contenido dinámico en la propia página, y es en esta internación en donde se encontraron problemas.

Estructura del ejemplo:

 

Prueba 1 – Conflicto entre popup jqModal y llamda $.get

La primera implementación realizada hacia uso dialogo desplegable cuyo contenido es proporcionado por un User Control, pero a su vez la pagina principal renderiza una grilla que es cargada dinámicamente mediante la funcionalidad de jQuery $.get.

El ejemplo de esta situación lo podrán apreciar en la carpeta de proyecto web de nombre “jqModal1”.

Deberán iniciar con la pagina “Principa.aspx”, en esta observaran un link que desplegara el popup, y un botón que por medio de una llamada de $.get, desplegara una tabla. En el cuadro de dialogo cuentan con un botón que les permite cargar una grilla en su interior.

El popup funciona de maravilla siempre y cuando no se realice la carga de la tabla proveniente de la función $.get, una vez que esta es mostrada en pantalla, el popup se desplegara, pero los eventos del los controles de asp.net dejaran de funcionar, mostrando un error de javascript que dice:

Sys.WebForms.PageRequestManagerServerErrorException: The state information is invalid for this page and might be corrupted.

Este problema se debe a que la invocación por medio del uso de $.get, no solo trae el contenido de la grilla, sino que además obtiene el viewstate de la pagina a la que llama, esto hace que entre en conflicto con el viewstate que se encuentra actualmente en la pagina original, provocando problemas en las llamadas de los controles de asp.net. Básicamente lo que provoca es que los dos viewstate entren en conflicto y deje en estado invalido la información de la página, o sea exactamente lo que informa el mensaje del error.

Prueba 2 – Resolución del conflicto popup jqModel y llamada $.ajax

Para resolver el conflicto provocado por la llamada $.get y el viewstate, además de tener en cuenta que solo era necesario renderizar contenido html, el cual confecciona la grilla, fue por lo cual se utilizo la llamada $.ajax.

Esta implementación la observaran bajo la carpeta “jqModal2”, deberán poner como página de inicio a “Principal.aspx”, contenida en esta carpeta.

Observaran que la estructura es idéntica, salvo que en esta se hace uso del $.ajax, el cual realiza la invocación a web servicio, que se encuentra en el codebehind de Grid.aspx.

La clase del servicio ha sido declarada con el atributo [ScriptService],  mientras que el servicio se la agregaron los atributos [WebMethod, ScriptMethod]

[WebMethod, ScriptMethod]
public static string GetTable(int reloadcounter)
{
}

La invocación al servicio alojado en la página se realiza de la siguiente forma:

$.ajax({
    type: "POST",
    url: "Grid.aspx/GetTable",
    data: "{'reloadcounter':" + reloadnumber + "}",
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    success: function(result) {
        loadGrid(result);
    },
    error: function(XMLHttpRequest, textStatus, errorThrown) {
        alert(textStatus + ": " + XMLHttpRequest.responseText);
    }
});
function loadGrid(result) {
    $("#grid-information").html(result.d);
}

Este servicio será el encargado de retornar la tabla pero debe remarcarse que en esta oportunidad el resultado devuelto es bastante más controlado, pues uno en la implantación del servicio decide que string retornar, o sea en este caso no se obtiene el viewstate que interfiere en los eventos.

Notaran que ya no se produce el error de la primera prueba, pudiendo hacer uso del botón de asp.net del popup sin inconvenientes.

[Ejemplo]
[Documento]

viernes, 28 de noviembre de 2008

NHibernate, ejemplo práctico

Para abrir el blog no elegí mejor tema que el grandioso framework de persistencia, de cual puedo estar hablando sino es NHibernate.

La idea principal de esta publicación será ni más ni menos que proporcionar un ejemplo integral de varias características que mucha falta hacen y que tanto cuesta aprender al comenzar con esta herramienta.

Este artículo no tiene intención de ser detallista ni explicar puntualmente cada aspecto implementado, para ello podrá encontrar artículos mas destacados como por ejemplo el excelente articulo publicado en el Blog de Dario Quintana.

La idea final es simplemente proporcionar un ejemplo que permita ver varias características funcionando juntas.

Aspectos implementados

Entre las características implementadas encontraran los siguientes puntos:

  • Modelo completamente tipado
  • Relaciones many-to-one, one-to-many y many-to-many
  • Persistencia de enumerados
  • Persistencia de campos nText (type="StringClob")
  • Herencia table per class hierarchy <subclass>, table per subclass <joined-subclass>
  • Herencia table per class hierarchy <subclass>, utilizando una formula en el <discriminator>.
  • Consultas hql entre relaciones many-to-many

[C#]
 

Diagramas del Dominio

Diagrama del dominio de la Institución

Diagrama del dominio de reserva de libros

Diagramas de persistencia en SQL Server 2005

Diagrama de Base de Datos de la Institución

Diagrama de Base de Datos de las Reservas de libros

Persistencia de Enumerados

Un punto mas que importante para brinda consistencia al modelo, es la utilización de enumerados en al definición del dominio de la aplicación.

En la clase ClaseEntity, se observara un atributo de nombre FormaPago definido como un enumerado, por supuesto en la definición de su mapper (Clase.hbm.xml) encontraran la definición de de la propiedad

<property type="NHib.Infrastructure.GenericEnumMapper`1[[NHib.Domain.Entities.enumFormaPago, NHib.Domain]], NHib.Infrastructure" name="FormaPago" column="FormaPago" />

Se observa claramente la utilización de un mapper de enumerado (el cual esta alojado dentro del NHib.Infrastructure), el cual permitirá recuperar el número asignado al ítem del enumerado asignado.

Persistencia de campos nText

Un tema interesante para probar es la persistencia de atributos espaciales, como es el caso de comentarios, los cuales deben contener texto que escapa a un simple descripción. Es en estos casos que se utilizara en nText, existiendo en NHibernate una forma especial de definir este tipo de mapeo.

En la clase LibroEntity, es justamente donde se hace uso de este, en el atributo Comentarios definido en el mapper como:

<property column="Comentario" type="StringClob" name="Comentario" />

Es en la clase de test TestLibro.cs -> LargoComentarios(), donde se realiza una prueba de su correcta persistencia, comprobando que no se trunca el valor del atributo

Herencia

En la aplicación encontraran tres tipos de herencia implementada, utilizando:

Tabla para todas las clases

Este primer tipo de implementación lo ubicaran en la entidad LibroEntity, en esta se remarca la definición de los atributos discriminator-value el cual toma un valor numérico cuyo tipo es definido en el tag <discriminator>

Se aclara que el tipo de dato por defecto definido es del tipo de datos string, esto a simple vista pareciera no tener mayor implicancia, pero al momento de utilizar un <discriminator> con valores numéricos, todas las clases deberán tener definido un discriminator-value, aunque la clase principal no sea utilizada.

En el ejemplo la definción del tag <class>, al estar utilizando la interface ILibroEntity, debe definir un discriminator-value, con un valor que no es utilizado por el modelo como diferenciador en la herencia de clases, esto debe hace con el simple objetivo de evitar un error de tipos de datos.

Tabla para cada subclase

La implementación se podrá observar en la clase ReservaEntity, a diferencia del ejemplo anterior que utiliza una sola clase, no existe el discriminator-value, y es por medio de la relación uno a uno de las tablas que se mapean como se obtiene el tipo de objeto.

En este caso se hará uso de un tag de mapeo: <joined-subclass>, la cual definirá cual es el atributo relación entre las tablas, siendo esta la razón de utilizar <key column="" />

Herencia utilizando formulas en el discriminator

Esta será utilizada en los casos en donde se pretenda generalizar entidades que pueden ser agrupadas desde otro nivel de abstracción que requiere el modelo analizado.

En el ejemplo sucedió con los convenios, (definido en la entidad ConvenioEntity) el cual describe la existencia de varios tipos de talleres y excursiones, que se pueden asociar a una institución.

Pero el modelo solo necesitaba diferenciar entre convenios del tipo taller y excursión, para esto necesita agrupar distintos valores en un discriminator.

Además para aumentar la complejidad el atributo esta definido con un carácter mientras que el mapeo hará uso de un valor numérico.

Es por medio del atributo formula que se transformara el valor del discriminator de una valor del tipo carácter a uno numérico.

Se debe aclarar que este tipo de definiciones presentan un inconveniente, solo podrán recuperarse los distintos tipos de subclases definidas, pero no podrán persistirse, ya que es imposible por medio de la definición de la formula saber que atributo de tipo especificar, esto deberá realizarse implícitamente en la definición de la clase a persistir.

Es por esta razón que se notara la definición de un enumerado dentro de las subclases que permita diferenciar las distintas clases concretas de Convenios. La redefinición del constructor permite forzar la especificación al momento de crear una nueva subtipo de Convenio.

El framework necesita la especificación de un constructor sin parámetros, pero para evitar su utilización es que se define como protected. Al igual que la definición del atributo Tipo (también protected) que permitirá ser utilizado solamente por el mapper, permitiendo especificar el tipo concreto a persistir.

Además se debe entender que por tratarse de un campo del tipo char, se realiza por medio del “case” la asignación del valor correcto, si hubiera sido numérico se podría haber persistido el enumerado directamente como el caso visto en a persistencia de enumerados.

Consultas hql entre many-to-many

Si bien la definición del mapeo de la navegación entre entidades resulta de suma utilidad, hay ocasiones en donde se requiere obtener algún tipo de filtro aplicado al otro extremo de la relación, es en este punto donde se hará uso de las consultas HQL, y en ellas el operador JOIN.

Para visualizar un ejemplo de su implementación se podrán visitar varios puntos donde fueron utilizados, destacando:

  • ProfesorRepository -> GetAlumnos(): Implementa un join en donde se recorren dos relaciones muchos a muchos. Esta podrá ser testeada desde TestProfesores -> RecuperoAlumnos()
  • AlumnoRepository -> GetByClase(): Define varias sobrecargas con diferente filtros que son aplicados a un join.

Eliminar en cascada

Como es de suponer esta facilidad de actualizar la cascada de elementos relacionados es de muchísima importancia, pero durante la utilización de la navegación entre entidades me encontré con un pequeño detalle que quería remarcar.

Se trata de la utilización de la propiedad inverse="true", esta es de suma importancia cuando se esta utilizando navegación bidireccional.

De no especificarse esta propiedad al momento de producirse, por ejemplo, la eliminación de un objeto padre, sus hijos serán actualizados y al no encontrase la entidad superior generará una excepción que indicara la imposibilidad de insertar null en el campo de la tabla.

Esto podría resolverse fácilmente marcando el campo de relación para que permita null en su contenido, pero no todos los tipos de relaciones en el modelo pueden hacer esto.

Un ejemplo del uso de esta propiedad podrá encontrase en el achivo de mapeo de la entidad Institución (Institucion.hbm.xml), y la prueba de la eliminacion en cascada se encuentra en el test: TestInstituciones -> EliminoInstitucion()

Nota: Si quieren probar como funciona esta propiedad elimínenla del archivo de mapeo Institucion.hbm.xml, de la relación con el Alumno por ejemplo, e intenten correr el test: TestInstituciones -> EliminoInstitucion(), para ver los resultados.

También podrán marcar el campos "Institución" de la tabla "Alumnos", observarán como se soluciona el problema.

Implementación

En la aplicación encontraran que esta formada por varias capas entre ellas la de Aplicación y Presentación, pero estas no están implementadas, pues no era necesario para el objetivo de aprender NHibernate, simplemente con la utilización del proyecto de Test se pudo comprobar el correcto funcionamiento del framewok de persistencia.

Se debe remarcar que el dominio debe ser tomado algo inventado, no perteneciente a un dominio existente, pensado simplemente para lograr el objetivo principal de verificar la funcionalidad de NHibernate.

Si bien el código es sumamente útil con tan solo visualizar el código de los test, así como también el de los mapper, es posible ejecutarlo si se conecta apropiadamente a una base de datos. Para ellos, como en la mayoría de las aplicaciones, simplemente se debe modificar el app.config del proyecto de pruebas, apuntándolo a la DB que corresponda.

Para crear la estructura de la base de datos se encontrar un proyecto con los script de creación de la estructura, o en caso contrario podrán hacer uso de los archivos .mdf y .ldf de la base de datos, adjuntándolos por medio de la opciones de attach del SQL Server 2005.

Para la ejecución de los test hice uso de UnitRun, de esta forma podía ejecutarlos uno a uno en modo debug e ir analizando como se comporta cada uno.

Conclusión

Espero antes que nada haber aportado un granito mas de arena a la investigación de este potentísimo framework, apuntando principalmente aspectos un poco mas avanzados de los cuales es complicado encontrar ejemplo cerrados que los integre.

Quedan varios puntos todavía por probar, como ser el mapeo a store procedure, paginado, etc

Espero les sea de utilidad y cualquier duda, modificación o error que encuentren serán bienvenidos.