Uso de custom exceptions en aplicaciones GTK# con Monodevelop

Hace unos días publiqué una entrada que mostraba el uso de excepciones personalizadas y como ejemplo de esa técnica un proyecto de consola de MonoDevelop que obtenía registros de una tabla en una base de datos PostgreSQL, derivado de ese ejemplo hice una aplicación en GTK# la cual muestra los mismos conceptos pero ahora con un formulario gráfico, por lo que hice algunas modificaciones con respecto al proyecto de consola de manera que pueda entenderse de una mejor manera.
Los pasos son:

  1. Ejecutamos MonoDevelop y creamos un proyecto GTK# llamado GtkPostException
  2. Descargamos y descomprimimos el código del ejemplo de excepciones de este enlace.
  3. Con el botón derecho encima del icono de la solución agregamos las siguientes clases a la solución.
  4. Book.cs
    BooksDataManager.cs
    DataBaseException.cs
    Logger.cs
    RuntimeException.cs
    
  5. Una vez agregadas las clases a la solución verificamos y en su caso editamos para que cada clase se agrupe dentro del namespace GtkPostException.
  6. Creamos un formulario con dos controles label, un control TextView (en donde teclearemos la Connection String), un control Treeviewdonde mostramos el resultado de la consulta y un botón de manera que el formulario tenga el aspecto visual de la siguiente imagen:
  7. Cambiamos el código de la clase BooksDataManager en el método SelectAll para que reciba un parámetro string con el valor de la Connection String recibida desde la interfaz de usuario. Aquí el listado completo de la clase:
  8. Agregamos al evento Clicked del Botón el metodo ExecuteQuery, cuyo código se muestra a continuación:
  9. protected virtual void ExecuteQuery (object sender, System.EventArgs e)
    {
    try{
    AddColumns(GridOutput);
    GridOutput.Model = CreateModel();
    lbmsg.Text += "Consulta ejecutada";

    }catch(GtkPostException.DataBaseException ex){
    MessageBox(ex.Message);

    }
    catch(GtkPostException.RuntimeException ex){
    MessageBox(ex.Message);

    }
    }
  10. El listado completo de la clase MainWindow se muestra a continuación:
  11. using System;
    using Gtk;
    using GtkPostException;

    public partial class MainWindow : Gtk.Window
    {
    public MainWindow () : base(Gtk.WindowType.Toplevel)
    {
    Build ();
    }

    protected void OnDeleteEvent (object sender, DeleteEventArgs a)
    {
    Application.Quit ();
    a.RetVal = true;
    }
    protected virtual void ExecuteQuery (object sender, System.EventArgs e)
    {
    try{
    AddColumns(GridOutput);
    GridOutput.Model = CreateModel();
    lbmsg.Text += "Consulta ejecutada";

    }catch(GtkPostException.DataBaseException ex){
    MessageBox(ex.Message);

    }
    catch(GtkPostException.RuntimeException ex){
    MessageBox(ex.Message);

    }
    }

    void MessageBox(string messageText){
    using(Dialog dialog = new MessageDialog(this,
    DialogFlags.Modal | DialogFlags.DestroyWithParent,
    MessageType.Error,
    ButtonsType.Ok,
    messageText,"")){
    dialog.Run();
    dialog.Hide();
    }
    }
    //Creamos los encabezados de las columnas
    void AddColumns (TreeView treeView) {
    CellRendererText rendererText = new CellRendererText ();
    string[] s = {"Titulo","Año","Páginas","Autores"};
    TreeViewColumn column;
    for(int i = 0;i < s.Length;i++){
    column = new TreeViewColumn (s[i], rendererText, "text", i);
    column.Resizable = true;
    column.MinWidth = 128;
    treeView.AppendColumn (column);
    }
    }
    //Creamos el modelo de datos
    ListStore CreateModel(){
    ListStore store = new ListStore(typeof(string),
    typeof(int),
    typeof(int),
    typeof(string));

    foreach(Book b in BooksDataManager.SelectAll(txtConnStr.Buffer.Text)){
    store.AppendValues(b.Title,b.PubYear,b.NumPages,b.Authors);
    }

    return store;
    }
    }

