miércoles, 14 de julio de 2010

Servicios web y componentes de cliente

En un post anterior vimos cuales eran los secretos y las consideraciones a la hora de trabajar con el control UpdatePanel.

Sin embargo hay ocasiones en las que un control UpdatePanel no satisface nuestros requerimientos por diversos motivos. De este modo, tenemos otras opciones que nos brinda ASP.NET AJAX como son los servicios web y los métodos web de página.

Un servicio web puede servir a distintos propósitos (no sólo para ayudarnos a la actualización parcial de una página) pero en esta entrada nos centraremos exclusivamente en esa funcionalidad. Siendo así, un servicio web se diferencia de un control UpdatePanel en lo siguiente:

  • Un servicio web no pertenece a una página aspx (a excepción de los métodos web de página aunque no es relevante).
  • Un servicio web (tampoco un método web de página) no lanza el ciclo de vida de una página .aspx.
  • El tamaño del envío y la respuesta de un servicio web es más reducido puesto que sólo hay que enviar unos mínimos datos al servicio y el servicio a su vez sólo devolverá el contenido solicitado.
  • En lo relativo a la representación parcial de una página, un servicio web es utilizado cuando el contenido que queremos actualizar no depende de los datos de la propia página o el contenido es literal (no queremos devolver un control de usuario con comportamiento de cliente ASP.NET AJAX).

Esto último es de vital importancia puesto que a grandes rasgos, un servicio web siempre devuelve texto, sólo texto. Por ejemplo, podríamos devolver el texto que resulta de renderizar un control pero no su comportamiento. De este modo, el servicio web nos devolvería la primera instantánea del control pero al intentar interactuar con el control, la página fallaría porque no conoce el control (no se ha declarado en la página .aspx, ni se ha incluido de forma dinámica a través de la colección de controles de algún otro control contenedor de la página y además si fuera necesario crear un componente de cliente a través de ASP.NET AJAX para su funcionamiento, tampoco se hubieran ejecutado sus scripts requeridos). En definitiva, si vamos por la vía de un servicio web o un método web de página, nuestro principal uso será devolver contenido literal que podremos después inyectar en el DOM de cliente, pero no podemos esperar por ejemplo, devolver un control Tab de ASP.NET AJAX Control Toolkit y que funcionen las pestañas.

Ahora la pregunta final que resolveremos en un siguiente post es ¿Cómo cargar un control que necesita componente de cliente – rutinas de javascript – y conseguir que su carga sea lo más óptima posible (esto es no utilizando un UpdatePanel)?

Un saludo!

Cuidado con el control UpdatePanel!

El control UpdatePanel hace posible la representación parcial de una página. Esto es que, con este control, es posible actualizar sólo cierto contenido de la página en el cliente. Para su utilización hay que tener en cuenta 2 cosas muy importantes y que si no se entienden correctamente podrían arruinar el funcionamiento esperado del control.

  1. Durante el proceso de la petición asíncrona que desencadena un control UpdatePanel, en el servidor se ejecuta todo el ciclo de vida de la página que alberga el control. Esto es que, aunque el resultado de la petición sólo sea la actualización de una parte de la página del cliente, en realidad la petición ha desencadenado:
    • El envío y posterior actualización del estado de vista.
    • Se ha cargado de nuevo la página en el servidor y ha ocurrido todo el ciclo de vida de la página (aunque insisto nuestro único propósito era actualizar sólo cierta parte de la página).
    • Se han ejecutado todos los manejadores declarados en el código de la página en el servidor que procedan (Page_Load, Page_PreRender, etc.)
  2. El control UpdatePanel tiene una propiedad que determina drásticamente su comportamiento y eficiencia, la propiedad es UpdateMode. La propiedad UpdateMode tiene 2 posibles valores: Always y Conditional. Por defecto el valor de la propiedad es Always y eso justamente y en mi opinión es un error puesto que, aunque en principio nos facilita la vida cuando no queremos indagar en exceso sobre el funcionamiento del control UpdatePanel, cuando hablamos en términos de rendimiento y control preciso de que partes de la página cliente son actualizadas, es un escollo que tendremos que tratar.

Para ilustrar de forma concreta la importancia de estas 2 propiedades, lo mejor será un ejemplo de su utilización. Supongamos una sencilla página con 2 controles de tipo UpdatePanel y en cada uno de ellos un control Literal y un botón.

<asp:UpdatePanel ID="UpdatePanel1" runat="server">
<ContentTemplate>
<asp:Literal runat="server" ID="Literal1" Text="Texto no asignado"></asp:Literal>
<br />
<asp:Button runat="server" ID="Button1" Text="Actualizar" />
</ContentTemplate>
</asp:UpdatePanel>

<asp:UpdatePanel ID="UpdatePanel2" runat="server">
<ContentTemplate>
<asp:Literal runat="server" ID="Literal2" Text="Texto no asignado"></asp:Literal>
<br />
<asp:Button runat="server" ID="Button2" Text="Actualizar" />
</ContentTemplate>
</asp:UpdatePanel>


Y con el siguiente código en el servidor:

    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
        Literal1.Text = System.DateTime.Now.ToString
    End Sub


    Protected Sub Button2_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button2.Click
        Literal2.Text = System.DateTime.Now.ToString
    End Sub


Con la página cargada, nuestra primera acción es pulsar el botón Button1. El resultado en pantalla es:

clip_image002

