Utilizando JavaScript desde código ASP.NET

Actualmente es difícil construir una aplicación ASP.NET que no haga uso de las capacidades de JavaScript para realizar operaciones del lado del cliente o browser, operaciones que si fueran desarrolladas en su totalidad con la arquitectura ASP.NET serian demasiado costosas, basta recordar que ASP.NET es una arquitectura totalmente hecha para su uso del lado del servidor, por lo que hace uso de un mecanismo de Postback o de ida y vuelta entre los datos de la página solicitada por el cliente y por el servidor que proporciono la página, por lo que el mecanismo de Postback hace uso de uno de los recursos más valiosos de una red: el ancho de banda.

Durante ese viaje pueden ocurrir eventos que retarden o interrumpan la respuesta del servidor, causando una pérdida o una retrasmisión de los datos por parte del usuario resultando en una forma ineficiente de comunicación ya que ASP.NET reconstruye la página completa con todo su contenido en cada solicitud.

Para ayudar a no utilizar con frecuencia este mecanismo, hay que utilizar JavaScript en el cliente o browser como un complemento a las operaciones ASP.NET. Para utilizar JavaScript junto a ASP .NET, existe la clase ClientScriptManager la cual se programa mediante la propiedad ClientScript de la clase Page, la cual expone métodos que permiten incrustar código JavaScript dentro de la página ASP.NET.

Los métodos más comunes usados de esta clase son:

  1. RegisterClientScriptBlock: Incrusta el código JavaScript al inicio del formulario después de la etiqueta
    , genera dinámicamente el código JavaScript desde una cadena.
  2. RegisterStartupScript: Incrusta el código JavaScript en el final de la página, antes del cierre del formulario o sea antes del cierre de la etiqueta

    , por lo que se recomienda para operaciones con controles HTML o ASP.NET, ya que hace referencia a ellos una vez cargados.

  3. RegisterClientScriptInclude: sirve para ejecutar el código JavaScript que se guarda en un archivo externo (comúnmente con extensión .js) tiene dos cadenas como argumentos un nombre para identificar el script y el archivo que contiene el código JavaScript.

Como ejemplo mostraremos una página ASP.NET que utiliza dos listas de usuarios (ListBox) y dos botones (HTML input) que se utilizan para mover los usuarios de una lista hacia otra.
Los eventos son controlados mediante código JavaScript, esto sin utilizar código JavaScript incrustado en la página HTML, sino que el código JavaScript se genera desde código ASP.NET.
A continuación, el código Default.aspx:



El código ASPX/HTML de este ejemplo no crea funcionalidad alguna por lo que explicaremos la funcionalidad contenida en el código C#, en el código Default.aspx.cs.



Primero creamos el código JavaScript que tendra la funcionalidad para agregar los elementos de una lista hacia la otra, esto se logra con la función FuncCopyItem(string name, string source, string destiny) esta función recibe tres argumentos el nombre de la función JavaScript que se creará en el código HTML al solicitar la página y con la cuál se identificaran los eventos de los controles , el control ListBox fuente de donde inicialmente se tomarán los items y por último el control ListBox destino a donde se pondrán los items.
Con la siguiente línea de código:

    ClientScriptManager cs = Page.ClientScript;

Obtenemos el objeto ClientScriptManager de la página, con él cual utilizaremos el metódo cs.RegisterStartupScript con sus parámetros correspondientes, algo importante por lo cuál usamos este metódo es por que para construir el código JavaScript utilizamos la propiedad ClientID del control ListBox, es decir el control debe existir obligatoriamente de lo contrario, cuando ASP .NET contruya la página contruirá el código JavaScript con estos parámetros nulos lo que causará un error no en la página ASP.NET sino en el código JavaScript cuando se llame para ejecutarse.

Fig 1 La vista del proyecto en Monodevelop.

Fig 2 Ejecutando la aplicación ASP.NET en Internet Explorer 9.0

Fig 3 Ejecutando la aplicación ASP.NET en Firefox.

Fig 4 Mostrado el código JavaScript en la página generada por ASP.NET.

Descarga el proyecto para MonoDevelop

Entendiendo Logging en Aplicaciones .NET con Log4net

Esta entrada se relaciona con esta entrada anterior, que trata también sobre el tema de Logging.
Log4net es opción altamente recomendable para la implementación del logging en las aplicaciones desarrolladas para .NET. Sobre todo cuando se necesita una solución más configurable y robusta que la proporcionada por las clases del ensamblado System.Diagnostics, que en comparación con log4net están en un nivel elemental, si necesitamos una herramienta que soporte diferentes fuentes de persistencia, que no afecte el desempeño de las aplicaciones y que sea transportable entre la implementación .NET de Microsoft y la del proyecto Mono.

