[ASP.NET Identity] I: Introducción

La irrupción de ASP.NET ha supuesto una ruptura y un giro hacia una nueva modalidad de evolución del producto. Disponemos del código fuente de ASP (al menos en su totalidad) y un ciclo de versiones mucho más dinámico y no anclada a una versión del framework dedicado. En esta serie de artículos voy a dedicarlo a analizar la parte de Identity puesto que supone grandes cambios respecto a las versiones anteriores de Membership.

Como novedades más destacadas tenemos:

  • Aplicable a todo ASP.NET (Webforms, MVC, WebApi, SignalR, etc)
  • Mucho menos acoplado
  • Por lo tanto, más fácil de testear.
  • Soporte de roles, de claims, oauth, WAAD,etc.
  • OWIN
  • Basado en NuGet

Todo esto ha supuesto básicamente romper y hacer de nuevo la parte de Membership, quedando como resultado este diagrama de dependencias:

A través de este esquema vemos un par de cosas básicas:

  • La implementación de Identity está basada en la implementación de Microsoft.OWIN.
  • Para dar soporte a toda la generación, conexión y gestión de datos hace uso de EntityFramework.

Estas dos últimas características unidas a las anteriores suponen una ruptura del paradigma respecto a Membership. Mientras que antes teníamos que “colocar con calzador” nuestras entidades de usuario a Membership para poder trabajar con ella, ahora podemos usar nuestras entidades y poder trabajarlas sin tener que estar tan acopladas a un sistema de autenticación u otro.

Es decir, mientras antes teníamos que usar WebSecurity para todo ahora tenemos un UserManager que dispone de toda la funcionalidad. El concepto de manager acude a nosotros como la parte encargada de gestionar la funcionalidad completa y nos permitirá hacer todo lo posible con el usuario (login, creación, borrado, asignar roles, claims, obtener datos, etc). Para ello hará uso de una UserStore que será quien por debajo implemente toda la funcionalidad necesaria. Estamos separando el manager que usaremos para la gestión de quién se encarga de ejecutar las tareas:

   1: public class UserManager<TUser> : IDisposable where TUser : IUser

   2: {

   3:        public UserManager(IUserStore<TUser> store);

   4:        ...

   5: }

Aquí vemos que nuestro Manager depende de un TUser que implementa la interfaz IUser. Vamos por partes…

IUser

Nuestro TUser será un usuario. Y este usuario no es mas que cómo define Identity la entidad de usuario, basada únicamente en una clave o propiedad única e identificativa y un nombre de usuario. Tenemos dos posibles implementaciones:

  • La primera, la que incluye por defecto que es IUser donde la clave es un string.
  • O podemos extender IUser con una clave del tipo que queramos:
   1: public class MyUser : IUser<int>

   2:     {

   3:         public int Id { get; set; }

   4:         public string UserName { get; set; }

   5:     }

Podemos ir completando nuestro usuario con otra serie de interfaces, como por ejemplo IdentityUserLogin, IdentityUserRole, IdentityUserClaim… todas aquellas que sean necesarias para poder ir construyendo un usuario y una identidad hecha a medida para nuestra aplicación:

image

IUserStore

Lo siguiente que nos pide el Manager es un UserStore. Este será el encargado de proporcionar la funcionalidad que puede usar el manager. Y si vemos, el UserStore se basa en cumplir una serie de interfaces que son Stores que suministran funcionalidad. La más básica de todas es una IUserStore:

   1: public interface IUserStore<TUser, in TKey> : IDisposable where TUser : class, IUser<TKey>

   2:    {

   3:        Task CreateAsync(TUser user);

   4:        Task DeleteAsync(TUser user);

   5:        Task<TUser> FindByIdAsync(TKey userId);

   6:        Task<TUser> FindByNameAsync(string userName);

   7:        Task UpdateAsync(TUser user);

   8:    }

Al igual que el caso anterior, podemos optar por usar la UserStore suministrada por defecto o usar una nuestra. Y del mismo modo que en el caso anterior, vamos añadiendo interfaces a nuestra UserStore para adoptar la funcionalidad deseada:

   1: public class UserStore<TUser> : UserStore<TUser, IdentityRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>,

   2:  IUserStore<TUser>, IUserStore<TUser, string>, 

   3: IDisposable where TUser : IdentityUser

   4: {

   5: ...

   6: }

image

Así pues, tenemos el siguiente esquema final:

image

Donde primero, definimos nuestra entidad de dominio (por ejemplo, MyUser). Este será usado por una Store (por ahora la UserStore) y esta Store, por el UserManager.

¿Qué ventajas aporta este modelo frente al antiguo? Evidentemente, todo mucho más desacoplado y testeable. Tenemos un control absoluto de las entidades y la funcionalidad que queremos dar a nuestra identidad.

