ASP.NET ViewState a fondo (I)

BinaryBag

El ViewState está presente en cualquier desarrollo con ASP.NET y puede ser un aliado o un enemigo. Para intentar que sea un aliado hay que conocerlo bien, saber cómo funciona y qué se puede hacer con él.

Este es el primero de una serie de artículos en el que nos meteremos a fondo con el ViewState, desde por qué surge, qué es y cómo es su estructura (lo que veremos en este artículo), hasta qué cosas podemos hacer con él, que ya veréis que son unas cuantas y bastante interesantes.

 

ViewState: el comienzo

In the beginning, was the command line… No, no nos remontaremos tan atrás, pero sí vamos a mirar un poco al pasado del desarrollo web para ver por qué nace el ViewState.

Las páginas web son servidas a través del protocolo HTTP, el cual carece de información de estado por razones intrínsecas a su diseño. Esto significa que no guarda información sobre la conexión anterior que se ha hecho desde un cliente dado, aunque esta conexión ocurriera hace 1 segundo. Cada vez que se hace una petición a través de HTTP se regenera la página desde cero y se envía al cliente, sin guardar la información que el usuario había introducido en su navegador en la interacción anterior.

El HTTP no fue pensado para el uso que se le da hoy en día, que es ser capaz de servir complejas aplicaciones web que prácticamente nada tienen que envidiar a aplicaciones clásicas de escritorio. HTTP fue diseñado para una cosa mucho más sencilla: enviar a un cliente (user agent en la jerga HTTP) recursos identificados por una URL y situados en un servidor. Los recursos que pueden viajar a través de HTTP son de cualquier tipo y gracias a esta gran flexibilidad se pueden construir las páginas tan completas que pueblan hoy Internet, con texto, imágenes, audio, vídeo, etc.

Con estos mimbres hay que trabajar, o dicho de otra forma: había que paliar esa carencia de información de estado intrínseca a HTTP para poder simular el concepto de una “sesión de usuario”, es decir, una interacción de un usuario con una aplicación en la que, basándose en la información previa y actual, la aplicación irá ejecutando su lógica hasta que esta sesión termine.

Existen un buen número de maneras de mantener la información de estado. Sin entrar en ellas, ya que no es el objetivo de este artículo, sí que al menos vamos a mencionar los métodos con los que contamos en ASP.NET:

  • Cookies
  • Campos ocultos
  • Query string
  • ViewState
  • ControlState
  • Session State
  • Application State
  • Cache
  • Profile Properties
  • HttpContext.Items

Todas ellas tienen sus pros y sus contras, escenarios donde su uso es más o menos ventajoso y su propia ración de buenas prácticas, pero centrándonos ya en el protagonista del artículo, el ViewState es un constructo provisto por la arquitectura de ASP.NET y es uno de los métodos más importantes para mantener la información de estado de un usuario (mantener su sesión).

 

Estructura interna

El ViewState está representado por estructuras diferentes dependiendo de en qué lado del roundtrip se encuentre, el cliente o el servidor. En el lado del cliente, el ViewState es un campo oculto que guarda la información de estado codificada en base-64.

A continuación se muestra una página con sólo un GridView que contiene datos de las dos primeras filas de la tabla Production.Product de la BBDD de esos grandes vendedores de bicicletas que son la gente de AdventureWorks :

clip_image001

El código ASPX del formulario es este:

   1: <form id="form1" runat="server">

   2: <div>

   3:     <asp:SqlDataSource 

   4:         ID="sdsAWorks" 

   5:         ConnectionString="Persist Security Info = False; 

   6:         server = localhost;database = AdventureWorks;

   7:         Integrated Security = yes"

   8:         DataSourceMode="DataSet"

   9:         SelectCommand="SELECT TOP 2 Name, ProductNumber, 

  10:         DaysToManufacture, ListPrice, SellStartDate 

  11:         FROM Production.Product"

  12:         runat="server">

  13:     </asp:SqlDataSource>

  14:         

  15:     <asp:GridView 

  16:         ID="gvProducts" 

  17:         DataSourceID="sdsAWorks" 

  18:         runat="server">

  19:         <RowStyle BackColor="#CEE8E7" />

  20:         <HeaderStyle BackColor="#5BA3D7" BorderColor="#0066FF" />

  21:     </asp:GridView>

  22: </div>

  23: </form>

