sábado, 21 de julio de 2012

Implementando Caching con PostSharp mediante AOP

En computación, la cache es un conjunto de datos duplicados de los valores originales almacenados en otra parte, donde los datos originales son más difíciles de conseguir por el hecho de que lleva un mayor tiempo de procesamiento comparando al esfuerzo que se toma al leerlo solamente en la cache. Podemos indicar además, que una cache es una zona temporal de almacenamiento donde en varias ocasiones se acceden a los datos para un rápido acceso. Desde el momento en que los datos estén guardados en la cache, se pueden utilizar a partir de entonces accediendo a la misma sin la necesidad de acceder a la fuente original de los datos, de tal forma a evitar el reprocesamiento en la obtención de los mismos.

El uso de la cache ha sido comprobado y analizado, resultando ser sumamente efectivo en muchas áreas de la computación por el hecho de que el modelo de acceso en usos de computadores típicos se maneja a través de direccionamiento por referencias o posiciones de memoria. Existen varias clases de lugar o posicionamiento, pero aquí nos centramos específicamente con los datos que se sitúan cerca en el tiempo (el lugar temporal). Los datos podrían estar o no localizados físicamente cerca uno del otro (el lugar espacial).

Como ya lo explicamos la cache es un espacio de almacenamiento temporal, es decir, es un bloque de memoria que se utiliza para recuperar ciertos datos más rápidamente. El procesador y el disco duro frecuentemente utilizan la cache, además de los navegadores web y servidores web.


Una cache está compuesta de un conjunto de entradas. Cada entrada contiene un dato que es una copia del dato que está en su lugar original, a su vez, cada entrada tiene una etiqueta, que especifica la identidad del dato del lugar original del cual la entrada es una copia.


Cuando el cliente de la cache (procesador, navegador web, sistema operativo) desea utilizar un dato presumiblemente en la memoria, este primero busca en la cache. Si una entrada puede ser encontrada coincidiendo con una etiqueta, el cual es el dato buscado, el dato de la entrada se utiliza en su reemplazo. Esta situación es conocida como hit cache. Para ir a un ejemplo más simple y concreto, un programa del navegador web puede revisar la cache local en el disco para verificar si este tiene una copia local del contenido de la página web de un enlace URL en particular. En este ejemplo la URL es una etiqueta y el contenido de la página web es el dato. El porcentaje de acceso que resulta en la cache hit es conocido como hit rate de la cache.


La situación alternativa, sucede cuando la cache es consultada y no contiene el dato de la etiqueta deseada, a este fenómeno se le denomina cache miss (pérdida de la cache). El dato previo no cacheado se recupera del lugar original durante el proceso en donde ocurre el cache miss, aquí el dato es comúnmente copiado a la cache, para que en las próximas búsquedas ya se encuentre el dato en la cache.


Podemos nombrar algunos tipos de cache existentes tales como: Cache de procesador, Cache de disco, Cache de web y otros.

A continuación demostramos el código en C# .net valiéndonos de la utilidad del caching:


Ejemplo de Caching en POO
private const string KeyNumber = "RandomNumber";
private int GetNumber()
{
       int value;
       if (!CacheHelper.Get(KeyNumber, out value)
      {
              value = DataAccess.GetNumberRandom();
              CacheHelper.Add(value, KeyNumber);
       }
       return value;
}

Podemos observar que en el método GetNumber se devuelve un número entero y dentro de la lógica mediante !CacheHelper.Get se verifica si el valor que se quiere obtener ya se encuentra en la cache de ser así entonces automáticamente se retorna dicho valor, sin embargo si es que no se encuentra en la cache se obtiene el valor entero de la base de datos por medio de DataAccess.GetNumberRandom() y se agrega a la cache por única vez con el método CacheHelper.Add para que en las sucesivas peticiones del dato este se encuentre ya en la cache.

La implementación de caching con el framework PostSharp quedaría de la siguiente manera:

Ejemplo de Caching en AOP con PostSharp
private const string KeyNumber = "RandomNumber";
// Se pasa la clave asociada con el item cacheado en el constructor
[Cache(KeyNumber)]
private int GetNumber()
{
       /* Esta lógica es ejecutada solamente cuando el atributo Cache lo amerita*/
       return DataAccess.GetNumberRandom();
}


Por último se define el cuerpo del aspecto en el siguiente fragmento de código:

Definición de Aspecto de Caching en AOP con PostSharp
[Serializable]
public sealed class CacheAttribute : OnMethodInvocationAspect
{
       private readonly string key;
       public CacheAttribute(string key)
       {
              this.key = key;
       }
     
       public override void OnInvocation(MethodInvocationEventArgs context)
      {
              object value;
              if (!CacheHelper.Get(key, out value))
             {
                    /* Se hace la consulta basada en la lógica del llamador*/
                    value = context.Delegate.DynamicInvoke();
                    CacheHelper.Add(value, key);
               }
               context.ReturnValue = value;
       }
}

En esta sección de código en la definición del aspecto de caching, podemos indicar que el método OnInvocation se encarga de verificar a través de !CacheHelper.Get si el valor numérico se encuentra o no en la cache y de esta manera decide si ir a buscar o no en la base de datos.

miércoles, 9 de mayo de 2012

Refactorizando código de sección crítica con AOP y PostSharp

La sincronización expuesta aquí se basa en los procesos y nos referimos a ella por el hecho de que esta otorga varias técnicas de coordinación de hilos que se ejecutan en forma simultánea y procesos para poder completar una determinada tarea con el objetivo de que estas se ejecuten en un orden establecido y que no se dé lugar a eventos inesperados.


Mencionamos a continuación en forma general los tipos de sincronización existentes: Barrier, Bloqueo/Semáforo (Lock/Semaphore), Unión de hilos (Thread join), Exclusión Mutua (Mutex), Monitores (Monitors), Sincronización no bloqueante (Non-blocking synchronization), Rendezvous, Contadores de eventos y secuenciadores (Event counters and sequencers).

Utilizaremos el tipo de sincronización basado en Exclusión Mutua de acuerdo al código de ejemplo que expondremos seguidamente, dicho ejemplo se encuentra codificado en C# .net sin la utilización de aspectos:


Ejemplo de Sincronización sin AOP para Exclusión Mutua

public void SynchronizedMethod()
{
        lock(this)
       {
               //.. hacer algo, esta es una sección //crítica de código o de exclusión mutua
       }
}

Y más abajo exponemos lo mismo pero en su presentación en AOP a través de un atributo llamado Synchronized:


Ejemplo de Sincronización en AOP para Exclusión Mutua con PostSharp

[Synchronized]
public void SynchronizedMethod()
{
         //.. hacer algo, esta es una sección crítica de //código o de exclusión mutua
}

Básicamente la idea es hacer un código más elegante y a la vez más fácil de leer por parte de algún programador y mediante la utilización de los atributos se logra este objetivo. Finalmente colocamos la definición del aspecto de sincronización en el fragmento de código siguiente valiéndonos del framework PostSharp orientado a AOP:

Definición de Aspecto de Sincronización en AOP con PostSharp

[Serializable]
public class SynchronizedAttribute : OnMethodBoundaryAspect
{
         public override void OnEntry(MethodExecutionEventArgs eventArgs)
        {
                Monitor.Enter(eventArgs.Instance);
        }

        public override void OnExit(MethodExecutionEventArgs eventArgs)
       {
               Monitor.Exit(eventArgs.Instance);
       }
}


Con esto nos ahorramos el hecho de colocar en cada método la palabra clave lock, el cual garantiza la ejecución de una sección crítica de código o exclusión mutua también denominada, una sección crítica de código es aquella que no permite ser ejecutada al mismo tiempo, debido a que puede darse el caso de que haya lectura sucia o que al mismo tiempo dos o más hilos de ejecución traten de modificar el valor de una variable, por lo tanto se bloquea dicha sección de código en forma exclusiva en un ambiente concurrente, no permitiendo de esta forma a que otro hilo trate de ejecutar esa sección crítica en un instante dado.

Además al evitar repetir esta palabra clave nos aseguramos de no repetir código por un lado y por otro de separar la funcionalidad básica del aspecto de sincronización.

En la definición del aspecto de sincronización expuesto más arriba, podemos decir que existen dos métodos, el primero llamado OnEntry el cual habilita la entrada a la ejecución del código de sección crítica y se utiliza la clase Monitor que provee un mecanismo para sincronizar el acceso a los objetos y el segundo método llamado OnExit, que se usa para realizar el abandono de la sección crítica de la ejecución del código, por lo tanto se invoca al método Exit de la clase Monitor.




sábado, 5 de mayo de 2012

Direccionando el concepto de Tracing con AOP y PostSharp

En ingeniería de software, el tracing es una clase especializada de logging para recolectar información acerca de un programa en ejecución. Comúnmente esta información es utilizada por los programadores con el objetivo de depurar el código y dependiendo sobre todo del tipo y la información contenida en un log de traza (trace log), otras personas, tales como los administradores de sistemas o personal de soporte técnico pueden realizar detecciones y posibles soluciones a problemas comunes con algún software utilitario.


Con la siguiente tabla se trata de establecer algunas diferencias entre los conceptos de Tracing y Logging:

Diferencias entre Logging y Tracing


LoggingTracing
 Utilizados principalmente por administradores de sistemas. Utilizados principalmente por programadores.
 Registra informaciones de alto nivel y más amigables (ej. Instalación fallida de un programa)  Registra informaciones de bajo nivel y más crípticas (ej. Lanzamiento de una excepción)
 No debe ser “ruidoso”, es decir, no puede contener duplicación o información no relevante para la persona que lo va a auditar.  Puede ser “ruidoso”, pues generalmente se utiliza con fines de depuración.
 El formato de salida de la información debe o requiere ser estándar.  Pocas limitaciones en cuanto al formato de salida de la información.
 Los logs de eventos son a menudo localizadas, es decir, están expuestos en el idioma correspondiente a la región o país dado.  La localización no es una preocupación, comúnmente en las herramientas de tracing y los entornos de desarrollo en general se utilizan el idioma inglés.


A continuación presentamos un código sencillo de tracing en C# .net, lo que se expone aquí son dos trazas, una cuando ingresa al método realizando la traza del nombre del método y sus parámetros de entrada y otra al finalizar el método utilizando además otros parámetros:

Ejemplo de Tracing sin PostSharp

public bool Method(params string[] inputValues)
{
       Trace("Enter", "MethodName", inputValues);
       // Traza todos los parámetros de entrada al //ingresar al método
       bool someValue;
       Trace("Exit", "MethodName", someValue);
       // Traza los parámetros al abandonar el método
       return someValue;
}

En contra partida exponemos la diferencia con el mismo método (función) con PostSharp, podemos notar el mejor encapsulamiento de este aspecto no funcional, reduciendo las líneas de código y separando por ende las incumbencias funcionales y no funcionales y no tener además la necesidad de llamar al método de Trace tanto cuando ingresa al método o como cuando se acaba la ejecución del método:


Ejemplo de Tracing en AOP con PostSharp

[Trace]
public bool Method(params string[] inputValues)
{
         //hacer algo
}


La declaración y definición del aspecto de tracing se indica en el trozo de código siguiente:

Definición de Aspecto de Tracing en PostSharp


[Serializable]
public class TraceAttribute : OnMethodBoundaryAspect
{
          public override void OnEntry(MethodExecutionEventArgs eventArgs)
         {
                   Trace.TraceInformation("Ingresando {0}.", eventArgs.Method);
         }

         public override void OnExit(MethodExecutionEventArgs eventArgs)
        {
                   Trace.TraceInformation("Saliendo {0}.", eventArgs.Method);
        }
}

En esta definición del aspecto implementado en PostSharp podemos observar dos métodos sobrescritos (overrides) una para detectar la entrada al método con OnEntry y otra para la salida del mismo con OnExit y por ende evitar las llamadas del método Trace, cuando uno ingresa o sale del método así como lo implementa el código sin el paradigma de AOP.


lunes, 23 de abril de 2012

Trabajando con AOP de la mano de PostSharp

En esta ocasión vamos a estar mostrando cuestiones relacionadas a la modularidad en en diseño y desarrollo de software y es la utilización del paradigma de orientación a aspectos con la utilización del framework PostSharp para .NET.

Primeramente debemos contar con el Visual Studio .NET, en esta ocasión, trabajaremos con la versión 2010 y para trabajar con aspectos, también podríamos llamar en este caso programación orientada a atributos, haremos uso del framework PostSharp.

Para las personas provenientes del mundo Java, los atributos vendrían a ser los annotations.

Mostraré un ejemplo sencillo con dos fragmentos de código, el primero sin el enfoque orientado a aspectos y el otro adoptando aspectos propiamente:


A continuación exponemos un método como fragmento de código en C# .net, que intenta realizar una división entre dos números enteros, en caso de haber alguna excepción o error, se registra el error en un archivo de texto:

Ejemplo de Logging sin PostSharp

static double Divide(int num1, int num2)
{
        try
       {
               return num1 / num2;
       }
       catch (Exception ex)
      {
              File.WriteAllText("logFile.txt", ex.ToString());
              return 0;
      }
}

La idea de la exposición del código anterior es observar las ventajas que ya de por sí nos brinda el paradigma de orientación a objetos a la hora de codificar (encapsulamiento, herencia y polimorfismo) y poder además realizar una comparación con la programación orientada a aspectos. Podemos fijarnos, sin embargo, que una de las cosas que la programación orientada a objetos no consigue manejar de forma adecuada es el comportamiento relacionado a las incumbencias transversales en un sistema, como lo es en este caso el campo de aplicación de Logging.

Ahondando detalladamente el código anterior podemos establecer que este mecanismo de bloques try-catch se repetirá en todos los métodos, ya sean funciones y procedimientos de módulos y clases que posea el código fuente en toda la aplicación y por ello obtendremos mucho código repetido, desperdigado y esparcido, por este motivo urge la necesidad de mejorar el diseño de lo anterior, inclusive aún si contamos con una abstracción a nivel de clases de la lógica de Logging nos veremos forzados a no olvidar nunca la llamada al método que realiza la tarea de Logging propiamente.

A continuación, exponemos el código fuente en C# .net resultante ya aplicando el paradigma de programación orientada a aspectos, utilizando parte del framework PostSharp, con el objetivo de comparar las dos implementaciones en ambos paradigmas o técnicas de programación:

Ejemplo de Logging AOP con PostSharp


[Logging]
static double Divide(int num1, int num2)
{
        return num1 / num2;
}

Podemos fijarnos que mediante una anotación o atributo (annotation/attribute) [Logging] expresada en la primera línea, estamos “llamando”, por decir de alguna forma, a la tarea de Logging en el caso de que surja una excepción y de manera con este atributo implementado como aspecto se separan y distinguen el código de la funcionalidad básica y el atributo perteneciente al aspecto de Logging. Cabe destacar que las anotaciones y atributos son unos mecanismos que sirven para añadir metadatos a las clases, métodos, campos, parámetros, variables locales y paquetes que se utilizan en tiempo de ejecución y su utilidad radica en interactuar con los elementos de programa y en cierta manera modificar su comportamiento dependiendo de determinados factores.

A continuación un código de ejemplo en el lenguaje C# .net con el framework PostSharp, donde se implementa el Logging como aspecto de la siguiente manera:

Definición de Aspecto de Logging en PostSharp

[Serializable]
public class LoggingAttribute : OnExceptionAspect
{
         public static void Log(object ex)
        {
                   File.WriteAllText("logFile.txt", ex.ToString());
        }

        public override void OnException(MethodExecution eventArgs)
       {
                  Log(eventArgs.Exception);
       }
}

Resumiendo lo anterior podemos concluir que la utilidad que nos proporciona esta nueva forma de encarar este problema consiste en separar las incumbencias de la funcionalidad básica perteneciente a la lógica del negocio (business logic) y las características no funcionales que en este caso se implementan como aspecto. La manera como se logra separar esto es mediante la implementación del aspecto como una unidad separada del código de la funcionalidad de tal forma a que estos no se mezclen y que esto pueda ser más legible, mantenible y escalable con bajo acoplamiento.

miércoles, 21 de enero de 2009

Autcompletar textbox con AJAX para búsquedas

Encontré algo bien didáctico y útil a la vez que por el momento anda un poco de moda y es relativamente nuevo que son las búsquedas tipo "intellisense" o "code completion" que tienen las herramientas de desarrollo para facilitar la codificación propiamente, en este caso para buscar algún texto.

Los pasos son utilizar el control de ajax toolkit llamado AutoCompleteExtender:

asp:TextBox ID="txtFiltro" runat="server">
TargetControlID="txtFiltro" MinimumPrefixLength="1" ServiceMethod="GetNames">




En donde ServiceMethod es el método utilizado para realizar la búsqueda y la misma se hace a través de un webmethod de un webservice, como el que sigue:

[System.Web.Services.WebMethod]
public static string[] GetNames(string prefixText, int count)
{
dbDataContext db = new dbDataContext();
return db.Countries.Where(n => n.Nombres.StartsWith(prefixText)).OrderBy(n => n.Nombres).Select(n => n.Nombres).Take(count).ToArray();
}



Seguidamente a través de este link hay un ejemplo detallado de como utilizar:

http://www.dbtutorials.com/advanced/ajax-sql-autocomplete-cs.aspx

Saludos.

domingo, 18 de enero de 2009