viernes, 26 de junio de 2015

¿El ViewState de ASP.NET está poniendo lenta tu página?

Un desafío muy común entre desarrolladores y diseñadores web es crear sitios web que sean interactivos, con buen diseño, responsivos y que carguen rápido, de esta forma mantener al usuario feliz. ASP.NET es una plataforma de desarrollo fantástica, pero como desarrolladores podemos tener problemas de performance causados por el viewstate, sobretodo si trabajamos con .Net Framework 2.0.

¿Que es el viewstate?

El Viewstate es una técnica usada por ASP.NET para manejar los estados de los web forms a través de los postbacks. Un postback es un intercambio de datos de un formulario con el server. El viewstate de una página es, por defecto, almacenado en un campo oculto del formulario de la página web llamado __VIEWSTATE y es de donde pueden surgir los problemas:

< input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwULLTEwNzcyMTI4ODkPZBYCAgMPZBYGAgMPD2QWAh4Dc3JjBRVDb250cm9sL0dldEltYWdlLmFzaHhkAgcPDxYCHgRUZXh0BSJNw7NkdWxvIENhcnRlcmEgLSBNZWRpc3luIENvbG9tYmlhZGQCCQ8PFgIfAQUIQ3RyMDAxaXdkZBgBBR5fX0NvbnRyb2xzUmVxdWlyZVBvc3RCYWNrS2V5X18WAgULaWJ0bkFjZXB0YXIFC2lidG5MaW1waWFyA3FR8EtTrIE9o6Xpg7IeJ9eVFzg=" >

¿Porque es un problema?

El volumen de datos almacenado en este campo oculto puede ser muy largo. Esto implica que se incrementa la carga de la página y ya que todo el contenido del campo oculto debe ser intercambiado durante un postback, se puede ver un notorio aumento del tiempo de completitud del request HTTP.

Los desarrolladores web actualmente están dispuestos a pulir el sitio, dejándolo operativo para conexiones lentas, mantenido el user-friendly, todo esto para minimizar el tamaño de datos almacenado e intercambiado en el viewstate. A esto hay que sumarle que Google confirmó que el tiempo de carga de un sitio web es uno de los factores que influye en como aparecen en el SERP, lo que hace que el desarrollador web se incentive a que el sitio cargue rápido. Quizá lo que más importa, desde la perspectiva de SEO, es que los datos de un viewstate en general preceden al contenido de una página importante en el orden de origen y puede inflar significativamente el código de la página.

¿Qué podemos hacer?

1) En general, un sitio no debería basarse en el viewstate. Esto debe ser decidido en un comienzo ya que desactivarlo una vez el sitio está desarrollado puede traer resultados inesperados (y el sitio dejará de funcionar).

2) Considera el uso de otros controles de estado de formulario, por ejemplo session state, application state, manualmente poblar/restaurar campos ocultos, cookies. El ASP.NET Cache puede ser usado para almacenar datos o repoblar controles para prevenir vueltas innecesarias a la base de datos.

3) Si usas controles de servidor intenta restringir el uso de tipos simples, por ejemplo repetidores en vez data grid/view.

4) El Viewstate puede ser eliminado de una página web cuando no se usan formularios o controles de servidor (con el atributo runat="server"), sin embargo el desarrollador es el responsable de escribir el código para hacer los post de los formularios manualmente al estilo antiguo (Request.Form, por ejemplo http://www.w3schools.com/asp/asp_inputforms.asp).

5) Viewstate puede ser desactivado de una aplicación, página o control usando el tag enableViewState="false". Si desactivas el viewstate de toda la aplicación se mantendrá "cierto" viewstate en todas las páginas (como el viewstate es usado para manejar que controles requieren postback). Esta es la forma de comunicación entre ASP.NET Web Forms y el modelo de postback.

6) En la mayoría de los casos, basta con desactivar el viewstate de un control y asegurarse que están llegando todos los datos en cada postback, proveen una reducción suficiente en el tamaño del viewstate. Cuando se hace esto, se debe repoblar los controles en el evento Init, no en evento Load. Hacer el data binding durante el evento Load tiene el riesgo de sobreescribir los valores los cuales serán restaurados desde los Formularios durante el evento Load.