Log4net es un Framework open source creado por la fundación Apache basado en la implementación de los servicios de logging existentes en log4j, un componente de logging usado durante años en los ecosistemas Java.
La arquitectura de Log4net puede resumirse en tres clases principales cada una encargada de una responsabilidad dentro del Framework y que se detalla a continuación:

  • Logger Captura la información para la bitácora.
  • Appender Publica la información hacia diversas fuentes de logging configuradas en la aplicación. Al menos debe definirse un Appender, el Appender predeterminado es el ConsoleAppender cual dirige su salida hacia una terminal de Consola.
  • Layout Se utiliza para darle formato haciendo legible cada salida de los distintos Appenders.

Para más información consultar http://logging.apache.org/log4j/1.2/manual.html aunque es para log4j la arquitectura es idéntica a log4net.

Como ejemplo del uso de log4net vamos a crear dos programas un cliente y un servidor los cuales se comunicarán entre sí, la clase ServerProgram es un servidor TCP que escucha en el puerto 6060 y la clase Program que es el cliente que al conectarse con el servidor, le solicita al usuario el nombre de un archivo de texto el cuál sera leído y cada línea de texto será enviada hacia el servidor. El programa cliente implementa todo el código básico para el manejo del logging con log4net.

Código de la clase ServerProgram


Código de la clase Program (cliente)



El archivo de configuración App.config en donde van las opciones de configuración de Log4net



En el archivo de configuración utilizamos el declarado en la siguiente sección

<appender name="FileAppender" type="log4net.Appender.FileAppender">
<file value="log.txt"/>
<appendToFile value="true"/>
<layout type="log4net.Layout.PatternLayout">
<conversionPattern value="[%date{dd-MM-yyyy HH:mm:ss}] [%level] %message %newline"/>
</layout>
</appender>

Declaración del componente principal para implementar los métodos del Logger.