Si se compila correctamente al ejecutar la aplicación veremos una pantalla como la siguiente imagen:

Al presionar el botón OK mostrará los registros de la tabla conforme a la consulta, como en la siguiente imagen.

Hasta aquí el flujo normal de la aplicación o happy path,ahora vamos a ocasionar una excepción reemplazando el usuario postgres por el usuario sa el cual no existe en la base de datos hecho esto presionamos el botón para que se muestre la excepción personalizada en un cuadro de dialogo, esta excepción queda a nivel base de datos de ahí que lance una DataBaseException.

Este tipo de excepciones son registradas por PostgreSQL si el proceso del servidor se ejecutó desde una terminal como en la siguiente imagen:

Para generar la excepción a nivel aplicación en vez del valor de la Connection String tecleamos cualquier cadena no válida con lo que al presionar el botón nos mostrará el mensaje referente a la clase RuntimeException como en la siguiente imagen:

El detalle a nivel técnico de las excepciones originales se podrá consultar en el archivo log.txt el cual por lo general no es accesible al usuario final sino únicamente al personal técnico.
A continuación el enlace para descargar el ejercicio completo.

  Descarga el código fuente

Utilizando los objetos NpgsqlConnection y NpgConnectionStringBuilder para PostgreSQL con MonoDevelop

Hoy día es difícil imaginarse un sistema informático que no haga uso de fuente de datos para persistir información.
Entre estas fuentes de datos se encuentran:

  • Sin estructura: Archivos que no tienen un orden lógico. (Cartas, memos)
  • Estructurado, sin jerarquía: Contienen datos agrupados por separadores o indicadores de inicio y fin (archivos de acceso secuencial, archivos separados por tabuladores ó comas)
  • Jerárquica: tienen una estructura de nodos anidados. (XML)
  • Bases de datos relacionales:
  • Objetos: Los datos están organizados como objetos.

Debido a esta variedad, las clases de ADO para acceder y extraer cuentan con un diseño independiente y portable de la implementación de la fuente de datos que se utilice.
Desde luego, primeramente debemos tener acceso a la fuente de datos antes de ejecutar operaciones de consulta o modificación.
Para lograr este propósito usamos un objeto proveedor de conexión especifico según la fuente de datos a utilizar, cada uno de estos objetos son equivalentes en funcionalidades generales por derivar de la clase DBConnection.
Aunque estos objetos son equivalentes en la funcionalidad general, cada uno tiene código específico para su fuente de datos, como se muestra en la tabla siguiente.


+-----------------+-------------------+
| Fuente de datos |Objeto de conexión |
+-----------------+-------------------+
| SQL Server | SqlConnection |
+-----------------+-------------------+
| Suportan OLE DB | OleDbConnection |
+-----------------+-------------------+
| Oracle | Oracleconnection* |
+-----------------+-------------------+
| PostgreSQL | NpgsqlConnection |
+-----------------+-------------------+
*Depende de que el cliente de Oracle se encuentre instalado.

El objeto NpgsqlConnection

Un objeto conexión representa una conexión física a una fuente de datos, por lo que una de las mejores recomendaciones es conectarse y cerrar las conexiones abiertas en cuanto dejen de utilizarse.
ConnectionStrings (Cadenas de conexión)
Antes de trabajar con cualquiera de los objetos de conexión es indispensable proporcionarle la Connection String (cadena de conexión) , esta Connection String es propia de cada objeto Connection y cada objeto Connection es propio de cada fuente de datos con la que trabajemos.
La cadena de conexión es una serie de parámetros y valores separados por (;) y aunque cada Connection String cambie según el proveedor de Base de datos, hay ciertos parámetros que son siempre requeridos, como:


+---------------------------+-------------------------------------------------+
|Data Source o Server | El lugar donde se encuentra la fuente de datos: |
| servidor, máquina, lugar de red, IP, directorio,|
| archivo. |
+---------------------------+-------------------------------------------------+
DataBase o Initial Catalog | El nombre de la base de datos a usar. |
+---------------------------+-------------------------------------------------+
|User ID | El usuario para acceder. |
+---------------------------+-------------------------------------------------+
Password | El password del usuario para aceder |
+---------------------------+-------------------------------------------------+
Integrated Security | Si es true se utiliza la seguridad de Windows
| para acceder, si es false se debe de proporcionar
| el user y password para acceder.
+---------------------------+-------------------------------------------------+
Timeout o Connection Timeout| El tiempo que debe esperar la aplicación para |
| que el servidor le asigne una conexión. |
| El valor predeterminado es de 15 segundos |
+---------------------------+-------------------------------------------------+
Pooling | Crea un pool de conexiones para la cadena de |
| conexión si no existe, caso contrario |
| asigna una conexión de un pool existente. |
+---------------------------+-------------------------------------------------+
MinPoolSize | Número mínimo de conexiones por pool, valor |
| predeterminado 1 |
+---------------------------+-------------------------------------------------+
MaxPoolSize | Número máximo de conexiones por pool, valor |
| máximo permitido 100 |
+---------------------------+-------------------------------------------------+

El sitio Connection Strings.com contiene ejemplos y una amplia referencia a cada uno de los parámetros y valores según la fuente de datos.

El objeto NpgsqlConnectionStringBuilder

Hay una clase que nos ayuda a evitar errores de sintaxis al momento de construir cadenas de conexión, esto para no tener recordar cada una de las opciones en el caso de trabajar con diferentes bases de datos. Esta clase es similar a las clases Connection, hay una clase DbConnectionStringBuilder de donde surgen las objetos ConnectionStringBuilder específicos para cada cadena de conexión de cada objeto Connection.
Derivados de la clase DbConnectionStringBuilder, existe la clases NpgsqlConnectionStringBuilder la cual encapsula una ConnectionString (cadena de conexión) para PostgreSQL, la cual demostraremos en el siguiente programa junto con el uso de la clase NpgsqlConnection respectivamente.

Abrimos MonoDevelop, creamos una nueva solución GTK# y agregamos unos controles al formulario para que su aspecto luzca como en la siguiente imagen.

Como utilizaremos el proveedor de datos de PostgreSQL para .NET debemos agregarlo a la solución, hacemos click derecho en la solución(recomiendo la versión 2.x).

Aparecerá la ventana para agregar/quitar las referencias a los ensamblados instalados en el GAC o para buscarlos en el sistema de archivos.

Una vez agregado correctamente, se mostrará el ensamblado en la solución.

Dentro del método para dar funcionalidad al botón de aceptar, mostramos la utilización de la clase NpgsqlConnectionStringBuilder como ayuda para crear la cadena de conexión.

También dentro de este método tenemos el código para la creación de la conexión al servidor.


using(NpgsqlConnection conn = new NpgsqlConnection(connString.ToString())){
conn.Open();
if(conn.State == System.Data.ConnectionState.Open)
MessageBox("Conexión exitosa",MessageType.Info);
}

Si descargamos el código completo del proyecto, al compilarlo correctamente y ejecutarlo,nos pedirá teclear los parámetros para una conexión hacia un servidor PostgreSQL.

Si los parámetros son correctos y el servidor se encuentra disponible entonces la aplicación mostrará el mensaje siguiente:

En caso de un parámetro incorrecto nos mostrará los mensajes de la excepción recibida.

  Descarga el código fuente del proyecto para MonoDevelop

Utilizando la clase FtpWebRequest para un cliente básico FTP con MonoDevelop

Una de las tareas más habituales que existen en el tema de interacción entre sistemas es la copia de archivos de una máquina a otra, existen distintos mecanismos según el tipo de sistema operativo que se disponga entre la máquina destino y la máquina fuente, por ejemplo si se trata de sistemas UNIX/Gnu/Linux podemos utilizar el comando rcp o scp si se trata del servicio SSH.
Para el caso de copiar archivos entre sistemas operativos distintos la opción más común es utilizar el protocolo FTP (File Transfer Protocol) que es un protocolo orientado al usuario que hace uso de comandos para realizar operaciones con archivos en el servidor donde se establece la comunicación y para el cual .NET nos ofrece la clase FtpWebRequest que encapsula toda la funcionalidad de un cliente FTP y de los comandos propios del protocolo mediante los campos de la clase WebRequestMethods.Ftp.