En principio todo parece correcto, pero con la ayuda de firebug vamos a poder ver los entresijos de la petición desencadenada por la pulsación del botón y ver como no es oro todo lo que reluce. La petición ha enviado toda la información de la página sólo para actualizar un literal con la hora actual. Vemos como se incluyen todos los campos del formulario (entre los que está el campo __VIEWSTATE – que puede ser enorme!). Aunque en este caso concreto nos parezca que no se ha enviado demasiada información (o al menos que es soportable el tamaño del envío), en páginas complejas el tamaño de los datos enviados puede ser mucho mayor y en ocasiones estaremos hablando de un “killer-application” o lo que también viene llamándose “matar moscas a cañonazos”.

clip_image004

Además, también es hora de examinar la respuesta del servidor a nuestra petición:

clip_image006

Ver como en la respuesta hay 3 bloques claramente diferenciados:



  • El contenido a actualizar para el control UpdatePanel1.

  • El contenido a actualizar para el control UpdatePanel2.

  • El contenido a actualizar para los controles Hidden que gestiona automáticamente ASP.NET (__VIEWSTATE, __EVENTVALIDATION, etc.)

En realidad el bloque 1 y 3 son innegociables, así funciona el control UpdatePanel y no podemos hacer nada para evitarlo, pero la pregunta es ¿Por qué se actualiza también el contenido de UpdatePanel2? De hecho, se actualiza al mismo contenido que tiene, así que además de “engordar” el tamaño de la respuesta de la petición, actualiza el control a su mismo contenido (aquí cabe mencionar que aunque parezca que no importa actualizar una parte del DOM del cliente a un mismo valor, en realidad bajo cuerda suceden eventos de cliente de ASP.NET AJAX, se tienen que eliminar elementos del árbol DOM de cliente y volverlos a crear, así que para un Literal no parece un problema, pero para contenido más complejo estaremos robando ciclos de CPU al cliente en un tarea que se puede evitar). Está en nuestra mano poder eliminar el bloque 2 (al que nadie invitó a la fiesta). Simplemente añadiremos la propiedad UpdateMode = “Conditional” al control UpdatePanel2. Con esta propiedad le estamos indicado al control UpdatePanel que sólo debe actualizarse si él mismo (o alguno de sus desencadenadores válidos – triggers) ha iniciado la petición.

<asp:UpdatePanel ID="UpdatePanel2" runat="server" UpdateMode="Conditional">


Veamos ahora como al pulsar de nuevo el botón Button1, la respuesta sólo incluye la actualización del control UpdatePanel1 y la actualización de los controles Hidden de ASP.NET (pero ya no incluye, ni toca, ni manipula, ni hace nada con el control UpdatePanel2).

clip_image008

A la hora de trabajar con UpdateMode y el valor Conditional, también hay que tener en cuenta las siguientes consideraciones. Si por ejemplo, como es nuestro caso, tenemos un primer UpdatePanel que siempre se actualiza con cualquier petición asíncrona de la página y un segundo UpdatePanel que sólo se actualiza si es él mismo quien desencadena la petición, el siguiente código puede hacer que en el servidor tengamos un valor en el control Literal distinto del control renderizado en el cliente.

clip_image010

Si nos fijamos, en la segunda pulsación del botón 1, podemos ver que al actualizar tanto el Literal1 (dentro de UpdatePanel1) como el Literal2 (dentro de UpdatePanel2) se produce una situación en la que el valor de Literal2 en el cliente no está coordinado con el valor de Literal2 en el servidor. Es decir, en el servidor el valor de Literal2 es la fecha actual, mientras que en el cliente Literal2 aún mantiene su valor original (“Texto no asignado”) porque no se ha actualizado (debido al valor de la propiedad UpdateMode a Conditional). Siempre que sea oportuno podemos forzar la actualización de un control UpdatePanel desde el servidor, con independencia del valor de su propiedad UpdateMode. Esto se hace a través del método Update(). Así, por ejemplo, aunque el control UpdatePanel2 se actualiza de forma condicional, el siguiente código fuerza a que se actualice siempre y que funcione como si tuviera el valor Always en su propiedad UpdateMode.

    Protected Sub Button1_Click(ByVal sender As Object, ByVal e As System.EventArgs) Handles Button1.Click
        Literal1.Text = System.DateTime.Now.ToString
        Literal2.Text = System.DateTime.Now.ToString
        UpdatePanel2.Update()

    End Sub


Como resumen a lo explicado se muestra una lista de las ventajas y desventajas más relevante del control UpdatePanel (en mi opinión, claro está!).




  • Ventajas



    • Actualización parcial de la página (no podría vivir sin ella, de hecho creo que es una de las características más demandadas en la llamada web 2.0).


    • Aunque el tamaño de la petición puede ser importante, siempre será menor que una llamada asíncrona convencional (PostBack).


  • Desventajas



    • Por defecto la propiedad UpdateMode tiene el valor Always que significa “actualiza todos los controles UpdatePanels de la página aunque con ellos no vaya la película siempre que se produzca una petición asíncrona).


    • Si no se tiene cuidado con la contención de los datos que envía un UpdatePanel (por dios no metas toda tu página dentro de un UpdatePanel a cholón!), el tamaño del envío y de la respuesta, lejos de ser beneficioso para la aplicación, puede ser un cuello de botella para nuestra aplicación del que después será difícil salir.

Un saludo!