static readonly log4net.ILog log = log4net.LogManager.GetLogger(
System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

carga la configuración del archivo de configuración para Log4net.

XmlConfigurator.Configure();

Escribir una excepción al log.

log.Error(ex.Message, ex);

Escribir información al log

log.Info("Ejecutando la aplicación en " + DateTime.Now.ToLongTimeString());

Compilando los programas:

Ejecutando el servidor

Ejecutando el cliente

Generando excepción de formato

Generando excepción de comunicación

Revisando el archivo de log, creado por log4net

Descarga el código fuente del programa Server.

Descarga el código fuente del programa cliente.

Utilizando Windows Management Instrumentation (WMI) con Microsoft .NET

Para las aplicaciones que necesiten implementar una solución de un inventario total o parcial del hardware de la computadora, existe una interfaz de programación llamada WMI (Windows Management Instrumentation) a la cual puede accederse mediante lenguajes scripting como VBScript, Perl,Power Shell o cualquier lenguaje diseñado para el .NET Framework que haga uso de los ensamblados System.Management y System.Management.Instrumentation respectivamente.

Windows Management Instrumentation (WMI) es un conjunto de extensiones del Modelo de drivers de Windows WDM (Windows Driver Model) que es un modelo uniforme de clases que representan los valores del hardware y sistema operativo de la computadora,WMI es además la implementación Microsoft de Web-Based Enterprise Management (WBEM) un estándar de la industria que agrupa tecnologías desarrolladas para unificar la administración de los ambientes de computo, por lo que WMI es compatible con WBEM y provee el soporte para el Common Information Model (CIM) que es el modelo que describe los objetos de administración.

Un inventario de hardware con WMI se realiza mediante búsquedas en el repositorio o base de datos de WMI utilizando un lenguaje de consultas conocido como WQL (WMI Query Language) para encontrar los valores de ciertas propiedades del Hardware. WQL es un subconjunto de SQL.

WMI trabaja dentro del contexto de un ensamblado, siendo el predeterminado root\cli que controla las propiedades y métodos que están disponibles en WMI, la seguridad para el WMI se configura a través del control WMI (wmimgmt.msc), de forma predeterminada los usuarios tienen permisos para consultar el proveedor WMI en una computadora local mediante el comando WMIC, desde la línea de comandos del command prompt.

A continuación las clases de un proyecto de consola que imprime información del hardware del equipo.

1-.Una clase PC que será el objeto de transporte

2-.Una clase PCManager que obtendrá algunos de los valores del Hardware para este ejemplo, mediante consultas WQL del repositorio WMI similar a las consultas SQL para en base de datos.

3-.Finalmente el programa principal que despliega información del equipo.

El resultado de la ejecución del programa se muestra en la siguiente imagen:

Los puntos claves del programa utilizados por la clase PCManager son

  1. Utilizar los ensamblados System.Management y System.Management.Instrumentation.
  2. Crear una instancia de la clase ManagementObjectSearcher
  3. Encapsular cada consulta dentro de la clase ObjectQuery
  4. Obtener los resultados de la búsqueda en una colección de ManagementObject representada por la clase ManagementObjectCollection, para iterar dentro de la colección e imprimir los valores.

También se puede mostrar información desde una página ASP.NET como en el siguiente listado:

El resultado de la ejecucción de la página:

  Descarga el código fuente del proyecto para consola

  Descarga el código fuente del proyecto ASP.NET

Entendiendo Logging en Aplicaciones para .NET Framework

El logging es un proceso recomendado durante la etapa de pruebas e indispensable durante la etapa de liberación o puesta en producción de aplicaciones de software, logging se refiere a la utilización de una bitácora, registro o log, donde guardemos la información de los diferentes eventos que genero la aplicación entre el tiempo de arranque y durante el tiempo de ejecución.
El registro o log debe proporcionarnos toda información necesaria para realizar con la aplicación las siguientes actividades:

  1. Mantener el rastro de todos sus estados (recording)
  2. Depurar (debugging)
  3. Auditar (audit)
  4. Diagnosticar su estado actual (tracing)

Un factor importante a considerar durante el logging es el performance de la aplicación ya que es un hecho que se incrementará el tiempo de las operaciones de escritura, sobre todo si el logging es requerido para una auditoría, este incremento en el performance se explica de la siguiente manera:

Si una operación del proceso de negocio emplea 5 segundos para ejecutarse y producir un resultado, con el logging necesitará 2 o 3 segundos adicionales para registrarse en el log, por lo que ahora el tiempo es de 7 u 8 segundos para el total de la operación.

El siguiente fragmento de código ilustra este factor:

try
{
//operación de negocio 5 segs.
var output = BussinessObject.ExecuteOperation();
//registro en el log 3 segs.
Logger.WriteLine(output);
//El tiempo total de la transacción es de 8 segs.

}catch(Exception e){

//No está incluído dentro de la transacción.
Logger.WriteLine(e.Message);

}

Hay muchas formas de implementar logging en las aplicaciones, esto varía dependiendo del tipo de aplicación y del requerimiento a satisfacer la forma más sencilla es mediante el uso de las clases contenidas en el ensamblado System.Diagnostics. específicamente con las implementaciones derivadas de la clase TraceListener las cuales son responsables de desplegar o de guardar los mensajes generados por las clases Trace y Debug, hay tres implementaciones de esta clase:

  1. TextWriterTraceListener escribe mensajes a cualquier clase que se derive de Stream
  2. EventLogTraceListener escribe los mensajes a el Event Viewer de Windows.
  3. DefaultTraceListener escribe los mensajes a la ventana de salida (Output window).

El siguiente listado muestra el uso de TextWriterTraceListener y EventLogTraceListener, la lógica del programa es simple, si el programa tiene permisos administrativos usará el event viewer como log, de lo contrario usará un archivo de texto, realizando un intercambio entre los Tracelisteners: EventLogTraceListener y TextWriterTraceListener respectivamente.

Al compilar el listado se debe de incluir la opción /d:TRACE como se muestra en la siguiente imagen:

Al ejecutar el programa mostrará un resultado como en la siguiente imagen:

Con el siguiente código el programa intercambia entre Listeners dependiendo si pertenece al grupo con permisos de administración o no.

Si no pertenece al grupo de administradores utiliza como log un archivo de texto log.txt caso contrario utiliza el Event Viewer (visor de eventos de Windows) como se muestra en la siguiente imagen:

Lo más recomendable para el logging es la utilización de componentes como el Logging Application Block de Microsoft o el Log4net de la fundación Apache.

Download el código fuente para Xamarin Studio o Visual Studio

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

Uso de Custom Exceptions (excepciones personalizadas) en .NET parte II

En la entrada anterior del blog, me olvide de publicar el archivo App.config donde se encuentra la cadena de conexión (ConnectionString) y se accede a su valor mediante la siguiente línea en la aplicación y se asigna a la variable connStr.



En caso de ser necesario y de tener algún incoveniente en construir la solución aqui dejo el enlace para descargar el código fuente.

  Descarga el código fuente