Una vez renderizado el form, en el navegador cliente encontramos el campo oculto __VIEWSTATE en el HTML recibido , que tiene este aspecto:

   1: <input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" 

   2: value="/wEPDwUKLTM0NDU4NDM2NA9kFgICAw9kFgICAw88KwANAgAPFg />

   3: QeC18hRGF0YUJvdW5kZx4LXyFJdGVtQ291bnQCAmQMFCsABRYIHgROYW1

   4: lBQROYW1lHgpJc1JlYWRPbmx5aB4EVHlwZRkrAh4JRGF0YUZpZWxkBQRO

   5: YW1lFggfAgUNUHJvZHVjdE51bWJlch8DaB8EGSsCHwUFDVByb2R1Y3ROd

   6: W1iZXIWCB8CBRFEYXlzVG9NYW51ZmFjdHVyZR8DaB8EGSsBHwUFEURheX

   7: NUb01hbnVmYWN0dXJlFggfAgUJTGlzdFByaWNlHwNoHwQZKVtTeXN0ZW0

   8: uRGVjaW1hbCwgbXNjb3JsaWIsIFZlcnNpb249Mi4wLjAuMCwgQ3VsdHVy

   9: ZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRlMDg5H

  10: wUFCUxpc3RQcmljZRYIHwIFDVNlbGxTdGFydERhdGUfA2gfBBkpXFN5c3

  11: RlbS5EYXRlVGltZSwgbXNjb3JsaWIsIFZlcnNpb249Mi4wLjAuMCwgQ3V

  12: sdHVyZT1uZXV0cmFsLCBQdWJsaWNLZXlUb2tlbj1iNzdhNWM1NjE5MzRl

  13: MDg5HwUFDVNlbGxTdGFydERhdGUWAmYPZBYGAgEPZBYKZg8PFgIeBFRle

  14: HQFD0FkanVzdGFibGUgUmFjZWRkAgEPDxYCHwYFB0FSLTUzODFkZAICDw

  15: 8WAh8GBQEwZGQCAw8PFgIfBgUGMCwwMDAwZGQCBA8PFgIfBgUSMDEvMDY

  16: vMTk5OCAwOjAwOjAwZGQCAg9kFgpmDw8WAh8GBQxCZWFyaW5nIEJhbGxk

  17: ZAIBDw8WAh8GBQdCQS04MzI3ZGQCAg8PFgIfBgUBMGRkAgMPDxYCHwYFB

  18: jAsMDAwMGRkAgQPDxYCHwYFEjAxLzA2LzE5OTggMDowMDowMGRkAgMPDx

  19: YCHgdWaXNpYmxlaGRkGAEFCmd2UHJvZHVjdHMPPCsACgEIAgFkaB91DdG

  20: 5dfsk09w1hbqOusnzCpM="

Aún con un sólo grid que muestra dos filas, tenemos bastante información en el ViewState.

El primer paso para empezar a desentrañar lo que hay codificado en este campo es usar algún decodificador en base-64 de los que abundan por internet. Una vez decodificado a texto plano nos encontramos con esto:

clip_image002

Aquí podemos empezar a ver ya datos relacionados con el GridView, nombres de columnas, valores, tipos de datos, etc. Sin embargo, no vemos toda la información claramente, hay mucho ruido aún. Ahora veremos qué contiene este ruido, pero ya hemos visto al ViewState en acción desde el lado del cliente, guardando la información en el campo oculto__VIEWSTATE del HTML recibido en el navegador.

 

¿Y qué pasa en el servidor?

En el lado del servidor, el ViewState está implementado como un diccionario de pares clave-valor que contiene la información de estado (también contiene un hash para detectar si alguien ha manipulado el ViewState entre postbacks, de esto hablaremos en un próximo artículo). Cuando ASP.NET renderiza el HTML de una página, incluye el campo oculto __VIEWSTATE, serializa la información de estado que va a almacenar en él, la codifica en base-64 y la envía en ese campo oculto.

El hecho de que se serialice la información no es un detalle al que se le deba prestar poca atención, sobre todo desde el rol del arquitecto. Dado que en el ViewState se pueden almacenar otros datos aparte de la información de estado que incluye automáticamente ASP.NET, en algunos proyectos podría surgir el problema de que se ha decidido almacenar en él elementos que no son serializables, no se prepararon para que lo fueran, no pueden serlo por restricciones funcionales o técnicas, etc. Por tanto, no hay que pasar por alto ese punto al diseñar la arquitectura de una aplicación si se van a hacer usos específicos del ViewState que no sean el uso por defecto del runtime ASP.NET.