[C#] ¿Cómo funcionan los atributos?

Siguiendo en la línea de mis últimos artículos en los que desgrano un poco el framework de .NET para ver qué hace por dentro, ahora le toca el turno a los atributos.

¿Qué es un atributo?

Un atributo no es más que una clase que sirve para decorar un tipo. Esta decoración puede ser de tipo informativa o funcional. Por ejemplo, podemos decorar una propiedad con el atributo Description para indicar más información a nivel de metadatos y los que trabajamos en ASP.MVC tenemos el archiconocido atributo Authorize que indica si el usuario está autenticado o no para permitir entrar en la acción seleccionada o redirigirlo a una página de error.

¿Cómo crear un atributo?

Muy sencillo, heredamos de Attribute (o si partimos de otro atributo ya creado) y sobreescribimos los métodos que queramos:

   1: public class MyAttribute : Attribute

   2:         {

   3:             public override bool IsDefaultAttribute()

   4:             {

   5:                 return base.IsDefaultAttribute();

   6:             }

   7:  

   8:             public override bool Match(object obj)

   9:             {

  10:                 return base.Match(obj);

  11:             }

  12:         }

Como lo que nos interesa es reusar uno que ya existe, vamos a heredar de Description para añadirle un campo más:

   1: [AttributeUsage(System.AttributeTargets.Property,

   2:                    AllowMultiple = false,

   3:                    Inherited = false)]

   4:         public class CustomDescriptionAttribute : DescriptionAttribute

   5:         {

   6:             private readonly string shortDescription;

   7:  

   8:             public CustomDescriptionAttribute(string shortDescription, string longDescription)

   9:                 : base(longDescription)

  10:             {

  11:                 this.shortDescription = shortDescription;

  12:             }

  13:         }

Con AttributeUsage podemos definir cómo queremos que se comporte ese atributo y para qué queremos que sea: para cualquier tipo de objeto, sólo para clases, métodos, propiedades, etc.:

  • AttributeTarget: Indica cuál es el destino del atributo.
  • AllowMultiple: Indica si para un mismo tipo, podemos colocar el atributo más de una vez.
  • Inherited: Indica si este atributo se heredará en las clases que hereden del atributo actual.

Una vez lo tenemos creado, lo asociamos a lo que queramos o hayamos definido previamente. Sé que el ejemplo no es el más adecuado, pero me gustaría usarlo sólo para ver cómo funciona:

   1: public class Person

   2:        {

   3:            [CustomDescription("First name", 

   4:                "A given name or the name that occurs first in a given name.")]

   5:            public string Name { get; set; }

   6:        }

Con este atributo podemos decorar una propiedad para indicarle una descripción corta y una larga. Esto podría ser útil si queremos exportar esta entidad a un fichero o un documento donde muestre una información más detallada del campo que se está mostrando.

¿Y qué hace por dentro?

Bien, vamos por partes. Evidentemente aquí no hay magia, si ponemos un breakpoint o una salida por consola vemos que el atributo no hace nada. Pero antes de eso vamos a ver que el código que ha generado en CIL:

   1: .class auto ansi nested public beforefieldinit CustomDescriptionAttribute

   2:         extends [System]System.ComponentModel.DescriptionAttribute

Primero vemos que muestra una herencia de una clase normal y que no hay nada del otro mundo. Un objeto cualquiera, vamos. Sigamos:

   1: .custom instance void [mscorlib]System.AttributeUsageAttribute::.ctor(valuetype [mscorlib]System.AttributeTargets) = ( 01 00 80 00 00 00 02 00 54 02 0D 41 6C 6C 6F 77   // ........T..Allow

   2:                                                                                                                            4D 75 6C 74 69 70 6C 65 00 54 02 09 49 6E 68 65   // Multiple.T..Inhe

   3:                                                                                                                            72 69 74 65 64 00 )                               // rited.

Aquí tenemos la línea que indica el AttributeUsage mencionado anteriormente. Quedemos con esto porque es bastante importante… Pero mientras, sigamos:

   1: .field private initonly string shortDescription

   2:     .method public hidebysig specialname rtspecialname 

   3:             instance void  .ctor(string shortDescription,

   4:                                  string longDescription) cil managed

   5:     {

   6:       // Code size       18 (0x12)

   7:       .maxstack  8

   8:       IL_0000:  ldarg.0

   9:       IL_0001:  ldarg.2

  10:       IL_0002:  call       instance void [System]System.ComponentModel.DescriptionAttribute::.ctor(string)

  11:       IL_0007:  nop

  12:       IL_0008:  nop

  13:       IL_0009:  ldarg.0

  14:       IL_000a:  ldarg.1

  15:       IL_000b:  stfld      string Blog.AttributeToCil.Program/CustomDescriptionAttribute::shortDescription

  16:       IL_0010:  nop

  17:       IL_0011:  ret

  18:     } // end of method CustomDescriptionAttribute::.ctor

Aquí tenemos el resto de la clase. No hay diferencia alguna con cualquier otro tipo de clase en MSIL. Una vez tenemos esto, vamos a ver cómo queda la propiedad Name de la clase Person a la que le añadimos el atributo:

   1: .property instance string Name()

   2:    {

   3:      .custom instance void Blog.AttributeToCil.Program/CustomDescriptionAttribute::.ctor(string,

   4:                                                                                          string) = ( 01 00 0A 46 69 72 73 74 20 6E 61 6D 65 3B 41 20   // ...First name;A 

   5:                                                                                                      67 69 76 65 6E 20 6E 61 6D 65 20 6F 72 20 74 68   // given name or th

   6:                                                                                                      65 20 6E 61 6D 65 20 74 68 61 74 20 6F 63 63 75   // e name that occu

   7:                                                                                                      72 73 20 66 69 72 73 74 20 69 6E 20 61 20 67 69   // rs first in a gi

   8:                                                                                                      76 65 6E 20 6E 61 6D 65 2E 00 00 )                // ven name...

   9:      .get instance string Blog.AttributeToCil.Program/Person::get_Name()

  10:      .set instance void Blog.AttributeToCil.Program/Person::set_Name(string)

  11:    } // end of property Person::Name

Vemos el get/set y además, una referencia a una instancia del atributo que hemos creado. El mismo tipo de referencia que había en el caso anterior con el AttributeUsage. En CIL, .custom se usa para indicar CustomAttributes que es todo aquello que hereda de System.Attribute. Y CIL obtiene la información del atributo y la coloca además en el propio código generado, en el metadata asociado. La forma de instanciar un objeto de este tipo es por su nombre y un array de bytes en el que previamente se indican el tipo de objeto que irá en el constructor:

   1: .ctor(string,

   2:   string) = ( 01 00 0A 46 69 72 73 74 20 6E 61 6D 65 3B 41 20   // ...First name;A 

   3:               67 69 76 65 6E 20 6E 61 6D 65 20 6F 72 20 74 68   // given name or th

   4:               65 20 6E 61 6D 65 20 74 68 61 74 20 6F 63 63 75   // e name that occu

   5:               72 73 20 66 69 72 73 74 20 69 6E 20 61 20 67 69   // rs first in a gi

   6:               76 65 6E 20 6E 61 6D 65 2E 00 00 )                // ven name...

Con esto vemos que por un lado cada tipo mantiene una referencia a una instancia del tipo del atributo. Pero no la instancia en sí misma. La instancia se crea en cuanto se consulta la información del atributo. Si no hay nadie que la consulte, no se crea.

¿Cómo obtengo la información del atributo?

Por Reflection. Tenemos dos vías (en función del atributo que busquemos):

  • Obtener directamente el atributo empleando Reflection y navegando en los campos, métodos, etc para buscarlo.
  • Usar Attribute.GetCustomAttributes que nos permitirá dado un assembly, obtener los tipos de atributos que hemos creado para ello.

Afortunadamente hace un par de días publicaron una actualización que permite navegar por el código fuente de .NET de una forma más amena. Podemos encontrar como la clase Attribute y ver cómo obtiene los atributos por reflexión.

Por ejemplo, con el siguiente código podemos obtener el valor de CustomDescription que hemos creado anteriormente. Básicamente consiste en obtener la primera propiedad de la clase Person y preguntarle por sus CustomAttributes:

   1: var customDescription = person.GetType().GetProperties().First().GetCustomAttributes(true);

   2:  

* Si quieres saber más sobre atributos, puedes consultar la definición en el ECMA (sección II.22; página 205 y siguientes)

[C#] ¿Cómo funciona el Garbage Collector? ( III )

Siguiendo con la miniserie dedicada al Garbage Collector, hemos visto los siguientes puntos:

  • Primero sobre cómo se destruyen los objetos en C# y qué mecanismos e implicaciones hay.
  • Segundo, sobre cuándo se dispara el GC y qué efectos tiene sobre la memoria y el rendimiento.
  • Y en este tercero vamos a ver cómo podemos monitorizar el GC.

Notificaciones

El GC puede emitir notificaciones en estos casos:

  • Se ejecuta sobre una generación 2.
  • Pasan objetos de generación 1 a generación 2.

Por motivos de rendimiento no se registra el caso de objetos pequeños  Pero como hemos visto en el artículo anterior, si GC se dispara a menudo repercute en términos de rendimiento en nuestra aplicación. Y si además es porque tenemos objetos de generación 1/2 podemos añadir notificaciones a nuestra aplicación que nos permitirá ver cuándo se prepara y cuándo se ha ejecutado el GC. Veamos un ejemplo:

   1: GC.WaitForFullGCApproach();

Con este método paramos el hilo actual hasta que el GC esté preparado para lanzarse. Podemos hacer que sea un bloqueo indefinido o especificarle un tiempo en milisegundos de espera.

   1: GC.WaitForFullGCComplete();

Y con este otro método detenemos el hilo hasta que el GC completa su tarea en esa colección. Al igual que el método anterior podemos especificarle el tiempo máximo de espera en milisegundos.

Podemos crear un método que se encargue estar pendiente de estas notificaciones a través de un hilo:

   1: public static void WaitForFullGCProc()

   2: {

   3:     while (true)

   4:     {

   5:         while (true)

   6:         {

   7:             GCNotificationStatus s = GC.WaitForFullGCApproach();

   8:             if (s == GCNotificationStatus.Succeeded)

   9:             {

  10:                 Console.WriteLine("GC Notification raised.");

  11:             }

  12:             else if (s == GCNotificationStatus.Canceled)

  13:             {

  14:                 Console.WriteLine("GC Notification cancelled.");

  15:                 break;

  16:             }

  17:             else

  18:             {

  19:                 Console.WriteLine("GC Notification not applicable.");

  20:                 break;

  21:             }

  22:  

  23:             s = GC.WaitForFullGCComplete();

  24:             if (s == GCNotificationStatus.Succeeded)

  25:             {

  26:                 Console.WriteLine("GC Notifiction raised.");

  27:             }

  28:             else if (s == GCNotificationStatus.Canceled)

  29:             {

  30:                 Console.WriteLine("GC Notification cancelled.");

  31:                 break;

  32:             }

  33:             else

  34:             {

  35:                 Console.WriteLine("GC Notification not applicable.");

  36:                 break;

  37:             }

  38:         }

  39:  

  40:  

  41:         Thread.Sleep(500);

  42:  

  43:         if (finalExit)

  44:         {

  45:             break;

  46:         }

  47:     }

  48: }

Este método extremadamente feo y horroroso irá notificando las operaciones del GC y podremos ir viendo cuándo y en qué frecuencia se dispara. Es de notar que esto se llama sólo en caso de generación 1 y 2 y que además sólo en situaciones de bloqueo, por lo que si se llama muchas veces implica que quizá hay algo en nuestra aplicación que está consumiendo demasiados recursos.

Monitorización

Podemos configurar nuestra aplicación para que nos indique algunos datos sobre el consumo de memoria y CPU. A esto se le llama Application Resource Monitoring. Podemos activar la monitorización a través de la siguiente instrucción:

   1: AppDomain.MonitoringIsEnabled = true;

Una vez activada, no se puede desactivar. Aquí tenemos los contadores disponibles:

   1: AppDomain.MonitoringSurvivedProcessMemorySize

Con esto podemos ver cuánta es la memoria gestionada por el proceso que ha sobrevivido a una ejecución del GC.

   1: AppDomain.CurrentDomain.MonitoringSurvivedMemorySize

Y con esta lo mismo que la anterior pero referida al Application Domain. Tanto esta instrucción como la anterior toman en consideración la última aplicación del algoritmo en una fase bloqueante. Traduciendo, significa que se ha invocado a GC.Collect() de modo explícito o directamente lo ha hecho el hilo.

   1: AppDomain.CurrentDomain.MonitoringTotalAllocatedMemorySize

Con esta podemos ver cuántos bytes hemos alojado durante la vida de la aplicación. Hasta aquí todas las operaciones que permiten evaluar la memoria destinada en la aplicación y el proceso.

   1: AppDomain.CurrentDomain.MonitoringTotalProcessorTime

Y esta última el tiempo en el que la CPU está ejecutando el hilo de Application Domain. Veamos un ejemplo con el siguiente código, al que le pondremos estos contadores con el CustomObject de artículos anteriores:

   1: for (int i = 0; i < 10000; i++)

   2: {

   3:     var customObject = new CustomObject();

   4: }

Que produce la siguiente gráfica:

image

Vemos como la memoria total del proceso va en aumento hasta que entra el GC en su primera recolecta en la iteración 871. A partir de ahí, la memoria total crece pero el GC se encarga de ir recolectando. Si suprimimos esta variable para ver con un poco más de nitidez el resto, nos encontramos con esto:

image

Donde se aprecia los pequeños saltos que va optimizando el GC en las recolectas. Y un poco más en detalle:

image

Con lo que tenemos otro método para ver cuánta memoria consumimos y cómo el GC la va organizando. Si vemos que conforme la vida de nuestra aplicación la memoria aumenta y el coste de CPU también puede ser un síntoma de algo que no estamos haciendo bien.

Este es el último artículo de la “serie”. A modo de resumen hemos visto los siguientes puntos del Gargabe Collector:

  • En el primer artículo, hemos visto cómo se finaliza un objeto dentro de .NET Framework.
  • En el segundo, cómo se organizan los objetos internamente y el control algo “manual” del GC.
  • Y en este último sobre cómo podemos recibir notificaciones y medir la memoria empleada para averiguar si tenemos algún problema.

[C#] ¿Cómo funciona el Garbage Collector? ( II )

En el artículo anterior introduje brevemente qué mecanismos disponemos para liberar los recursos de los objetos y cómo actúa GC sobre ellos. Normalmente para un desarrollador GC es algo que está ahí pero no necesita trastear. Pero para aplicaciones más avanzadas, sistemas críticos o debug/testeo suele ser muy útil.

Liberando memoria

El GC tiene la función de liberar la memoria de los objetos. Esto puede ocurrir en los siguiente casos:

  • Limitaciones técnicas de la máquina: Tenemos demasiada memoria ocupada y alguien tiene que liberarla. Si es nuestra, el GC se encargará de ello.
  • Los objetos que tenemos en el Heap se acumulan y excede el máximo permitido. El GC se dispara y recolecta.
  • O directamente, porque lo invocamos explícitamente.

Veamos ejemplos de los dos últimos casos.

Para el primer ejemplo, simplemente tenemos el siguiente código:

   1: for (int i = 0; i < 100000; i++)

   2: {

   3:     var customObject = new CustomObject();

   4:  

   5:     System.Console

   6:         .Write(string.Format("Iteration {0}; Memory {1}n", i, GC.GetTotalMemory(false)));

   7: }

Donde tenemos un bucle que va instanciando los objetos. Al instanciar un objeto, se reserva la memoria necesaria para él y se crea la instancia. Ese objeto permanecerá en memoria hasta que el GC estime oportuno. Vamos a ver cómo a medida que se incrementa el número de iteraciones la memoria asignada por el GC irá aumentando hasta que decida liberarla.

En el segundo ejemplo, forzamos a través de GC.Collect() que cada vez que se crea el objeto, invocamos al GC para que recorra todo su grafo de objetos y elimine aquellos que no se van a usar:

   1: for (int i = 0; i < 100000; i++)

   2: {

   3:     var customObject = new CustomObject();

   4:     GC.Collect();

   5:     System.Console

   6:        .Write(string.Format("Iteration {0}; Memory {1}n", i, GC.GetTotalMemory(false)));

   7: }

Y como resultado para esta muestra obtenemos la siguiente gráfica:

image

 

Donde podemos observar como en el primer ejemplo el GC acumula memoria hasta que la libera según estima oportuno mientras que en el segundo caso la cantidad de memoria ocupada permanece estable. Evidentemente esta recolecta forzada no sale gratis, veamos ahora en escala de tiempo cuánto repercute. Como el coste por iteración es despreciable, a continuación muestro qué ocurre si invocamos el GC o no en bloques de iteraciones:

image

Podemos ver una diferencia de tiempo de orden exponencial. Es decir, debemos seguir las recomendaciones dadas de dejar que GC trabaje y nosotros no interactuar con él salvo que tengamos razones muy justificadas para ello.

Generaciones

Una vez que hemos visto cuándo se dispara y qué ocurre cuando se dispara, vamos a ver cómo esta distribuido. Aquí introduzco el tema de “Generaciones”, que es la estructura de distribución de objetos que dispone GC:

  • Generación 0: Contiene los objetos que menos duran, como variables temporales. Inicialmente todo objeto irá directamente a este nivel de generación.
  • Generación 1: Contiene objetos de corta duración y actúa de buffer entre objetos de corta y larga duración.
  • Generación 2: Contiene objetos de larga duración. Por ejemplo, variables estáticas a nivel de aplicación.

Todo objeto en función de su uso y otros factores, se van moviendo en las respectivas generaciones. Como ejemplo se puede ver cómo se obtiene la generación de un determinada instancia:

   1: GC.GetGeneration(customObject);

Esto devuelve 0,1 ó 2 en función de la generación de esa instancia. Además de el objeto en sí, podemos pasarle una WeakReference directamente.

También podemos conocer cuántas veces se ha invocado el GC en una generación concreta a través de este método:

   1: GC.CollectionCount(0);

Los objetos que no son reclamados al GC se llaman “supervivientes”. Si están en la generación 0 y no han sido reclamados, pasan a la 1. Si están en la 2, siguen en la 2. El GC además detecta estos casos como algo particular: si cada vez hay más supervivientes, balancea el algoritmo para lograr un equilibrio entre el consumo de memoria y el tiempo de ejecución del mismo.

El algoritmo sigue el siguiente proceso:

  • En una primera fase se busca y se crea una lista con todos los objetos vivos.
  • Después se actualizan las referencias de los objetos y se relocalizan.
  • Por último, una fase de compactación de memoria que agrupa el espacio disponible junto con el liberado por los objetos que ya no existen. La compactación siempre se hará salvo para aquellos casos en que los objetos sean demasiado grandes. Se puede recurrir a esta propiedad del GC para forzar que los objetos grandes siempre se compacten (sólo disponible a partir del 4.5.1).

Antes de que el GC se dispare, todos los hilos de ejecución de la aplicación se suspenden para poder activarse el hilo dedicado al GC. En ese momento el GC aplica el algoritmo y el proceso mencionado anteriormente. Cuando termina, el resto de hilos prosiguen en su ejecución:

When a thread triggers a Garbage Collection

[C#] ¿Cómo funciona el Garbage Collector? ( I )

Una de las principales diferencias respecto a C/C++ y similitudes con Java es la presencia del Garbage Collector (GC) que permite delegar en un proceso la gestión de los objetos que creamos. A priori nos olvidamos de tener que invocar de forma explícita el destructor de los objetos y liberar su memoria. Veamos qué ocurre realmente.

El GC aparece en la primera versión de .NET Framework. Su funcionamiento permite que hagamos cosas como estas:

   1: class CustomObject

   2: {

   3:     public byte[] ByteArray { get; set; }

   4:  

   5:     public CustomObject()

   6:     {

   7:         this.ByteArray = new byte[1024];

   8:     }

   9: }

Vemos que creamos un objeto array de una longitud determinada y al finalizar el programa podemos comprobar a través de herramientas que no tenemos fugas de memoria. Si analizamos el código CIL generado podemos ver que no aparece tampoco ninguna mención a destruir nada. El GC, a través del Framework se encarga todo. Todo totalmente transparente.

Para seguir hablando de GC es necesario introducir el concepto básico de memoria en Stack y en el Heap. Para tratar este tema aconsejo este artículo y su segunda parte que tratan sobre el diseño e implementación del stack. Como añadido, este no vendrá mal. El resumen sencillo y algo banal es que todo tipo que se instancia irá alojado en el Heap que es autogestionado. Es decir, hay alguien que se encarga de una vez instanciado un objeto que ya se ha usado, determinar qué hacer con él.

Finalizadores

La primera aproximación siguiendo un paradigma OO será que al igual que un objeto tiene constructor debe tener un destructor. Al igual que en C++, C# posee este método siguiendo la misma nomenclatura para cualquier tipo de objeto:

   1: class CustomObject

   2:         {

   3:             public byte[] ByteArray { get; set; }

   4:  

   5:             public CustomObject()

   6:             {

   7:                 System.Console.Write("Object created");

   8:             }

   9:  

  10:             ~CustomObject()

  11:             {

  12:                 System.Console.Write("Finalizer invoked");

  13:                 System.Console.Read();

  14:             }

  15:         }

Donde podremos definir qué ocurre una vez se “destruye” el objeto. Realmente no se destruye, puesto que en C# se llama “finalizador”. Se finaliza la vida del objeto pero este sigue estando vivo. Simplemente funciona así porque el GC tiene varias fases que veremos más adelante. Pero analicemos este hecho un poco más en profundidad. Si obtenemos el código IL generado observamos el siguiente método:

   1: .method family hidebysig virtual instance void 

   2:         Finalize() cil managed

   3: {

   4:   // Code size       31 (0x1f)

   5:   .maxstack  1

   6:   .try

   7:   {

   8:     IL_0000:  nop

   9:     IL_0001:  ldstr      "Finalizer invoked"

  10:     IL_0006:  call       void [mscorlib]System.Console::Write(string)

  11:     IL_000b:  nop

  12:     IL_000c:  call       int32 [mscorlib]System.Console::Read()

  13:     IL_0011:  pop

  14:     IL_0012:  nop

  15:     IL_0013:  leave.s    IL_001d

  16:   }  // end .try

  17:   finally

  18:   {

  19:     IL_0015:  ldarg.0

  20:     IL_0016:  call       instance void [mscorlib]System.Object::Finalize()

  21:     IL_001b:  nop

  22:     IL_001c:  endfinally

  23:   }  // end handler

  24:   IL_001d:  nop

  25:   IL_001e:  ret

  26: } // end of method CustomObject::Finalize

Para IL no existe el concepto de destructor. Existe y transforma lo que llamaríamos un destructor en un finalizador, en un método llamado Finalize() que alguien invocará. De hecho, si intentamos declarar lo siguiente:

   1: protected override void Finalize()

   2: {

   3:     //...

   4: }

Vemos como el propio compilador no te permite declarar ese método y te sugiere que declares un destructor.

¿Qué se debe escribir en un finalizador? Liberación de recursos (streams, conexiones, etc) y al ser crítico, un código que no deba fallar ni provocar excepciones. Y la siguiente pregunta es: ¿no estaba IDisposable para esto? En efecto, en la página de MSDN nos cuentan que IDisposable sirve para liberar los recursos que no se pueden gestionar porque GC, al no ser determinístico no sabemos cuándo podrá lanzarse. Y además como son recursos que no se pueden gestionar, GC los ignorará. Típicos ejemplos de esto son los streams. Y por eso se recomienda cada vez que abrimos un stream, insertarlo dentro de un using. Cuando finaliza el using se invoca de forma automática al método Dispose(). Algo se vio en un artículo anterior.

Un ejemplo sencillo de cómo funciona y cómo implementar correctamente Dispose lo tenemos en el siguiente fragmento de código:

   1: class DisposableObject : IDisposable

   2:         {

   3:             public void Dispose()

   4:             {

   5:                 this.Dispose(true);

   6:                 GC.SuppressFinalize(this);

   7:             }

   8:  

   9:             protected virtual void Dispose(bool disposing)

  10:             {

  11:                 if (disposing)

  12:                 {

  13:                     // Dispose on each object of this instance.

  14:                     // ...

  15:                 }

  16:             }

  17:  

  18:             ~DisposableObject()

  19:             {

  20:                 this.Dispose(false);

  21:             }

  22:         }

Al implementar la interfaz IDisposable tenemos que implementar el método Dispose(). El patrón ofrece una estructuración de las llamadas a Dispose() del objeto actual y los objetos que se integren, de modo que si invocamos Dispose() se invocará el Dispose() de cada uno de los objetos que hayamos ido añadiendo. Sin embargo si se invoca al finalizador a través del “destructor”, el GC seguirá su curso. Nótese la diferencia entre Dispose() y el finalizador:

– Dispose(): Primero invoca al método protegido para que se invoquen en cascada los Dispose de cada objeto y posteriormente, se indica al GC que no es necesario que invoque al finalizador en este instante puesto que ya hemos liberado los recursos. ¿Necesario? No. ¿Optimizado? Un poco, puesto que ahorramos llamadas al GC.

– ~(): Simplemente invoca al Dispose(false) que no debería invocar al Dispose de los objetos internos. Desde el punto de vista nuestro respecto al GC, no sabemos en qué estado están y ya es función del GC determinar si debe ir a por ellos o no.

Siguiendo con la pregunta de ¿qué diferencia hay entre un finalizador y IDisposable? La respuesta a nivel teórico es: ninguna. Ambos están pensados para liberar aquellos recursos que empleamos en los objetos. La única diferencia es que tenemos un mecanismo para controlar cuándo lo llamamos si empleamos IDisposable; porque el finalizador, al depender de GC no podemos esperar nada de él. No sabemos cuándo será llamado.

A modo de resumen tenemos lo siguiente:

  • En C# NO podemos destruir los objetos. Como mucho podemos indicar qué ocurre cuando tienen algún recurso asociado y liberarlo.
  • IDisposable no se invoca sólo. Hay que invocarlo. E invocarlo, no significa destruir el objeto.
  • ~() Es el método finalizador del objeto. Sólo es invocado a través del GC y cuando éste lo estima oportuno.

[C#] ¿Cómo funcionan las excepciones?

Introducción

Una excepción no suele ser algo bueno en nuestro sistema. Puede ser porque haya sido provocada para controlar o prevenir un comportamiento incorrecto o directamente puede ser que algo se nos haya pasado y falle todo. El objetivo de este artículo es profundizar un poco en qué ocurre cada vez que se lanza una excepción. Así que vayamos al grano y fijémonos en el siguiente fragmento de código:

   1: static void Main(string[] args)

   2: {

   3:     var list = new List<int>();

   4:     var result = list[1];

   5: }

Evidentemente, producirá una excepción de tipo ArgumentOutOfRangeException indicando que no ha sido controlada y el programa se cerrará. ¿Qué ha ocurrido aquí? Según el detalle de la excepción, tenemos:

clip_image002

Analizando el StackTrace podemos ver:

Si nos fijamos, vemos toda la traza del hilo y procesos asoaciados al Framework hasta que llegamos a System.GenericList que es donde se produce la excepción. En concreto, dentro del get accessor:

clip_image004

Bien, es obvio. GenericList verifica que el elemento que estamos accediendo es superior a la cantidad de elementos que tiene la lista y lanza la excepción de tipo ArgumentOutOfRangeException. Todo lo que vemos debajo en la pila es en primer lugar la clase que está llamando a la lista y así sucesivamente hasta llegar al hilo que ha arrancado todo el proceso. Puesto que la excepción no está controlada, el Framework detendrá el hilo de ejecución actual:

   1: System.Threading.Thread.CurrentThread.Abort();

Al detener el hilo, se lanza la excepción ThreadAbortExcepction y se detiene toda la ejecución del proceso.

Capturar la excepción

Una vez que hemos visto qué ocurre cuando tenemos una excepción que no está provocada, vamos a ver qué es lo que ocurre cuando intentamos capturarla. Siguiendo el ejemplo anterior, podemos introducir una cláusula try/catch para ver qué ocurre. El comportamiento esperado es que el programa se ejecutará, se producirá la excepción y ésta será capturada por el bloque catch. El flujo del programa no se detendrá y el hilo finalizará correctamente:

   1: static void Main(string[] args)

   2: {

   3:     var list = new List<int>();

   4:     try

   5:     {

   6:         var result = list[1];

   7:     }

   8:     catch (Exception ex)

   9:     {

  10:         // Do something...

  11:     }

  12: }

Hemos puesto por ahora una excepción genérica a capturar. Veamos qué código CIL ha generado esto:

   1: .method private hidebysig static void  Main(string[] args) cil managed

   2: {

   3:   .entrypoint

   4:   // Code size       26 (0x1a)

   5:   .maxstack  2

   6:   .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<;int32> list,

   7:            [1] int32 result,

   8:            [2] class [mscorlib]System.Exception ex)

   9:   IL_0000:  nop

  10:   IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<;int32>::.ctor()

  11:   IL_0006:  stloc.0

  12:   .try

  13:   {

  14:     IL_0007:  nop

  15:     IL_0008:  ldloc.0

  16:     IL_0009:  ldc.i4.1

  17:     IL_000a:  callvirt   instance !0 class [mscorlib]System.Collections.Generic.List`1<;int32>::get_Item(int32)

  18:     IL_000f:  stloc.1

  19:     IL_0010:  nop

  20:     IL_0011:  leave.s    IL_0018

  21:   }  // end .try

  22:   catch [mscorlib]System.Exception 

  23:   {

  24:     IL_0013:  stloc.2

  25:     IL_0014:  nop

  26:     IL_0015:  nop

  27:     IL_0016:  leave.s    IL_0018

  28:   }  // end handler

  29:   IL_0018:  nop

  30:   IL_0019:  ret

  31: } // end of method Program::Main

Nótese un par de puntos interesantes. Vemos que como valores locales establecemos la List<int> que queremos acceder, la variable result (que el compilador ha especificado como tipo int32) y por último un tipo System.Exception. Vemos claramente el try y el catch definidos.

Al finalizar el try y el catch aparece la instrucción leave.s. Esta instrucción adquiere sentido en el contexto actual, puesto que dentro de CLR los bloques try/catch.. se consideran bloques protegidos. Al ejecutar la instrucción leave.s (o leave a secas indicando la dirección) indicamos que vamos a salir del bloque protegido, cedemos la gestión a CLR y además vamos a una determinada dirección. En este caso y en los siguientes, leave.s hace referencia a una dirección que suele ser la finalización de la sección try para que el flujo continúe por la función.

Volviendo al tema que nos ocupa, el código de C# es idéntico al del primer caso expuesto. Se ejecuta una función que provoca una excepción. En ese momento, el Framework busca primero dentro de la región actual si hay alguna instrucción catch que sea del mismo tipo que la excepción que se ha producido. En ese caso, salta al bloque catch correspondiente y sigue su curso. Y, ¿qué ocurre si no encuentra un bloque catch? Pues en ese momento es cuando el Framework recorre TODA la pila de llamadas buscando un bloque que lo capture. Y cuando no lo encuentra, él mismo detiene el hilo lanzando el ThreadAbortException.

image

Por ejemplo, vamos a incluir que capture el ArgumentOutOfRangeException y veremos el código CIL que genera. En primer lugar tenemos el código:

   1: static void Main(string[] args)

   2: {

   3:     var list = new List<int>();

   4:     try

   5:     {

   6:         var result = list[1];

   7:     }

   8:     catch (ArgumentOutOfRangeException ex)

   9:     {

  10:         // 

  11:     }

  12:     catch (Exception ex)

  13:     {

  14:         // Do something...

  15:     }

  16: }

Y aquí el código CIL:

   1: .method private hidebysig static void  Main(string[] args) cil managed

   2: {

   3:   .entrypoint

   4:   // Code size       31 (0x1f)

   5:   .maxstack  2

   6:   .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<;int32> list,

   7:            [1] int32 result,

   8:            [2] class [mscorlib]System.ArgumentOutOfRangeException ex,

   9:            [3] class [mscorlib]System.Exception V_3)

  10:   IL_0000:  nop

  11:   IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<;int32>::.ctor()

  12:   IL_0006:  stloc.0

  13:   .try

  14:   {

  15:     IL_0007:  nop

  16:     IL_0008:  ldloc.0

  17:     IL_0009:  ldc.i4.1

  18:     IL_000a:  callvirt   instance !0 class [mscorlib]System.Collections.Generic.List`1<;int32>::get_Item(int32)

  19:     IL_000f:  stloc.1

  20:     IL_0010:  nop

  21:     IL_0011:  leave.s    IL_001d

  22:   }  // end .try

  23:   catch [mscorlib]System.ArgumentOutOfRangeException 

  24:   {

  25:     IL_0013:  stloc.2

  26:     IL_0014:  nop

  27:     IL_0015:  nop

  28:     IL_0016:  leave.s    IL_001d

  29:   }  // end handler

  30:   catch [mscorlib]System.Exception 

  31:   {

  32:     IL_0018:  stloc.3

  33:     IL_0019:  nop

  34:     IL_001a:  nop

  35:     IL_001b:  leave.s    IL_001d

  36:   }  // end handler

  37:   IL_001d:  nop

  38:   IL_001e:  ret

  39: } // end of method Program::Main

Como se puede apreciar, disponemos de las dos regiones de cada uno de los catch. Una para el ArgumentOutOfRangeException y otra para la System.Exception básica. Si recordamos la pila de llamadas expuesta al inicio del artículo, el proceso seguirá siendo tal que:

image

Cuyo algoritmo es:

– List<T> lanzo una excepción, esperando que el elemento anterior en la pila de llamadas la captura.

– Elemento anterior de la pila: Recibo una excepción. ¿Estoy dentro de un try/catch? ¿Tengo algún catch con el tipo de excepción recibida? Si es así, la capturo y ejecuto el bloque catch correspondiente. Si no, entonces  busco en el elemento anterior de la pila. Y así sucesivamente hasta llegar al fondo de la pila.

Cuando llega al fondo de la pila y la excepción no se ha controlado, el propio Framework mata el proceso con la instrucción previamente mencionada en la introducción.

Lanzando una excepción

Bien, ahora que sabemos qué ocurre cuando la capturamos, veamos qué ocurre cuando la lanzamos. Siguiendo con el patrón de código usado hasta ahora, vamos a lanzar la excepción que recibimos de List<int>:

   1: static void Main(string[] args)

   2: {

   3:    var list = new List<int>();

   4:    try

   5:    {

   6:        var result = list[1];

   7:    }

   8:    catch (ArgumentOutOfRangeException ex)

   9:    {

  10:        throw ex;

  11:    }

  12:    catch (Exception ex)

  13:    {

  14:        // Do something...

  15:    }

  16: }

Colocamos que en un catch, nos lance la excepción. Nótese el término throw, que significa que lanzamos hacia arriba la excepción, en el sentido que el elemento que esté en la parte anterior de la pila del actual será quien reciba el objeto. Veamos el CIL:

   1: .method private hidebysig static void  Main(string[] args) cil managed

   2: {

   3:   .entrypoint

   4:   // Code size       30 (0x1e)

   5:   .maxstack  2

   6:   .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<;int32> list,

   7:            [1] int32 result,

   8:            [2] class [mscorlib]System.ArgumentOutOfRangeException ex,

   9:            [3] class [mscorlib]System.Exception V_3)

  10:   IL_0000:  nop

  11:   IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<;int32>::.ctor()

  12:   IL_0006:  stloc.0

  13:   .try

  14:   {

  15:     IL_0007:  nop

  16:     IL_0008:  ldloc.0

  17:     IL_0009:  ldc.i4.1

  18:     IL_000a:  callvirt   instance !0 class [mscorlib]System.Collections.Generic.List`1<;int32>::get_Item(int32)

  19:     IL_000f:  stloc.1

  20:     IL_0010:  nop

  21:     IL_0011:  leave.s    IL_001c

  22:   }  // end .try

  23:   catch [mscorlib]System.ArgumentOutOfRangeException 

  24:   {

  25:     IL_0013:  stloc.2

  26:     IL_0014:  nop

  27:     IL_0015:  ldloc.2

  28:     IL_0016:  throw

  29:   }  // end handler

  30:   catch [mscorlib]System.Exception 

  31:   {

  32:     IL_0017:  stloc.3

  33:     IL_0018:  nop

  34:     IL_0019:  nop

  35:     IL_001a:  leave.s    IL_001c

  36:   }  // end handler

  37:   IL_001c:  nop

  38:   IL_001d:  ret

  39: } // end of method Program::Main

Y analizamos el fragmento en cuestión:

   1: catch [mscorlib]System.ArgumentOutOfRangeException 

   2: {

   3:   IL_0013:  stloc.2

   4:   IL_0014:  nop

   5:   IL_0015:  ldloc.2

   6:   IL_0016:  throw

   7: }  // end handler

Únicamente lo que hacemos aquí es almacenar el valor que está en la cima de la pila, que es el recibido por la excepción y de tipo ArgumentOutOfRangeException (variable que dentro de las variables locales tiene la posición 2, como se puede apreciar en el código completo). La volvemos a cargar para que esté en la cima e invocamos a la instrucción throw para que ese objeto lo reciba el elemento que está en la parte superior de la pila del actual.

Finally

Ahora veamos qué comportamiento introduce una cláusula finally en todo esto. Añadiremos el finally al código actual de modo que tendremos:

   1: static void Main(string[] args)

   2: {

   3:     var list = new List<int>();

   4:     try

   5:     {

   6:         var result = list[1];

   7:     }

   8:     catch (ArgumentOutOfRangeException ex)

   9:     {

  10:         throw ex;

  11:     }

  12:     catch (Exception ex)

  13:     {

  14:         // Do something

  15:     }

  16:     finally

  17:     {

  18:         // Finally clausule

  19:     }

  20: }

Y veamos el código CIL generado:

   1: .method private hidebysig static void  Main(string[] args) cil managed

   2: {

   3:   .entrypoint

   4:   // Code size       36 (0x24)

   5:   .maxstack  2

   6:   .locals init ([0] class [mscorlib]System.Collections.Generic.List`1<;int32> list,

   7:            [1] int32 result,

   8:            [2] class [mscorlib]System.ArgumentOutOfRangeException ex,

   9:            [3] class [mscorlib]System.Exception V_3)

  10:   IL_0000:  nop

  11:   IL_0001:  newobj     instance void class [mscorlib]System.Collections.Generic.List`1<;int32>::.ctor()

  12:   IL_0006:  stloc.0

  13:   .try

  14:   {

  15:     .try

  16:     {

  17:       IL_0007:  nop

  18:       IL_0008:  ldloc.0

  19:       IL_0009:  ldc.i4.1

  20:       IL_000a:  callvirt   instance !0 class [mscorlib]System.Collections.Generic.List`1<;int32>::get_Item(int32)

  21:       IL_000f:  stloc.1

  22:       IL_0010:  nop

  23:       IL_0011:  leave.s    IL_001c

  24:     }  // end .try

  25:     catch [mscorlib]System.ArgumentOutOfRangeException 

  26:     {

  27:       IL_0013:  stloc.2

  28:       IL_0014:  nop

  29:       IL_0015:  ldloc.2

  30:       IL_0016:  throw

  31:     }  // end handler

  32:     catch [mscorlib]System.Exception 

  33:     {

  34:       IL_0017:  stloc.3

  35:       IL_0018:  nop

  36:       IL_0019:  nop

  37:       IL_001a:  leave.s    IL_001c

  38:     }  // end handler

  39:     IL_001c:  nop

  40:     IL_001d:  leave.s    IL_0022

  41:   }  // end .try

  42:   finally

  43:   {

  44:     IL_001f:  nop

  45:     IL_0020:  nop

  46:     IL_0021:  endfinally

  47:   }  // end handler

  48:   IL_0022:  nop

  49:   IL_0023:  ret

  50: } // end of method Program::Main

  51:  

Centrémonos en dos secciones. Por una parte vemos que el flujo ha cambiado de forma significativa. Primero notamos la introducción de la instrucción finally dentro del código. Vemos además que todos tanto el try/catch cuando finalizan, saltan al final de la función y no al finally. Esto es porque el CLR ejecuta siempre el bloque finally en cualquier condición, por lo que él asocia los caminos de salida de los respectivos bloques pero siempre al terminar el try/catch, salta a la sección finally. La sección de finally también es un bloque de código protegido, al igual que el try/catch:

   1: finally

   2: {

   3:     IL_0020:  nop

   4:     IL_0021:  nop

   5:     IL_0022:  endfinally

   6: }  // end handler

La única particularidad del bloque finally es que tiene que finalizar con la instrucción endfinally. De todas formas, la única función de un bloque finally es la de hacer de “cleanup”, es decir, de liberar todo recurso empleado en el try.

Conclusiones

Hemos visto cómo funciona una excepción por dentro para entender su comportamiento y su mecanismo. Siempre se recomienda que no se deben usar excepciones como mecanismo de control de flujo, por mala praxis en el desarrollo y por asuntos de rendimiento:

  • Cada vez que se produce una excepción, CLR va a buscar en toda la pila de llamadas el bloque catch que coincida con el tipo de la excepción producida. Esto, evidentemente genera un problema de rendimiento importante si sucede muy a menudo.
  • No hay que abusar de excepciones. Las justas y necesarias para tenerlo todo bajo control. El valor ideal como siempre, en el término medio. Ni muchas ni ninguna, las necesarias.
  • Si capturamos una excepción,  hagamos algo con ella. La podemos lanzar, tracear, modificarla para lanzar otra distinta… pero siempre debemos hacer algo con ella. Nunca dejar un catch vacío.
  • Por supuesto, el catch debe ser seguro. No debe tener hueco que se produzca una excepción nueva dentro de un bloque catch.
  • Por último, el bloque finally debe actuar como cleanup. Debe liberar memoria/recursos/IO que hayamos inicializado en el try. Ten en cuenta que siempre se va a ejecutar.

[W8] Scroll infinito ( II )

 

Continúo la serie de scroll infinito. En la implementación del artículo anterior, usaba un LoadItemsResult para devolver la carga asíncrona de la fuente de datos. Sin embargo, hay ocasiones en que esto se nos queda corto, como por ejemplo cuando ocurre una excepción en la carga o queremos limitar los elementos que se van cargando. Es por ello que crearemos un nuevo tipo que implemente IAsyncOperation<LoadMoreItemsResult> que nos permita tener más control sobre lo que devolvemos. Esta interfaz dispone de un método y una propiedad:

  • GetResults: Devuelve el resultado de la operación
  • Completed: Una propiedad de tipo IAsyncOperationHandler.

La idea es sencilla. Anteriormente teníamos un método que se encargaba de obtener los datos. Ahora nosotros vamos a hacer ese método a través de esa interfaz, permitiendo el control absoluto de lo que queremos devolver. Tendremos un método asíncrono que es el encargado de obtener los datos y añadirlos al IncrementalSource (que recordemos, es una ObservableCollection) tal que así:

Code Snippet
  1. private async void LoadItems(IncrementalSource<T> source, uint count)
  2.         {
  3.             try
  4.             {
  5.                 var result = await SourceManager.Load(count);
  6.                 foreach (var item in result)
  7.                 {
  8.                     source.Add(item);
  9.                 }
  10.  
  11.                 results.Count = count;
  12.                 asyncStatus = AsyncStatus.Completed;
  13.                 if (Completed != null)
  14.                     this.Completed(this, asyncStatus);
  15.             }
  16.             catch (Exception ex)
  17.             {
  18.                 // Gesti?n de excepciones
  19.             }
  20.         }

Básicamente lo que hacemos es cargar los datos y cuando ya lo están:

  • Notificar cuántos elementos se han cargado.
  • Lanzar el evento Completed para notificar que la carga se ha completado. Le el objeto actual como sender y un parámetro de AsyncStatus que lo asignamos como Completed, lo cual notifica al subscriptor del evento que la parte asíncrona ya ha terminado. El resto de la clase sería así:
Code Snippet
  1. public class DataAsyncLoader<T> : IAsyncOperation<LoadMoreItemsResult>
  2.     {
  3.         private AsyncStatus asyncStatus = AsyncStatus.Started;
  4.         private LoadMoreItemsResult results;
  5.  
  6.         public DataAsyncLoader(IncrementalSource<T> source, uint count)
  7.         {
  8.             LoadItems(source, count);
  9.         }
  10.  
  11.         public AsyncOperationCompletedHandler<LoadMoreItemsResult> Completed { get; set; }
  12.  
  13.         public LoadMoreItemsResult GetResults()
  14.         {
  15.             return results;
  16.         }
  17.  
  18.         public AsyncStatus Status
  19.         {
  20.             get { return this.asyncStatus; }
  21.         }
  22.     }

Y por último y recordando el artículo anterior, en el método dentro de IncrementalSource que teníamos la carga de elementos debemos cambiarlo por:

Code Snippet
  1. public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
  2.         {
  3.             return new DataAsyncLoader<T>(this, count);
  4.         }

Y ya está. Con esto estamos externalizando la carga de elementos y teniendo un control absoluto de lo que ocurre antes, durante y después de la carga.

[W8] Scroll infiinito ( I )

 

Una de las mayores bazas de una interfaz de usuario es cómo se muestran los elementos y cómo se van cargando. En el caso de Windows 8 (y Windows Phone) disponer de elementos en un ListView es algo común. Pero cuando llegamos al final de la lista, podemos adoptar dos alternativas:

  • Colocar un botón tipo “Cargar más elementos”
  • O hacer que automáticamente cargue los elementos según lo necesite. Evidentemente, este es el caso que vamos a comentar.

En primer lugar tenemos un ListView con un ItemSource asociado a un ObservableCollection<T>. Para detectar si hay más elementos, el ListView dispone de una propiedad llamada IncrementalLoadingTrigger. Esta propiedad por defecto está ajustada a Edge. Si queremos que no haya ningún tipo de carga incremental, hay que ajustarla a None.

En segundo lugar, tenemos que lograr que los datos nos indiquen cuándo es necesario obtener más elementos. Como hasta ahora usamos un ObservableCollection<T> como binding, necesitamos cambiarlo un poco. En este punto se introduce la interfaz ISupportIncrementalLoading, que se encargará de notificar al destino del binding si hay más elementos disponibles para cargar y un método para cargar los datos en función de la cantidad de elementos previamente cargados:

HasMoreItems: Indica si hay más elementos para cargar.

LoadMoreItemsAsync: Carga asíncrona de los nuevos elementos.

Partiendo de lo anterior, necesitamos crear un nuevo tipo que implemente la interfaz ISupportIncrementalLoading y la ObservableCollection<T> para poder bindear los datos. Veamos un ejemplo que yo he llamado IncrementalSource:

Code Snippet
  1. public class IncrementalSource<T> : ObservableCollection<T>, ISupportIncrementalLoading
  2.         {
  3.             private int VirtualCount { get; set; }
  4.  
  5.             public IncrementalSource()
  6.             {
  7.  
  8.             }
  9.  
  10.             #region ISupportIncrementalLoading
  11.  
  12.             public bool HasMoreItems
  13.             {
  14.                 get { return this.VirtualCount > 0; }
  15.             }
  16.  
  17.             public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
  18.             {
  19.  
  20.             }
  21.             #endregion
  22.         }

Sólo tenemos que rellenar el método de LoadMoreItemAsync., que devuelve un IAsyncOperation de LoadMoreResultItems. Esta estructura tiene un único campo Count que indica la cantidad de elementos que han sido cargados. De este modo se va notificando al destino del binding qué elementos se van cargando de forma asíncrona para que los vaya obteniendo y renderizando. Voy a adjuntar un ejemplo teórico de cómo se podría rellenar ese método:

Code Snippet
  1. public IAsyncOperation<LoadMoreItemsResult> LoadMoreItemsAsync(uint count)
  2.         {
  3.             return Task.Run<LoadMoreItemsResult>(
  4.                 async () =>
  5.                 {
  6.                     var result = await SourceManager.Load(count);
  7.                     foreach(var items in result)
  8.                     {
  9.                         this.Add(result);     
  10.                     }
  11.                     return new LoadMoreItemsResult() { Count = (uint)result.Count };
  12.                 }).AsAsyncOperation<LoadMoreItemsResult>();
  13.         }

Y si el ListView está dentro de un ScrollViewer, ya tendremos el scroll infinito funcionando.

[WCF] Metadatos en un WCF habilitado para AJAX

 

Si queremos añadir  un servicio WCF habilitado para AJAX, podemos hacerlo desde Añadir un nuevo item y seleccionar el elemento correspondiente:

image

Esto añade el servicio a nuestro proyecto. Sin embargo, cuando lo arrancamos podemos ver un mensaje que indica que la publicación de metadatos está desactivada para ese servicio. La página del servicio nos sugiere que añadamos lo siguiente a nuestro fichero de configuración, que en efecto sirve para habilitar los metadatos sin mayor problema:

<behaviors>
    <serviceBehaviors>
        <behavior name="MyServiceTypeBehaviors" >
            <serviceMetadata httpGetEnabled="true" />
        </behavior>
    </serviceBehaviors>
</behaviors>

Lo añadimos pero sigue sin habilitarse los metadatos Incluso muestra un mensaje de error indicando que el fichero de configuración es incorrecto. Si observamos el fichero de configuración (normalmente web/app.config) veremos que faltan algunos datos por configurar. Aquí voy a mostrar cómo debe quedar el .config para que la publicación de metadatos sea efectiva.

El error que nos muestra indica que la definición del servicio no incluye la dirección del endpoint. Por ello:

1 – Lo primero que hacemos es añadir el behaviour de nuestro servicio en la sección que indiqué anteriormente y añadirle la etiqueta para que reconozca los metadatos y habilitarla.

    <behaviors>
      <serviceBehaviors>
        <behavior name="TestWCFAjax.Service1AspNetAjaxBehavior">
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>

2 – Fijémonos en la definición del servicio que ha creado por defecto:

<service name="TestWCFAjax.Service1">
        <endpoint address="" behaviorConfiguration="TestWCFAjax.Service1AspNetAjaxBehavior"
          binding="webHttpBinding" contract="TestWCFAjax.Service1" />
      </service>

Lógicamente no encuentra un endpoint similar y ahí se produce el error. Pero, ¿dónde está? Fijémonos que anteriormente teníamos definido un behaviour y dentro un endpoint que hacía referencia a nuestro servicio. Ahora al cambiar esto, es nuestro servicio como tal quien tiene un behavior asociado y no su endpoint:

 <service name="TestWCFAjax.Service1" behaviorConfiguration="TestWCFAjax.Service1AspNetAjaxBehavior">
        <endpoint address="" binding="webHttpBinding" contract="TestWCFAjax.Service1" />

Con esto nuestro servicio ya arrancará con los metadatos habilitados.

[C#] Parallel.For

 

Considero que desarrollar algo, por simple que sea, requiere el máximo cuidado y esmero. Sin embargo si estamos hablando de incluir las prácticas de paralelización, debemos ser mucho más cuidadosos y conocer mejor el problema que tratamos resolver. Resolver algo de forma paralela no es trivial en ningún caso.

No obstante, .NET nos ofrece una serie de ayudas a través del framework para paralelizar nuestro código. Vamos a empezar viendo un bucle y un código muy simple:

Code Snippet
  1. for (int i = 0; i < itemList.Count(); i++)
  2.             {
  3.                 //Console.WriteLine(i);
  4.                 itemList[i] += (int)Math.Cos(i);
  5.             }

Tenemos un array de N elementos y a cada uno de ellos vamos a sumarle el coseno i-ésimo. Para paralelizar esto, tenemos que tener en cuenta el coste de la paralelización. Ese coste está derivado del esfuerzo que hay que hacer para lanzar y mantener los hilos o las tareas. Si este coste es demasiado alto (por ejemplo para bucles u operaciones muy pequeñas) compensa más usar siempre la versión secuencial. Vamos a ver cómo podemos paralelizar esto, empleando Parallel.For.

Parallel.For (y Parallel.ForEach) están incluidos dentro de Parallel-LINQ (PLINQ) por lo que no tenemos que instalar nada extra para poder usarlos. La sintaxis en este caso es sencilla:

Code Snippet
  1. var parallelResult = Parallel.For(0, itemList.Count(), (i) =>
  2.             {
  3.                 itemList[i] += (int)Math.Cos(i);
  4.             });

Pese a que actualmente dispone de 12 sobrecargas, muestro por ahora la más sencilla:

– Primero indicamos el índice de origen para la primera iteración del bucle.

– Después hasta dónde queremos llegar, de forma exclusiva.

– Por último, definimos un Action. El action es lo que se ejecutará.

Traduciendo, lo que queremos hacer es un bucle paralelo que vaya de 0 a itemList.Count() y que ejecute el coseno ié-simo de cada elemento. Fíjese como no tenemos dependencias ni variables compartidas. Más tarde veremos este punto. Nótese además como Parallel.For devuelve una variable de tipo ParallelLoopResult, que nos indica cuándo el bucle ha terminado de ejecutarse y cuando se ha roto la iteración antes de romper el bucle. Para obtener la medición de los tiempos que se muestran a continuación he usado un sencillo StopWatch y para medir la parte paralela, he usado el IsCompleted del ParallelLoopResult que me ha permitido esperar la ejecución paralela y poder cuantificar el tiempo trascurrido.

Code Snippet
  1. while (parallelResult.IsCompleted == false) ;

Por ahora comparemos resultados. Para ello simplemente he ejecutado el código anterior con distintos tamaños de lista, desde 1 elementos hasta 10.000.000. Para obtener mediciones un poco más precisas he ejecutado cada sección 4 veces y he hecho la media de los resultados, puesto que en algunos casos el resultado de las operaciones puede ser trivial debido a su bajo coste:

image

Y aquí tenemos la representación gráfica de los resultados, donde se puede apreciar mejor la diferencia de valores:

image

Podemos apreciar como en valores de un bucle con pocos elementos, el coste de paralelo normalmente es superior al de secuencial. Esto es debido al coste necesario para el mantenimiento de las task que forman la ejecución de la parte paralela. Pero a su vez notamos que en el momento en que el orden de los elementos va creciendo, el coste de la parte secuencial se vuelve exponencial mientras que la parte paralela, pese a que crece, dispone de un coste cercano al lineal.