7) Puedes ver que páginas están usando de forma intensa el viewstate con una muy buena herramienta llamada ASP.NET View State Helper: http://www.binaryfortress.com/aspnet-viewstate-helper.
Explicación de las columnas en el herramienta:

  • Page Size: bytes que pesa la página de la columna Page URL.
  • View State Size: tamaño en bytes que pesa el viewstate y la cantidad de chunks si está dividido
  • View State %: del total de la página, cuanto % es del View State
  • View State %: del total de la página, cuanto % es del View State. Si es muy alto coloca amarillo.
  • Markup Size: tamaño en bytes del HTNL del código de la página que no es texto visible.
  • Markup %: del total del tamaño de la página, cuando corresponde a código no visible. El color amarillo por lo que leí acá el amarillo en esta columna significa que el valor está sobre un 90% o bajo un 10%.

8) Los ViewState muy grandes pueden ser fragmentada (chunk) en un número más pequeño de campos usando la opción maxPageStateFieldlength del web.config file. Esta opción permite setear la cantidad máxima en bytes. Por defecto es -1, por lo que no está dividido, pero si colocamos un entero, se dividirá hasta alcanzar ese valor. Por ejemplo si ponemos 40 se dividirá en trozos muy pequeños, pero si usamos 500 serán más grandes. Por ejemplo, usaremos chunks de 256 bytes por ejemplo quedaría así:

Si tenemos un ViewState:

<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTk2Njk3OTQxNg9kFgICAw9kFgICCQ88KwANAGQYAQUJR3JpZFZpZXcxD2dk4sjERFfnDXV/hMFGAL10HQUnZbk=" />

Si cambiamos maxPageStateFieldlength en el web.config para dejar 256 bytes:

<system.web>
...
<pages maxPageStateFieldLength="256">
...
</system.web>

Así se verá el ViewState:

<input type="hidden" name="__VIEWSTATEFIELDCOUNT" id="__VIEWSTATEFIELDCOUNT" value="3" />
<input type="hidden" name="__VIEWSTATE" id="__VIEWSTATE" value="/wEPDwUKLTk2Njk3OTQxNg9kFgICAw9kFgICCQ88" />
<input type="hidden" name="__VIEWSTATE1" id="__VIEWSTATE1" value="KwANAGQYAQUJR3JpZFZpZXcxD2dk4sjERFfnDXV/" />
<input type="hidden" name="__VIEWSTATE2" id="__VIEWSTATE2" value="hMFGAL10HQUnZbk=" />

En la herramienta ASP.NET ViewState Helper así se ven los chunks:


9) El modelo ASP.NET MVC no usa ViewState. Considera usar MVC para la capa interfaz de tu aplicación y mantener lo que tienes del modelo de Web Form para el negocio o acceso a datos. MVC lo puedes usar con el Framework .NET 3.5 en adelante.

10) ASP.NET 4.0 Web Forms incluye la propiedad ViewStateMode, el cual permite desactivar el viewstate por defecto y activarlo solo a los controles que lo requieran.

Esperemos que estas sugerencias ayuden a los desarrolladores que luchan por bajar unos pocos kilobytes de sus páginas .NET.

No confundir errores de ViewState con errores de programación

A veces, sucede que se le puede atribuir al ViewState, errores que no son de este, pero si de programación JavaScript o de código ASPX (VB.NET o C#). Recomiendo validar el ViewState y ver que si sube pero luego se mantiene, el problema puede ser otro. En mi caso subía hasta 12000 bytes si lo analizaba con ASP.NET ViewState Helper, pero pasado ciertos request se mantenía en ese valor.
Si sucede esto, el problema puede no ser el ViewState, sino un código erróneo en la aplicación, en mi caso era un error de código JavaScript y la forma de detectarlo fue usando las herramientas de Internet Explorer o Chrome, ambos con F12, usando el Generador de Perfiles en IE o Profiles en Chrome.

En IE aparece un árbol de llamadas y allí se ven los tiempos y el número de veces que se ejecuta cierta función del JavaScript. En mi caso, aparecía una función que se llamaba 131 mil veces en cierto momento. La solución fue comentar un línea de código.

Generador de Perfiles de IE 8.

Generador de Perfiles de IE 11.

En Chrome se debe escoger el tipo de Perfil y presionar START. Luego se verán las llamadas y tiempos de las funciones JavaScript. Con Control + F puedes buscar un nombre de función, por si tienes alguna pista de algo que podría andar lento.





Referencias


Traducción con aclaraciones y mejoras del artículo: http://www.freshegg.co.uk/blog/web-design/creating-lean-fast-web-pages-view-state