En este proceso interno del runtime de ASP.NET nos encontramos con interioridades poco documentadas de la arquitectura de ASP.NET, como la clase que se usa para serializar los datos, LosFormatter (Limited Object Serialization Formatter). Esta clase, de la que apenas existe documentación y que se encuentra en el espacio de nombres System.Web.UI, es capaz de serializar cualquier tipo de objeto (los mismos que el BinaryFormatter) en una cadena de texto ASCII compacta y codificada en base-64. Sin embargo, el LosFormatter está optimizado para serializar tipos sencillos como String, Integer, Array, Hashtable, ArrayList, Boolean, Pair y Triplet. LosFormatter, aunque creada para uso interno de ASP.NET, puede ser utilizada en cualquier desarrollo, con lo que si alguno estaba necesitando este tipo de serialización puede aprovecharse de ella 🙂

Hemos nombrado Pair y Triplet, dos clases de uso interno de ASP.NET y también escondidas en System.Web.UI, cuya finalidad es simplemente almacenar en una clase 2 ó 3 valores, exponiendo las propiedades First, Second (Pair y Triplet) y Third (Triplet).

Hablar de Pairs y Triplets es hablar ya de la estructura definitiva en la que se almacenan los datos en el ViewState. Pero primero que eso, para que los Pairs y Triplets tengan sentido, hace falta que revisemos en este contexto qué es lo que se guarda exactamente en el ViewState. Al principio hemos dicho que se guarda la información de estado, lo que permite que un usuario pueda mantener una “sesión” en una aplicación web. Esto es así a alto nivel, a nivel conceptual, pero ahora ya estamos al nivel de los átomos y en este nivel, la información de estado no es otra que la información de todos los controles existentes en la jerarquía de controles de una página. Es decir, en el ViewState se guarda toda la información de estado de toda la jerarquía de controles de la página y esta información se almacena en Pairs y Triplets. Recordemos que todos los controles de una página ASP.NET cuelgan del objeto Page, creando una jerarquía más o menos intrincada según la página.

La información de cada control se almacena en un Triplet, cuyo First contiene listas de clave-valor para almacenar valores de propiedades, por ejemplo: width-150px, height-50px. El Second contiene una lista con los índices de las posiciones que ocupan sus controles hijo en la jerarquía de controles de la página y Third almacena los Triplet de estos controles hijo. Casi nada. Para comprender mejor esta estructura podéis leer este post de Paul Wilson donde plantea un ejemplo que clarifica un poco las cosas.

Finalmente, para decodificar el ViewState y comprender mejor su estructura interna os recomiendo que os descarguéis el ViewState Decoder de Fritz Onion, una herramientas muy útil para trabajar con el ViewState.

Hasta aquí llega este primer artículo, en el próximo seguiremos ahondando en lo que se puede hacer con el ViewState, que aún queda mucha tela por cortar.

Cloud Computing: pros y contras

cloud computing

Hoy he leído este interesante estudio sobre cloud computing y lo recomiendo porque es de lo más sopesado y equilibrado que he leído al respecto de este tema tan de moda.

Recientemente hemos tenido que evaluar ofertas de proveedores que ya empiezan a tener en su catálogo el cloud computing y es un tema sobre el que hemos tenido que reflexionar y debatir.

Mis pensamientos sobre este modelo están en la línea de lo que se menciona en el estudio: el cloud computing es atractivo en muchos aspectos pero también tiene sus sombras y cualquier empresa que se plantee contratar SaaS debería hacer un estudio del estado del arte actualmente, las necesidades que se necesitan cubrir, los riesgos que se van a correr, la dimensión económica (a corto, medio y largo plazo), etc.

Digo esto porque hay responsables TIC con ganas de ser early adopters en este terreno y, como siempre, no deberían dejarse llevar por la tendencia sin sopesar bien beneficios y riesgos, que los hay.

Personalmente pienso que el tema está aún algo verde, sobre todo para delegar servicios estratégicamente críticos en él y me plantea algunas dudas, como confidencialidad de datos (probada, con auditorías externas como menciona el estudio), disponibilidad, flexibilidad en los desarrollos (Windows Azure aquí tiene mucho que decir), etc.

Las recientes caídas de Gmail han dado un toque de atención a algo que ya se da por superado como puede ser la disponibilidad 24×7. El impacto sobre una empresa que tenga su correo corporativo externalizado de esta manera es evidente.