+====================+========+==========================================+
| OPERACIÓN |COMANDO | DESCRIPCIÓN |
+====================+========+==========================================+
|AppendFile |APPE | Anexa un archivo a un archivo existente. |
+--------------------+--------+------------------------------------------+
|DeleteFile | DELE | Elimina un archivo existente. |
+--------------------+--------+------------------------------------------+
|DownloadFile | RETR | Descarga un archivo existente. |
+--------------------+--------+------------------------------------------+
|GetDateTimestamp | MDTM | Obtiene el date-time stamp de un archivo.|
+--------------------+--------+------------------------------------------+
|GetFileSize | SIZE | Obtiene el tamaño de un archivo. |
+--------------------+--------+------------------------------------------+
|ListDirectory | NLIST | obtiene un listado corto de los archivos |
| de un directorio en el servidor. |
+--------------------+---------------------------------------------------+
|ListDirectoryDetails| LIST | Obtiene un listado detallado de los |
archivos de un directorio en el servidor.|
+--------------------+--------+------------------------------------------+
|MakeDirectory | MKD | Crea un directorio en el servidor. |
+----------------------+------+-----------------------------------------+
|PrintWorkingDirectory | PWD | Imprime el nombre del directorio |
de trabajo. |
+----------------------+------+-----------------------------------------+
|RemoveDirectory | RMD | Elimina un directorio. |
+----------------------+------+-----------------------------------------+
|Rename | RENAME| Renombra un directorio. |
+----------------------+-------+----------------------------------------+
|UploadFile | STOR | Envia un archivo al servidor. |
+------------------------+-----+----------------------------------------+
|UploadFileWithUniqueName|STOU |Envia un archivo con |
un nombre unico al servidor. |
+------------------------+-----+----------------------------------------+

Y que ejemplificaremos a continuación programando un cliente FTP básico con MonoDevelop.
Ejecutamos MonoDevelop y diseñamos un formulario GTK# como se muestra en la siguiente imagen:

Agregamos una nueva clase a nuestro proyecto con el nombre de FtpDAO, que es donde se encontrarán los métodos que darán la funcionalidad a nuestro formulario.
En esta clase hacemos referencia a los siguientes ensamblados:


using System.IO;
using System.Net;

Y completamos la funcionalidad de la clase con los siguientes métodos.
Para listar los archivos del directorio en el servidor, escribimos el siguiente método:

Que nos regresa la lista de los archivos en un objeto ListStore que servirá de modelo para nuestra interfaz gráfica.
Con el siguiente código establecemos un Uri con la dirección IP o Host y con el directorio al cuál accedemos.


string.Format("ftp://{0}/{1}/",txtServer.Text,txtDir.Text);

La clase Uri sirve para encapsular un URI (Uniform Resource Identifier) que es una manera de identificar
Sin ambigüedades un recurso en Internet, puede ser un archivo, una dirección email o una dirección IP con su protocolo. Hay dos tipos de URIs: URL (Universal Resource Location) o URN (Universal Resource Names), en C# la clase Uri nos brinda todos los métodos y propiedades para el manejo de URI’s.
En nuestro ejemplo será la URL del servidor de FTP, como se utiliza con la línea.

Uri serverUri = new Uri(string.Format("ftp://{0}/{1}/",txtServer.Text,txtDir.Text));

Donde concatenamos el protocolo con la IP o nombre del servidor y el directorio donde mostramos el listado, para después seleccionar el archivo a descargar.

C# WebRequest y WebResponse

La clase System.Net.WebRequest representa una petición al servidor y la clase System.Net.WebResponse representa la respuesta del servidor como un flujo de datos o StreamReader.
Usamos el método estático Create de WebRequest para solicitar el recurso del servidor dependiendo del URI que le pasemos como parámetro.

_ftprequest = (FtpWebRequest)WebRequest.Create(serverUri);

Usamos el método GetResponse para obtener la respuesta como un flujo de datos y asociándola a un objeto StreamReader.