Por otro lado, y desde el punto de vista del productor y proveedor del software + servicio, se abren nuevas posibilidades que ya muchas empresas están explorando. Sin duda es un mundo muy interesante para la comunidad de desarrollo por sus posibilidades, pero también va a ser muy exigente, ya que el cliente final va a tener una tolerancia muy baja ante cualquier inconveniente. Esto significa que quienes no ofrezcan un producto, servicio (o ambos) muy pulido van a tener serios problemas, más serios que con los desarrollos tradicionales. Sería sensato pensar si se está en condiciones de ofrecer esto antes de lanzarse a ofrecerlo sin poder asegurar una calidad de servicio determinada.

Como ya sabemos los grandes están apostando fuerte por el cloud computing y veremos muchos progresos en los próximos meses/años. Desde Microsoft con sus Generation 4.0 Data Centers, Live Services, Windows Azure, etc. a Google con ideas de posibles Data Centers en el mar, Google Apps, pasando por Amazon con su Elastic Compute Cloud.

Parallel Extensions: del laboratorio a la empresa

No hace mucho, Rodrigo Corral nos comentaba el advenimiento de Parallel Extensions y la importancia que iba a tomar a tomar un tema como la programación concurrente en el futuro próximo.

multicore

Dado que la Ley de Moore no puede continuar sin añadir unidades funcionales extra a los microprocesadores, el paralelismo va a dejar de ser un tema casi exclusivamente circunscrito a la investigación y va a servirse en la gran mesa del desarrollo de soluciones empresariales.

La razón para esto es que ahora las aplicaciones de negocio van a tener que correr en máquinas multi-core, o en máquinas con varios procesadores. Hasta hace pocos años una máquina con varios procesadores estaba prácticamente reservada a la supercomputación, pero hoy en día ya son el pan nuestro de cualquier PC de escritorio e incluso se van a empezar a ver en dispositivos móviles.

La programación concurrente y el paralelismo no son una materia trivial, la sincronización entre procesos no lo es y dividir el trabajo, asignarlo a varios procesadores, recoger los resultados y mezclaros para reconstruir la solución final (Scatter/Gather) tampoco lo es. Mucho se ha trabajado ya en este campo por parte de la comunidad investigadora y existen estrategias (memoria compartida, paso de mensajes), constructos teóricos (locks, semáforos, monitores) y herramientas (OpenMP, MPI, PVM)  que se han ido puliendo durante este tiempo. Cualquiera que haya trabajado con algo de lo anterior sabe bien lo no trivial que es plantear una solución en esos términos y el costo que supone sobre un desarrollo secuencial clásico.

Sin embargo, y como ya comenté en el citado post de Rodrigo, los desarrolladores de negocio no van a tener que lidiar (necesariamente) con esta complejidad, porque sería dar un paso atrás en productividad. Su tarea es seguir dedicando sus esfuerzos a lo que mejor saben hacer, que es desarrollar aplicaciones a alto nivel que resuelven problemas de negocio, no bajar al nivel de ponerse a controlar la ejecución concurrente de sus procesos.

Es aquí donde entra Parallel Extensions, una serie de librerías que vienen incluidas nativamente ya en el .NET Framework 4.0 y que van a permitir al desarrollador de negocio trabajar a alto nivel con la programación concurrente y el paralelismo. Parallels Extensions va a formar parte importante de .NET FX 4.0 (metido hasta la médula en la propia mscorlib.dll) y va a proveer de algunas abstracciones muy interesantes para el desarrollo a alto nivel, como el concepto de Task. A partir de ahora éste debería ser el concepto más usado por desarrolladores de negocio, no el de Thread. Una Task expone una API igual de rica (incluso más) que la de un Thread a la vez que se encarga de dividir el trabajo y lanzar un número óptimo de threads basándose en el número de CPUs o cores que tenemos (evitando el overhead que suponen los cambios de contexto cuando se crean más threads que CPUs disponibles).

Yendo aún más allá, sin llegar al nivel de granularidad de una Task, podemos trabajar a más alto nivel aún con la nueva clase estática Parallel y escribir código tan sorprendente como:

PForEach

Usando un bucle así, y con la salvedad de controlar que una iteración del bucle no dependa de datos compartidos con otra iteración, dejamos que el runtime de Parallel Extensions se encargue de paralelizar el trabajo creando las Tasks y encargándose de todo el proceso. A este nivel, el trabajo con la fontanería del paralelismo casi ha desaparecido.

Los componentes básicos de Parallel Extensions son la Task Parallel Library (TPL), que va a proporcionar las funcionalidades principales mencionadas arriba con las Tasks y la clase Parallel, etc. PLINQ para trabajar con LINQ to Objects en paralelo (y todo lo interesante que suena eso) y el Coordination Data Structures (CDS) que ofrece nuevas primitivas de sincronización, a más alto nivel que las mencionadas antes (semáforos, monitores…).

Todo esto está explicado en detalle y con ejemplos de código funcionando en este video de Channel 9, que recomiendo a cualquiera que le interese este tema.

Parallel Extensions abre unas posibilidades muy interesantes para el futuro próximo, dado el escenario descrito al principio de este post, y dota a .NET de una API muy sencilla para trabajar con temas tan ásperos como la programación concurrente. Habrá que estar atento a su evolución de aquí a la versión final que se incluya en .NET 4.0.

Optimización: buenas prácticas

La optimización es un tema muy importante en un proyecto. En
algunos más que en otros dada su propia idiosincrasia y el escenario de
producción en el que se van a desenvolver, ya que no es interesante invertir
esfuerzo en optimizar algo que ya funciona bien, no hay que llevar al extremo
el hecho de que todo se puede optimizar, pero obviamente siempre es un tema importante. 

Se puede (y se debe) tener la optimización en mente desde
las fases más tempranas, sobre todo si está claro que el proyecto va a requerir
estar bien afinado para dar los resultados esperados.  

Sin embargo, es interesante recordar que lo importante es que
los equipos de desarrollo pongan el esfuerzo en el lugar adecuado en el momento
adecuado. No es eficiente pretender mejorar cada bucle del código para arañar
unos milisegundos, cuando por otro lado pueden estar ejecutándose 20
procedimientos almacenados para recuperar datos, siendo la mayoría innecesarios
en ese momento.

En paralelismo de computadoras es conocida la Ley de Amdahl,
que como dice Wikipedia:

“…se usa para averiguar la mejora máxima de un sistema
cuando solo una parte de éste es mejorado.”

Esta ley es igual de útil en paralelismo que para la
optimización de aplicaciones en general, ya que prácticamente siempre hay un único
agujero negro de rendimiento que, una vez optimizado, mejora el resultado final
dramáticamente y hace desaparecer el problema que existía.

Teniendo esto en cuenta, lo importante a la hora de afrontar
la optimización de una aplicación, subsistema, caso de uso, pantalla,
funcionalidad, etc. es descubrir cuál es ese agujero negro responsable
principal de la caída del rendimiento y optimizarlo. No intentar optimizar a
ciegas. Para esto podemos ayudarnos de herramientas útiles como profilers, si
las trazas y pruebas sobre el código no son suficientes para revelar dónde se
encuentra el problema principal.

Para ilustrar lo anterior con un ejemplo clásico: imaginemos
un caso de uso en el que el tiempo de ejecución dedicado a la lógica de negocio
consume un 15% del total, el 10% se dedica a carga de controles e interfaz
gráfica y el 75% restante nos encontramos esperando por la carga de datos desde
la base de datos.

En este escenario resulta fácil ver como no es eficiente
intentar optimizar la lógica de negocio, ni la interfaz de usuario, sino
averiguar qué falla en la recuperación de datos: ¿puede ser código ADO.NET muy
ineficiente? ¿Puede ser que estemos recuperando datos que no se necesitan en su
totalidad en este caso de uso? ¿Puede ser que inspeccionando el plan de
ejecución del procedimiento almacenado que se invoca veamos que hace cosas innecesarias
o que podrían mejorarse bastante mediante mejor código T-SQL?

La buena práctica para plantear la optimización, por tanto, consiste
en averiguar dónde está el performance penalty y optimizarlo,  no en dedicar esfuerzos a otras partes cuyo
impacto en el cómputo total es sólo el 10% ó 15%, porque optimizando esto la
mejora será casi imperceptible, el problema está en el 75%.

Historia informática

Hace unas semanas fui a una especie de mercadillo de libros, lo
organizaba una biblioteca que exoneraba títulos muy antiguos de su
catálogo. Había una colección totalmente ecléctica de títulos, todos al
precio de 1 €. Me pasé por curiosidad y acabé encontrando dos libros
que me llamaron la atención: «Iniciación a la informática» Vol. I y II
🙂 Estos ejemplares datan del año 1971, ya ha llovido lo suyo, sobre
todo para el campo de la informática.

Lo curioso de los libros
no fue encontrar esas grandes diferencias entre núcleos de ferrita y
las RAM actuales, tarjetas perforadas, que los lenguajes más utilizados
fueran FORTRAN, COBOL, ALGOL y PL 1, etc. Eso ya sabía que lo iba a
encontrar, lo más curioso fueron las sorprendentes similitudes en
algunos aspectos que, a priori, parecerían imposibles.