using(FtpWebResponse listResponse = (FtpWebResponse)_ftprequest.GetResponse()){
using(StreamReader reader = new StreamReader(listResponse.GetResponseStream())){

Podemos establecer el timeout de la respuesta en milisegundos con la propiedad timeout que tiene un valor predeterminado de 10000.
La funcionalidad de descargar un archivo se la damos con el siguiente método.

Al construir y ejecutar la aplicación veremos los resultados como en las siguientes imágenes.

Al oprimir el botón Aceptar después de teclear la IP, el directorio, el usuario y el password, si son válidos para el servidor se muestra el listado de los archivos.

Si activamos la casilla de Listado detallado mostrará el listado detallado de los archivos.

En caso de que ocurra un error en la conexión con el servidor, se mostrará un mensaje con la excepción correspondiente.

Si seleccionamos un archivo de la lista de archivos y pulsamos el botón guardar se solicitará el archivo del servidor y se guardará en el disco duro en el directorio donde se ejecute la aplicación.

En la siguiente imagen la aplicación se conecta a un sitio público habilitando la casilla de autenticación anónima.

 Descarga el código fuente del proyecto para MonoDevelop

Utilizando la clase WindowsPrincipal con MonoDevelop

En este post Conceptos Básicos de seguridad en .NET (parte II) muestro un par de programas ejemplificando acerca de la utilización de la clase WindowsPrincipal. Esta clase representa la información de autenticación y de autorización de una entidad dentro del sistema.
Estos ejemplos pueden compilarse y ejecutarse en Linux, en este caso lo hice en un sistema operativo OpenSuse versión 11.1, con el compilador 2.6.7 y el runtime de mono versión 2.6.7, como se muestra en la siguiente imagen:

El primero de los dos ejemplos lo compilamos y lo ejecutamos mostrando el resultado como en la siguiente imagen:

En la siguiente imagen mostramos el resultado si lo ejecutamos como el superusuario root.

En el caso del segundo ejemplo modificamos el código y cambiamos la interfaz de consola por una interfaz gráfica utilizando el diseñador GTK# de MonoDevelop, siguiendo los siguientes pasos:

  1. Creamos una solución con un proyecto GTK# 2.0 en Monodevelop como en la siguiente imagen:

  2. Modificamos la apariencia del formulario, agregamos dos botones btnEnter, btnClose y un textview nombrado txtInfo para que el formulario se vea como en la siguiente imagen:

  3. Agregamos el método para el evento Clicked del botón btnEnterutilizando la pestaña señales en la ventana de propiedades, seleccionamos el evento y hacemos doble click para que MonoDevelop genere el código del método, como se muestra en las siguientes imagenes:

  4. Agregamos las siguientes líneas al inicio del código de la clase MainWindow.cs , para utilizar los miembros de los ensamblados System.Security.Principal y System.Threading respectivamente.


    using System.Security.Principal;
    using System.Threading;
  5. Dentro del método OnBtnEnterClicked del código generado por MonoDevelop para el evento del botón btnEnter, escribimos el siguiente código, el cual contiene toda la funcionalidad de nuestro programa:

    Nuestro programa entonces se vera como en la siguiente imagen:

  6. Ahora creamos el código del evento Clicked para el botón btnClose de la misma forma que lo hicimos para el botón btnEnter, cuya acción será terminar el programa, por lo que escribimos el siguiente código dentro del método generado.


    protected virtual void OnBtnCloseClicked (object sender, System.EventArgs e)
    {
    Application.Quit();
    }

  7. Para compilar el programa hacemos click en el submenú Construir todo debajo del la opción Construir en el menú principal. En caso de ejecutar el programa hacemos click en cualquiera de las opciones Ejecutar o Depurar bajo el menú Ejecutar en el menú principal.

  8. Si el programa se compila correctamente al ejecutarse mostrará el resultado como en la siguiente imagen:

  9. Si ejecutamos el programa desde la terminal como superusuario nos mostrará el resultado como en la siguiente imagen:

Vemos en el resultado de la ejecucción que el metódo de autenticación es POSIX.

 Descarga el código fuente del proyecto para MonoDevelop

Trabajando LINQ para XML con Monodevelop Parte II de II

Esta es la segunda parte del post anterior Trabajando LINQ para XML con Monodevelop, aunque este tutorial supone que los dos proyectos están dentro la misma solución, los dos proyectos son independientes uno del otro, por lo que pueden crearse en diferentes soluciones.

Consultando XML con LINQ

Ahora agregamos un segundo proyecto a nuestra solución, este proyecto será una aplicación GTK# que llamaremos “SegundoLinqXML” como se muestra en la siguiente imagen y que nos mostrará como consultar el archivo XML con LINQ creado con la aplicación de consola anterior.



Utilizando el diseñador agregamos y acomodamos los siguientes controles GTK# con sus respectivos identificadores al formulario.

Tipo de control:Gtk.Entry NombretxtQuery

Tipo de control:Gtk.Button NombreBtnBuscar

Tipo de control:Gtk.RadioButton NombresRbCodigo,rbIdArticulo,rbNombre

Tipo de control:Gtk.TreeView NombresResultados

Colocando los controles el formulario deberá verse como en la siguiente imagen

El código completo es el siguiente:

El siguiente fragmento de código muestra como realizar la consulta del archivo XML donde la variable col adquiere el valor de cualquier elemento dentro del elemento table y donde el valor contenga el texto escrito dentro de txtQuery, es aquí donde en esencia se utilizan las expresiones de consulta de LINQ una vez que se carga el documento XML en memoria.

Construimos la aplicación pulsando el botón F8, seleccionamos el archivo de proyecto y haciendo click con e botón secundario del ratón seleccionamos la aplicación para que se inicie al ejecutar la solución, al ejecutarla teclear un valor y pulsar el botón consultar observaremos cualquiera de los siguientes resultados dependiendo del control radio seleccionado.




 Descarga el código fuente

Trabajando LINQ para XML con Monodevelop Parte I de II

XML se ha convertido en un excelente estándar abierto para el intercambio de información entre aplicaciones de software debido a la manera de representar información estructurada, independiente de la plataforma, lenguaje de programación o sistema operativo, es utilizado extensamente en archivos y protocolos de Internet por ser un formato fácil de leer y suficientemente flexible para adaptarse a muchos propósitos. Debido a estas características XML y sus tecnologías están completamente integradas y soportadas por la plataforma .NET mediante un conjunto de clases que proporcionan lectura y escritura de documentos, validación, navegación, serialización, transformación y búsquedas. Aunque estas clases son suficientes para trabajar con XML su utilización no es sencilla y puede producir código difícil de mantener, por lo que .NET incorpora LINQ para XML (LINQ to XML) como una mejor manera de trabajar con XML.

Creando el archivo XML

Para mostrar de una manera práctica la utilización de LINQ para XML, ejecutamos MonoDevelop y creamos una nueva solución en el menú “Archivo”, del lado derecho de la ventana de nueva solución seleccionamos el icono que dice “Solución en blanco” y en el cuadro de texto nombre escogemos “LinqXML”, esta será la solución que contenga los dos proyectos de este tutorial.


Agregamos a nuestra solución un proyecto de consola, le llamamos CrearXML , como se muestra en la siguiente imagen.


Para que el soporte de LINQ a XML es importante hacer referencia a los siguientes ensamblados:

System.Linq Es el ensamblado principal y básico que contiene todas las clases e interfaces para trabajar con LINQ.

System.Xml.LinqContiene las clases y extensiones para manipular XML con LINQ.

Esto se logra haciendo click derecho sobre el archivo de proyecto y en el menú desplegable escogemos la opción “Editar referencias” (Edit References) como se muestra en la imagen siguiente.


Ahora completamos el código generado por monodevelop, para que el programa completo quede como en el siguiente listado.

La diferencia de la sintaxis para crear documentos XML con respecto a la sintaxis empleada con las clases del ensamblado System.Xml, es que esta sintaxis se enfoca más en los elementos (representados por la clase XElement) que en el documento (representado en la clase Xdocument ).
En este listado creamos el documento XML de una manera declarativa anidando varias objetos XElement dentro de un objeto Xdocument , al final llamamos al método Save para guardar el XML en el disco.
Construimos la aplicación pulsando la tecla F8 y al ejecutarse veremos el resultado en la ventana de salida de MonoDevelop como se muestra en la imagen siguiente.

 Descarga el código fuente