Hay una
parte del volumen II dedicada al «uso del software» y en ella se
especifican las etapas de lo que el libro llama un «estudio
informático». La imagen muestra el esquema de las fases de dicho
estudio:

 

 

 

Como
se puede ver, la similitud con las fases de un proyecto de software
actual son realmente sorprendentes. Hablamos de 1971 y de un campo de
la informática que ha sido muy activo, con lo que el hecho de que 37
años después encontremos similitudes así llama la atención.

Entre
cosas como esta y la vuelta a la palestra de variaciones de la
arquitectura de mainframe y terminales tontos, puede resultar
interesante echar una ojeada a la historia de la informática y ver
cuántas cosas que creíamos obsoletas mantienen más vigencia de la que
pensábamos hoy en día 🙂

ASP.NET AJAX: posibilidades y escenarios de uso

La herramienta elegida por la mayoría de desarrolladores ASP.NET para implementar funcionalidades AJAX es ASP.NET AJAX. Encuestas como la de Nitobi sirven para dar una idea de lo que están usando los desarrolladores en general para implementar AJAX. En el mundo .NET en particular podemos ver en esta otra encuesta que el 73% de los desarrolladores usa el framework oficial de Microsoft para AJAX.

A colación de esto, resulta que recientemente (el mes pasado) Microsoft ha anunciado que va a incluir en Visual Studio soporte para JQuery, con lo que se amplian las opciones oficiales con la inclusión de esta librería. Sobre ASP.NET AJAX y JQuery os recomiendo este evento que organiza Second NUG y que tendrá lugar el 4 de Noviembre. Volviendo a las encuestas mencionadas, puede que éstas no tengan el mayor rigor científico, pero nos sirven para hacernos una idea.

 

 

 

En la encuesta de Nitobi la gran mayoría departicipantes respondieron que la razón por la que usan AJAX es para mejorar la experiencia de usuario. Es aquí donde ASP.NET AJAX cumple su función a la perfección, presentando una interfaz rica típica de AJAX, eliminando los refrescos completos de las páginas, el flickering, haciendo que la interfaz proporcione más feedback al usuario y sea más interactiva. En definitiva, mejorando la experiencia de usuario como se espera de AJAX.

Sin embargo, y pese a ser usado por la mayoría de la comunidad, a veces existe desconocimiento sobre su arquitectura, que usándola de la manera típica con ScriptManager y UpdatePanels difiere de lo que es la esencia de AJAX. En este post hablaremos sobre este modelo, sobre la alternativa más purista que provee el propio ASP.NET y sobrequé escenarios serían más propicios para una cosa u otra.

 

Partial Rendering

ASP.NET AJAX usa el modelo de Partial Rendering, también llamado AJAX Postback, que se basa precisamente en eso: un postback normal de ASP.NET llevado a cabo por el framework de AJAX y que, por tanto, respeta todo el ciclo de vida de una página ASP.NET. El runtime no hace nada diferente si ha recibido una petición desde un postback clásico o desde un postback AJAX hasta la etapa de renderizado. Durante el procesamiento de la petición se disparan todos los eventos a los que un desarrollador de ASP.NET está acostumbrado:

 

 Ciclo de vida de una página ASP.NET

 

Parte fundamental de esta arquitectura es el ScriptManager. Debe existir una instancia de este control en toda página ASP.NET que quiera implementar AJAX y al menos un UpdatePanel que envuelva la región de la página que se quiere refrescar.

En el lado del cliente, el ScriptManager es el encargado de averiguar si existen UpdatePanels, en cuyo caso envía las peticiones al servidor a través del objeto XMLHttpRequest del browser, no a través de un postback normal. En la petición se incluye toda la información de un postback típico más alguna información que el propio ScriptManager necesita del lado del servidor para procesar un postback AJAX. Aquí se ve que como en realidad no se está enviando menos información al servidor, como podríamos pensar al refrescarse sólo fragmentos de la página, y esto ya nos va dando información para plantearnos qué usos pueden ser indicados para este modelo de AJAX.

En el lado del servdidor el ScriptManager averigua si el postback que se está procesando es un postback AJAX o un postback normal. Para esto, inspecciona las cabeceras HTTP de la petición en busca de la información extra que le indica qué tipo de postback se ha producido. Una vez identificado como postback AJAX, invoca al método de renderizado ad hoc para AJAX en el que se buscan todos los UpdatePanels de la página, se renderiza el HTML para su contenido y se manda de vuelta al cliente (junto con alguna otra información). 

Aquí es donde viene el ahorro, ya que sólo se envía de vuelta al cliente el HTML de los UpdatePanels que se han actualizado. Entra en juego también aquí un buen diseño, ya que el ahorro puede ser mínimo si usamos la técnica a veces abusada de poner un UpdatePanel que englobe practicamente todo el contenido de la página o podemos mejorar significativamente el peso de la respuesta si identificamos exactamente las regiones que necesitan actualizarse y dejamos fuera HTML estático que no necesita viajar de vuelta. Una buena práctica es usar los UpdatePanels que sean necesarios para encapsular sólo las regiones que van a ser actualizadas en un determinado postback AJAX, así como jugar con la propiedad UpdateMode (establecerla a Conditional) de los UpdatePanels para evitar que siempre que haya un postback se refresquen todos, lo interesante es que cada uno se refresque refresque cuando el postback haya sido disparado por un control especifico que se encargue de su refresco.

De cualquier manera, siempre hay un ahorro en la respuesta, aunque no se apliquen estas buenas prácticas, pero si se aplican el ahorro será más importante. En global, contando con el peso extra de la petición y el ahorro de la respuesta, un postback AJAX ahorra ancho de banda respecto a un postback normal.

Hasta aquí Partial Rendering, como vemos, este patrón no tiene nada que ver con las llamadas asíncronas puras que se cuentan de AJAX, en la que se monta una petición sobre XMLHttpRequest, se ejecuta cierto servicio en el servidor y después se actualiza el HTML de la página con Javascript a través del DOM con la información de retorno del servicio.

 

Script Services y Page Methods

La alternativa existe, ya que en ASP.NET también podemos decidir saltarnos este modelo de Partial Rendering y decantarnos por una arquitectura puramente AJAX usando Script Services o Page Methods.

No los llamamos servicios web porque su ámbito de uso y su finalidad son bien diferentes: estos servicios nacen sólo para exponer datos concretos que satisfagan una llamada asíncrona desde el cliente, no como parte lógica de una arquitectura SOA más compleja. Aparte de esto, este intercambio se hace a través de HTTP y en formatos simples como JSON o XML, sin incluir información adicional de protocolos más pesados como SOAP ni metadatos que describan el servicio en forma de WSDL. Este tipo de servicios son conocidos como REST (Representational State Transfer) y nacen para satisfacer las necesidades de llamadas asíncronas AJAX, por lo que son más ligeros y menos ambiciosos que los servicios web.

Como hemos dicho, estos servicios son accedidos a través de HTTP, por lo que el formato de intercambio elegido puede ser desde JSON, XML o incluso texto plano. JSON (JavaScript Object Notation) suele ser el preferido porque aporta la ventaja de que codifica los datos en forma clave-valor y la función eval() de JavaScript es capaz de decodificar un string JSON en una colección de claves-valor. JSON introduce también algunas nuevas superficies de ataque para las páginas web, pero de eso habría que hablar en otro post.

Resumiendo lo que hemos dicho hasta ahora, una arquitectura AJAX basada en llamadas asíncronas puras consiste en exponer una capa de servicios REST que se encuentra encima de las capas del back-end de nuestra aplicación (típicamente la capa de negocio) y hacer llamadas a los mismos desde el browser cliente a través de JavaScript y XMLHttpRequest. Una vez que se recibe la respuesta hay que actualizar el fragmento de HTML de la interfaz que se iba a refrescar haciendo uso de un front-end construido en JavaScript y el DOM.

 

 

 

En ASP.NET existen dos maneras de seguir esta arquitectura. Una es a través de una modalidad especial de servicios web decorados con el atributo [ScriptService] y otra es a través de Page Methods. Los Page Methods son métodos estáticos que se crean en una página ASPX concreta y sólo pueden ser invocados desde dentro de esa misma página. Para marcar un método como Page Method hay que decorarlo con el atributo [Webmethod] y establecer EnablePageMethods=»true» en el ScriptManager.

Por su parte, los servicios web decorados con [ScripService] pueden ser llamados desde un script de una página cliente debido precisamente al atributo [ScriptService], cualquier método de un servicio web tradicional no podría ser invocado, el pipeline de ASP.NET devolvería un error. Este servicio también podría ser invocado a través de SOAP, pero esto se puede evitar especificándolo en el web.config. Esto último sería lo conveniente, ya que como dijimos antes, este tipo de servicios sólo debería tener sentido para proporcionarnos una determinada funcionalidad en un contexto AJAX. Por otro lado, el formato en el que se devuelve la información puede ser establecido dentro del atributo ScriptMethod (por defecto es JSON).

La cosa se complica más a la hora de recibir los datos en el lado del cliente, ya que es trabajo del desarrollador el recuperar toda la información de manera adecuada y rehacer el HTML necesario para actualizar la página. Por ejemplo, si es necesario pasar objetos desde el back-end hasta el cliente, luego habrá que recuperar los objetos codificados en JSON y guardarlos en sus objetos JavaScript espejo correspondientes en el cliente, por lo que se puede acumular bastante código JavaScript encargado de todas estas tareas. Otra cuestión que queda en nuestras manos es actualizar la interfaz de usuario. Podemos hacernos una idea de interfaces de usuario que no sean triviales de actualizar, como puede ser paginar un grid que muestre un listado. El parseo de la información de los objetos JavaScript y la creación, relleno y formateo del grid puede convertirse en una tarea árida, incluso con la ayuda de librerías JavaScript que faciliten la tarea. Otra cuestión que no hay que olvidar es la cantidad de código que se está escribiendo en un lenguaje interpretado como JavaScript, con la consecuente penalización de rendimiento y el hecho de que cada navegador proporciona su propio motor de JavaScript, con sus propios errores y rendimientos.

 

Conclusiones: escenarios de uso

Como hemos visto, ASP.NET AJAX con Partial Rendering es un patrón que presenta una ventaja clara: no se modifica en absoluto el modelo de desarrollo ASP.NET, lo cual es muy bueno para los equipos de desarrollo que, con poco esfuerzo pueden añadir funcionalidades AJAX a las aplicaciones web. Es un modelo poco intrusivo que exige sólo las destrezas propias del uso de la herramienta y las nuevas funcionalidades, pero no plantea un cambio de paradigma de desarrollo. Por otro lado, de cara al usuario, se consigue el fin de proporciona una interfaz rica típicamente AJAX.

Sus inconvenientes vienen precisamente derivados de no deshacerse del modelo de desarrollo de ASP.NET, ya que no se saca todo el partido posible al ahorro de ancho de banda de las peticiones, sino que se sigue haciendo un postback con mucha información al servidor.

Por su parte, los Script Services tienen su fuerte donde falla Partial Rendering, ya que sólo viaja al servidor una petición http para un Script Service y el servidor sólo devuelve la información necesaria para actualizar el fragmento de página que se quiere refrescar, sin postback. El ahorro de ancho de banda es máximo con una arquitectura basada en Script Services y la experiencia de usuario sigue obteniendo los mismos beneficios que con Partial Rendering. 

Será cada arquitecto quien tendrá que decidir qué se ajusta mejor para su escenario concreto: máximo rendimiento o máxima facilidad de desarrollo, considerando también la exigencia a nivel desarrollo con JavaScript que plantean cada altenativa.

Como normal general, la mayor parte de los escenarios que nos vamos a encontrar en las aplicaciones de gestión, son idóneas para usar Partial Rendering, ya que el rendimiento no es un factor clave y sí lo es el poder crear y gestionar lógica compleja que de solución a casos de uso que exigen código complejo del lado del servidor. Este tipo de aplicaciones no se van a beneficiar tanto de algún segundo menos en tiempo de respuesta como sí van a estar penalizadas por un incremento de la dificultad y el tiempo de desarrollo que implica tener que gestionar la interfaz de usuario y los datos que llegan al navegador de casos de uso complejos a base de JavaScript, aparte de lo inmantenible que puede tornarse este código a medio plazo.

Si cierto caso de uso produce páginas demasiado pesadas que necesitan optimización, una buena idea sería optar por rediseñar el caso de uso para crear interfaces más ligeras, separando funcionalidades que antes estaban juntas y poniéndolas en diferentes páginas, etc. Si por cuestiones de requisitos esto es inviable (todo puede ocurrir) podemos ir a un modelo híbrido, en el que el grueso de nuestra aplicación esté desarrollada con Partial Rendering y usar Script Services para los puntos que necesiten máximo rendimiento.

Arrancamos…

Hoy empiezo este blog y comienzo saludando a toda la comunidad y agradeciendo a la gente de Plain Concepts y Rodrigo Corral el hueco que me han hecho como vecino de Geeks.ms. El señor Toni Recio también tiene que ver en este asunto 🙂


Desde aquí escibiré sobre aspectos relacionados con el desarrollo de software, con temas encuadrados en cualquiera de sus etapas, e intentaré aportar mi granito de arena a la comunidad con experiencias y artículos que espero que resulten de provecho. 


Nos vemos en la próxima entrada metiéndonos ya en harina 🙂


¡Saludos!