You are on page 1of 184

Tecnologías ASP.NET 4.

0
(saltando desde la versión 2.0)

José Manuel Alarcón Aguín


Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
No está permitida la reproducción total o parcial de este libro, ni su tratamiento informático,
ni la transmisión de ninguna forma o por cualquier medio, ya sea electrónico, mecánico,
por fotocopia, por registro u otros métodos, sin el permiso previo y por escrito de los
titulares del Copyright. Diríjase a CEDRO (Centro Español de Derechos Reprográficos,
www.cedro.org) si necesita fotocopiar o escanear algún fragmento de esta obra.

Derechos reservados © 2009, respecto a la primera edición en español, por


Krasis Consulting, S. L.
www.krasis.com

ISBN: 978-84-936696-1-4
Depósito Legal: VG 961-2009

Impreso en España-Printed in Spain


Agradecimientos

La tarea de escribir un libro nunca es fácil, ni para el autor ni para quienes conviven
o trabajan con él. Por eso, entre otras razones, un libro nunca es exclusivamente obra
del que lo escribe.

Así, debo agradecer como siempre a mi familia el que hayan aguantado mis con-
testaciones secas al teléfono cuando interrumpían la escritura. Eso y que no me haya
pasado a verlos en unas cuantas semanas, claro.

En Krasis, si ya suele ser difícil hablar conmigo, mientras estaba con el libro ha sido
poco menos que imposible. Por ello vaya mi agradecimiento también por su paciencia
a Héctor, María, Pablo, Verónica, Fran, Yazmín, Dani y Eduardo.

A Pablo Iglesias hay que agradecerle especialmente su trabajo con las cubiertas del
libro. ¡Preciosas!

El bueno de Octavio Hernández, un sabio de la computación y autor también de


Krasis Press, revisó parte del material del libro desde la soleada California. Gracias
maestro.

La gente de Microsoft Ibérica, y en especial en este caso Beatriz y David, que


siempre se acuerdan de mi cuando hay algo de ASP.NET de por medio :-)

Finalmente, como siempre, a la principal sufridora de mis delirios frikis, Eva. Te


ρ = 1 - sin(θ)

vii
Contenido

CONTENIDO..........................................................................................................................ix
Presentación......................................................................................................................xiii

1. ASP.NET y sus versiones..................................................................................1


1.- La historia de ASP.NET hasta su versión 2.0........................................................1
2.- La versión 3.0 de .NET.................................................................................................3
3.- La versión 3.5 de la plataforma..................................................................................4
4.- Un Service Pack que es mucho más que un parche...........................................5
5.- .NET 4.0 y Visual Studio 2010....................................................................................6
6.- De qué trata este libro (y qué deja fuera).............................................................7
7.- En resumen........................................................................................................................8

2. FUNDAMENTOS DE AJAX......................................................................................9
1.- Interfaces de usuario avanzadas................................................................................10
2.- Un poco de teoría: el objeto XMLHttpRequest.................................................11
3.- Basta de teoría: vamos a la práctica.......................................................................13
4.- Problemas típicos de Ajax y sus soluciones.........................................................16
4.1.- Llamadas fuera de dominio..............................................................................16
4.2.- Gestión de errores y llamadas que no vuelven........................................17
4.3.- Envío de datos al servidor...............................................................................18
4.4.- Contenidos no actualizados debido a cachés........................................... 20
5.- Devolución de información: JSON...........................................................................21
6.- En resumen..................................................................................................................... 23

3. ASP.NET AJAX EN EL SERVIDOR.................................................................... 25


1.- Un primer ejemplo: mejora de una aplicación básica con AJAX.................. 26
2.- Postbacks parciales y repintados parciales de página....................................... 30
3.- El control ScriptManager............................................................................................31
4.- El control UpdatePanel............................................................................................... 32
5.- Modos de actualización parcial................................................................................ 33
6.- Disparadores.................................................................................................................. 34
7.- Indicación de progreso de las llamadas asíncronas............................................ 37
8.- Refrescos parciales periódicos................................................................................. 40
9.- Unicidad del ScriptManager....................................................................................... 42
10.- El control ScriptManagerProxy.............................................................................. 43
11.- Gestión de errores AJAX........................................................................................ 43
12.- Incompatibilidades de AJAX................................................................................... 48
13.- AJAX Control Toolkit.............................................................................................. 50

4. ASP.NET AJAX EN EL NAVEGADOR............................................................ 59


1.- Retrollamadas de red a métodos estáticos.......................................................... 60

ix
x  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

1.1.- Análisis de tráfico con un UpdatePanel.......................................................61


1.2.- Ejemplo optimizado con retrollamadas de red a métodos
estáticos................................................................................................................ 63
2.- Llamadas a servicios Web..........................................................................................67
2.1.- Definición de un servicio Web ASMX para ser llamado desde
JavaScript.............................................................................................................. 68
2.2- Creación de la página cliente para llamar al servicio..............................71
3.- Servicios de aplicación: Membership y Roles desde el navegador................ 75
4.- Referencias a scripts en páginas y controles........................................................81
5.- Optimización de uso de bibliotecas en ASP.NET 4.0....................................... 84
5.1.- Combinación de scripts.................................................................................... 87
6.- En resumen..................................................................................................................... 88

5. ENLAZADO A DATOS EN EL NAVEGADOR........................................... 89


1.- Concepto de plantillas de lado cliente...................................................................91
2.- Las bases para trabajar............................................................................................... 93
3.- Definición de la plantilla de productos................................................................. 94
4.- La clase DataView........................................................................................................ 95
5.- Pseudo-columnas y atributos especiales............................................................... 98
6.- Atributos sys condicionales..................................................................................... 100
7.- Atributos code para renderizado condicional.................................................... 101
8.- Enlazado de datos en tiempo real........................................................................ 103
9.- Vistas maestro-detalle: preparar el maestro..................................................... 105
10.- Vistas maestrro-detalle: preparar los detalles................................................ 106
11.- Devolver los datos modificados al servidor: contextos de datos............. 108
12.- La definición del método de guardado de cambios.......................................110
13.- Historia del navegador.............................................................................................112
14.- En resumen..................................................................................................................115

6. ASP.NET DYNAMIC DATA: INTERFACES DE DATOS A LA


VELOCIDAD DE LA LUZ.......................................................................................117
1.- ¿Qué es Dynamic Data?............................................................................................118
2.- Nuestro primer proyecto con Dynamic Data...................................................119
3.- Definir el modelo de datos......................................................................................121
4.- Añadiendo el modelo a Dynamic Data............................................................... 122
5.- Plantillas de interfaz de usuario............................................................................. 124
5.1.- Diseccionando una plantilla........................................................................... 125
6.- Plantillas para entidades........................................................................................... 129
7.- Plantillas para campos............................................................................................... 129
8.- Las rutas de las páginas dinámicas....................................................................... 132
8.1.- Parámetros individuales en las rutas.......................................................... 135
9.- Ampliando los metadatos del modelo................................................................. 136
9.1.- Mejorando la validación de campos............................................................ 138
9.2.- Validaciones personalizadas........................................................................... 140
10.- Plantillas de campos propias..................................................................................141
11.- Dynamic Data en páginas propias....................................................................... 143
12.- En resumen................................................................................................................ 146
Contenido  xi

7. FILTRADO DE DATOS AUTOMÁTICO CON


QUERYEXTENDER................................................................................................... 147
1.- El control QueryExtender....................................................................................... 147
2.- Tipos de filtros............................................................................................................ 148
3.- Creación de la página base para ejemplo........................................................... 149
4.- Primer filtro: búsqueda por nombre.................................................................... 152
5.- Filtrado por rangos de valores.............................................................................. 154
6.- Filtrado por valores de propiedades.................................................................... 154
7.- Parámetros de filtrado.............................................................................................. 155
8.- En resumen................................................................................................................... 157

ÍNDICE ANALÍTICO...................................................................................159
Presentación
En los últimos años la World Wide Web ha evolucionado mucho. Existe un verdadero
abismo tecnológico y conceptual entre aquellas primeras páginas estáticas —con
cuatro etiquetas para dar formato y unos pocos enlaces— y las actuales aplicaciones
Web 2.0 como Google Docs, Facebook o Live Maps. Hay tanta diferencia entre ellas
como entre los carruajes tirados por caballos y un Fórmula 1.
El mundo de mediados de los 90 tampoco era el mismo y, desde los 70 millones
de internautas estimados entonces a los casi 1.600 millones de 2009 (InternetWorld-
Stats.com), la cosa ha cambiado mucho.
Las diferencias estriban no sólo en lo que salta a la vista, sino también en lo
que no se ve. Las expectativas de los usuarios no son los mismas, los lenguajes de
programación tampoco. Antes era suficiente con mostrar texto plano y unos gráficos,
hoy es preciso habilitar una interactividad total entre los elementos de la pantalla
y el usuario.
Cuando todos accedíamos a la WWW usando módems de 28.8 Kbps era acep-
table esperar más de un minuto para recibir el contenido estático de una página.
Y dábamos gracias a los dioses por ello ;-) Hoy en día no sólo debe haber una
respuesta inmediata, sino que lo normal es que ni siquiera se evidencie en modo
alguno que ha habido un viaje al servidor. Las fronteras entre las aplicaciones de
escritorio y las aplicaciones Web son cada vez más difusas. ¡Bienvenidos al mundo
de AJAX y las RIA (Rich Internet Applications)!
ASP.NET es sin duda (y no es una opinión, sino un hecho) la plataforma de
creación de aplicaciones Web más productiva que existe. La base fundamental sobre
la que se sustenta esta tecnología y las diferentes características que ofrece, hacen
posible esta visión moderna, interactiva y escalable de la Red.
Este libro trata precisamente de esas tecnologías especializadas que marcan la
diferencia entre una aplicación Web corriente y otra de la era Web 2.0 y más allá.
ASP.NET 4.0 y Visual Studio 2010 nos traen las últimas mejoras de esta plata-
forma de desarrollo.

¿A quién va dirigido este libro?


Por lo que acabo de comentar el lector ya se dará cuenta de una cuestión importante:
este libro no es para principiantes.
El contenido va dirigido a programadores de ASP.NET 2.0 que quieren dominar
las principales tecnologías que aporta la última edición, ASP.NET 4.0. Se da por
hecho que el lector tiene unos conocimientos, cuando menos fundamentales, de esta
plataforma. Ahora bien, no se da por sentado nada en cuanto a las técnicas que se
explican en el interior, de las que se parte de cero para facilitar el aprendizaje.

xiii
xiv  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Es especialmente interesante que el lector conozca un poco el leguaje JavaScript


y una pizca de HTML, pues la tecnología AJAX se sustenta sobre ellos y vendrá
bien para comprender el código de ejemplo.

¿Qué temas se tratan en el libro?


Una gran parte del contenido se dedica a AJAX y todas las técnicas que hacen que
funcione.
Pero no te confundas, este no es el típico libro de AJAX que has visto por ahí. Va
mucho más allá del uso del UpdatePanel que todo el mundo conoce, para adentrarse
en la optimización para conseguir aplicaciones escalables.
Un primer capítulo se dedica a enseñar los fundamentos de AJAX sin el apoyo
de bibliotecas especializadas. Para que nos entendamos mejor: AJAX “a pelo”. Esto
te ayudará a comprender bien su funcionamiento primordial y podrás responder
mejor ante problemas que surjan más adelante en aplicaciones reales apoyadas en
código de otros.
ASP.NET AJAX es la biblioteca de Microsoft para crear páginas AJAX. El se-
gundo capítulo presenta esta tecnología para sacarle partido sin tener que salirse
de las técnicas habituales de todo programador .NET. Aprenderás a utilizar bien
sus controles de servidor, que ofrecen una grandísima productividad con un rápido
aprendizaje. Te proporcionará grandes ventajas y la usarás mucho, pero no está exen-
ta de problemas. Todo programador preocupado por el rendimiento y la escalabilidad
de sus aplicaciones debe ir más allá y no quedarse en este punto.
Por eso, el tercer capítulo se centra en las capacidades del lado de cliente de
ASP.NET AJAX. Se presenta la tendencia actual de las aplicaciones Web a trasladar
cada vez más procesamiento al navegador, intercambiado datos directamente con el
servidor. Aprenderemos lo necesario para poner en práctica esta visión, exponiendo
y recibiendo datos a través de servicios que se consumen desde el navegador con
JavaScript y el apoyo de ASP.NET AJAX.
En la cuarta parte del libro llevaremos el concepto de AJAX “puro” al extre-
mo gracias a las nuevas funcionalidades de plantillas para el lado cliente que nos
ofrece ASP.NET 4.0. Esta nueva tecnología abre las puertas a un desarrollo Web
super-eficiente que traslada toda la generación de la interfaz de usuario al navegador,
dejando el servidor como un intermediario para mover datos.
El quinto capítulo se centra en la nueva tecnología de generación de interfaces
de gestión de datos: Dynamic Data. Gracias a ella podemos conseguir en minutos
completas interfaces de administración de bases de datos para crear los típicos
“Mantenimientos”. Pero como veremos, la tecnología va mucho más allá, propor-
cionándonos total flexibilidad para hacer lo que queramos en la gestión de datos
sin apenas escribir código. Tendrás a tu alcance un nuevo nivel en la escala de la
de productividad.
La última parte del libro se centra en explicar un conjunto de controles Web
destinados a crear interfaces para filtrado de datos. Se trata de los QueryExtender
Presentación  xv

y las clases relacionadas con éstos. Con ellos, nuevos en ASP.NET 4.0, es muy sen-
cillo conseguir avanzados sistemas de filtrado de información sin tener que escribir
código. Combinándolos con los controles enlazados a datos podemos crear complejas
páginas con listados de información en minutos.

Las herramientas que necesitas para trabajar


Hace poco, después de una charla que impartí, tuve la oportunidad de hablar un
buen rato con un par de emprendedores del mundo TIC que se me acercaron. Tras
un tiempo de experiencia laboral por cuenta ajena decidieron volar solos, y un par
de meses antes habían constituido una empresa para desarrollar aplicaciones Web.
Me dijeron que se habían decidido a trabajar con PHP en lugar de con ASP.NET
por que “en PHP es todo gratis y para programar con ASP.NET necesitamos pagar
licencias a Microsoft.”.
¡Qué confundidos estaban! Y no sólo en esta afirmación, sino con otros muchos
mitos y leyendas equivocados que existen sobre ASP.NET y sobre lo que algún día
tengo que escribir largo y tendido. Menos mal que dieron conmigo para sacarlos de
su error ;-)
Para desarrollar con ASP.NET, tanto aplicaciones comerciales como para cual-
quier otro uso, no es necesario pagar ni un solo euro a Microsoft.
Visual Studio dispone de unas ediciones especiales llamadas Visual Studio
Express Edition que son gratuitas y de libre descarga. Apenas tienen limitaciones
para desarrollar y en concreto la versión especial para desarrollo Web, Visual Web
Developer Express, tiene toda la funcionalidad disponible en esta versión gratuita.
Para la parte de desarrollo de bases de datos Microsoft ofrece también una versión
Express de su gestor: SQL Server Express. Sus limitaciones son que sólo le está
permitido ocupar 1 GB de RAM para caché de datos, utilizar un único procesador
de la máquina (con los núcleos que tenga éste, da igual) y el tamaño máximo de las
bases de datos que puede manejar la licencia es de 4 GB. Son unas limitaciones bas-
tante amplias y es difícil llegar a superarlas en aplicaciones comunes en la PYME.
Ofrece herramientas adicionales de administración y de reporting entre otras, y es
perfecta para cualquier aplicación de gestión o para Internet. Y por supuesto sigue
siendo gratis aunque nuestras aplicaciones sean comerciales.
Las últimas versiones de Visual Web Developer Express y de SQL Server Express
se pueden descargar libremente desde http://www.microsoft.com/express/. Te las
recomiendo para practicar las explicaciones del libro.
Si quieres funcionalidades de trabajo en equipo, desarrollar para Office o para
móviles o poder depurar aplicaciones en remoto, puedes actualizarte a las ediciones
comerciales de Visual Studio. Toda la información aquí: http://www.microsoft.com/
visualstudio/.
Para aplicaciones empresariales de gran tamaño están disponibles las otras
ediciones comerciales de SQL Server. Consulta sus características en http://www.
microsoft.com/sqlserver/.
xvi  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

El código fuente de ejemplo


Todos los ejemplos y demos desarrollados en el libro se pueden descargar desde la
web de la editorial. Visita www.krasispress.com y busca el libro en el catálogo, bien
navegando por las categorías o con la caja de búsqueda.
En la ficha del libro existe un enlace para descargar los ejemplos de código.
Descárgate el archivo en formato ZIP y descomprímelo en cualquier carpeta de
tu ordenador. Para abrir los ejemplos desde Visual Studio lo mejor es usar la opción
Archivo·Abrir·Sitio Web y elegir la carpeta del ejemplo en el que tengas interés. Por
el nombre se deduce fácilmente a qué parte del libro corresponden.
Para los ejemplos que usan datos he empleado la archiconocida base de datos
Northwind. La puedes descargar en diversos formatos desde el sitio de descargas de
Microsoft. Vete a http://download.microsoft.com y una vez allí introduce la palabra
“Northwind” en el cuadro de búsqueda:

Figura 1.- Descarga de la base de datos Northwind

Se trata de una base de datos muy antigua, por eso pone que es una descarga
para SQL Server 2000, pero no te preocupes pues te funcionará bien con cualquier
versión moderna del gestor de datos. La he usado porque es la más popular entre los
programadores de .NET, y hay una alta probabilidad de que la conozcas ya.
Existe una versión nueva de esta base de datos, creada por la comunidad, que
puedes descargar desde http://www.codeplex.com/NorthwindCommunity/. Es un
proyecto reciente que trata de actualizar un poco el ejemplo original, pero no te
aseguro que los cambios que hayan hecho vayan a funcionar con los ejemplos del
libro, así que lo dejo a tu criterio, pero puedes probar.
Presentación  xvii

Contacto con el autor y la editorial


Puedes encontrarme y contactar conmigo a través de mi blog sobre desarrollo Web
en www.jasoft.org. Ahí publico constantemente todo tipo de consejos, noticias y
vídeos sobre el desarrollo para Internet con ASP.NET. Te recomiendo que lo visites.
Me encanta recibir comentarios y críticas constructivas, pero no me gustan los que
sólo se acuerdan de mi cuando necesitan algo “¿capisce?” ;-)
La web de la editorial es www.krasispress.com. Desde allí puedes ponerte en
contacto con el equipo editorial cuando quieras.
Dentro de Krasis existe el proyecto campusMVP (www.campusmvp.com) del que
probablemente hayas oído hablar. Se trata de formación on-line con cursos creados
y tutelados por conocidos MVP de Microsoft, para que tú y tu equipo os forméis a
vuestro ritmo y desde cualquier lugar preguntándole a los que más saben. En cam-
pusMVP tenemos un boletín mensual de noticias, trucos y “frikadas” varias que tiene
varios miles de suscriptores encantados de recibirlo. Te recomiendo que te suscribas.
También hemos puesto en marcha una página en Facebook (http://go.krasis.com/Fa-
cebook) con actualizaciones frecuentes sobre el mundo Microsoft y sus tecnologías,
enlaces a artículos interesantes, noticias, vídeos prácticos, ofertas exclusivas para
“fans”, etc... Si estás en esta red social no olvides hacerte fan de la página.

¡Comencemos!
Gracias por tu interés en este libro. Espero que el esfuerzo de escribirlo haya valido
la pena y que tras haberlo leído estés en condiciones de crear aplicaciones Web de alta
calidad, escalables y sacando todo el partido a las últimas tecnologías Microsoft.
capítulo
1

ASP.NET y sus versiones

¿Por qué comenzar un libro que no está dirigido a principiantes con una cuestión,
en apariencia, tan insignificante como ésta?... Pues porque no es un asunto trivial
en absoluto.
Como veremos, en ASP.NET los saltos en la numeración de las versiones no se
corresponden en realidad con los cambios cuantitativos que cabría esperar de éstos,
lo que causa gran confusión entre los programadores. Dada la cantidad de caracte-
rísticas existentes en esta potente plataforma de desarrollo Web, es muy importante
saber qué hay disponible en cada versión, e incluso en cada actualización dentro de
una versión. Por ello vamos a hacer una composición de lugar antes de ponernos a
trabajar.

1.- La historia de ASP.NET hasta su versión 2.0


La primera versión de ASP.NET, la 1.0, apareció en el mercado en enero de 2002,
junto con la edición inicial de Visual Studio para la plataforma .NET. Antes de este
lanzamiento oficial, hubo diversas Betas en las que aún la tecnología llevaba el
nombre de ASP+. Éstas estuvieron disponibles para experimentar desde mediados
de 2000, por lo que en realidad la plataforma tiene mucho más recorrido del que se
desprende de las fechas oficiales.
La idea era sustituir al ASP 3.0 clásico -por eso le dejaron un nombre parecido,
aunque no tengan nada que ver- y ofrecer un entorno de desarrollo para el servidor
con grandes ventajas sobre lo existente en el mercado. Un punto importante era
tratar de ofrecer los mismos paradigmas de desarrollo que las tradicionales aplica-
ciones de escritorio, pero aplicados a la Web. Esto implicaba sobre todo orientación
a eventos y a objetos, uso de lenguajes compilados, independencia automática del
navegador y productividad, mucha productividad. Con ASP.NET se empezaron a
desdibujar los límites entre cliente y servidor en el desarrollo Web.

1
2  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Nota:
Obviamente sin olvidarnos del inexcusable hecho de que hay una red de comunicaciones de
por medio, con lo que ello implica en cuanto a transferencia de datos y velocidad, lo cual sigue
siendo muy importante, claro.

El nuevo paradigma de desarrollo traído por los WebForms y la técnica de


Postback, realmente innovador y tan característico de ASP.NET, hizo que desarrollar
un formulario Web no fuese demasiado diferente de hacerlo para una aplicación de
escritorio. Todas las características de productividad de esta primera versión con-
virtieron rápidamente a ASP.NET en una plataforma muy popular, pues permitía
a los desarrolladores conseguir resultados impresionantes con muy poco tiempo y
esfuerzo.
En abril de 2002, poco después de un año, Microsoft actualizó mínimamente
la plataforma sacando ASP.NET 1.1 y Visual Studio .NET 2003. En esta ocasión
se incorporaron los controles móviles para el desarrollo Web orientado a PDA y
teléfonos móviles, así como la validación automática de los elementos de la interfaz
de usuario. Una actualización menor.
El verdadero punto de inflexión en lo que se refiere al ámbito de desarrollo
Web llegó en noviembre de 2005 (febrero de 2006 en España). En esta fecha se
lanza ASP.NET 2.0 junto con Visual Studio 2005. Este sí que constituyó un cambio
revolucionario dentro de ASP.NET; uno que influiría definitivamente en todas las
versiones posteriores. Con todo el feedback recibido por Microsoft durante los cinco
años previos, el equipo de desarrollo le dio un vuelco completo a la plataforma. Se
introdujeron tantos cambios y características que se necesita un libro entero1 para
explicarlas. Las más relevantes son las siguientes:
• Nuevo modelo de compilación y separación de código e interfaz, que es un
cambio de los propios fundamentos de trabajo de la plataforma.
• Precompilación y despliegue de sitios Web para obtener un máximo ren-
dimiento desde el primer momento. Soporte de sistemas de 64 bits.
• El modelo de proveedores, con sus implicaciones en cuanto a extensibilidad
de la plataforma.
• Acceso a datos, incluso en varias capas SOA, sin necesidad de escribir có-
digo, gracias a un modelo declarativo.
• Controles enlazados a datos de gran potencia, como las nuevas y mejoradas
rejillas GridView, y controles de visualización y edición integrada como
FormView o DetailsView.
1
El autor de este libro ha escrito también otra obra dedicada a explicar con detalle todas las tec-
nologías de ASP.NET 2.0, y que se puede adquirir en www.krasispress.com.
ASP.NET y sus versiones  3

• Independencia de la interfaz de las páginas de su maquetado (Master Pages)


y de su aspecto (Temas y Skins), fomentando la separación más absoluta entre
interfaz y funcionalidad.
• Interfaces de usuario para control de la seguridad mediante controles de
arrastrar y soltar. Puedes construir toda la seguridad de tu aplicación sin
escribir código alguno.
• Creación de portales personalizables mediante WebParts.
• Infraestructura de instrumentalización y monitorización de aplicaciones.
• Nuevas técnicas de localización y globalización de aplicaciones.
• Páginas asíncronas para sitios altamente escalables.
• Controles de navegación.
• Servicios de personalización de preferencias de usuario.
Este salto a la madurez de la plataforma abrió un nuevo mundo de posibilidades
de desarrollo con alta productividad para sistemas Web empresariales, pero hizo
que todos los programadores de la versión anterior tuvieran que actualizar sus co-
nocimientos.
La cuestión más importante, llegados a este punto, es señalar que el núcleo de
ASP.NET es exactamente este mismo, el 2.0, en el resto de versiones que han
salido desde entonces. Esto tiene una transcendencia fundamental, porque podemos
afirmar que si conoces bien ASP.NET 2.0 conoces todo lo necesario para sacarle
partido a las siguientes versiones, porque en esencia son la misma, como enseguida
veremos. Sin embargo, si provienes de la versión 1.x tendrás que aprenderlo todo
casi desde cero.

2.- La versión 3.0 de .NET


La versión 3.0 de la plataforma .NET se presentó en sociedad en noviembre de 2006,
justo un año después de la anterior. En realidad, esta versión 3.0 no era otra cosa
que la plataforma .NET 2.0 junto con cuatro nuevas API especializadas que estaban
construidas sobre ésta:
• Windows Communication Foundation (WCF): para la creación de sis-
temas distribuidos basados en conceptos SOA (arquitecturas orientadas a
servicios). A pesar de lo que pueda parecer por el poco afortunado nombre
—que confunde a algunos desarrolladores—, estos servicios no están atados
en absoluto a la plataforma Windows, sino que siguen los estándares de la
W3C y pueden interoperar con cualquier otro sistema operativo y lenguaje
de programación.
• Windows Workflow Foundation (WF): que permite el desarrollo de sis-
temas adaptables basados en flujos de trabajo tanto secuenciales como de
estado, persistentes en el tiempo y con otros servicios añadidos.
4  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

• Windows Presentation Foundation (WPF): la nueva API para creación


de interfaces de usuario vectoriales avanzadas. Va mucho más allá de los
sistemas tradicionales de ventanas para ofrecer un paradigma mixto entre el
escritorio y la Web. Un subconjunto de WPF, multiplataforma y específico
para la Web, es lo que se conoce como Silverlight.
• Windows CardSpace: un sistema cliente para la gestión de identidades en
Internet y en la empresa. Es, con diferencia, la API de la versión 3.0 que
menos adopción ha tenido.
Si bien todas estas API adicionales son muy importantes y abren muchas vías de
mejora en la programación de sistemas empresariales, lo cierto es que no dejan de
ser añadidos a la plataforma, que dependen de ésta, pero el núcleo sigue siendo
la versión 2.0 sin cambios de ningún tipo.
En lo que respecta al desarrollo Web y ASP.NET, la versión 3.0 no aporta carac-
terística nueva alguna. Obviamente, podemos sacarle partido a alguna de estas API
para añadir características a nuestras aplicaciones Web (por ejemplo, un servicio
Web creado con WPF o un flujo de decisión basado en WF), pero en lo que se
refiere a la plataforma ASP.NET, en ese momento sigue siendo la misma que había
un año atrás.

3.- La versión 3.5 de la plataforma


Mientras tanto, el mundo de las aplicaciones Web empieza a ver la invasión de AJAX.
Estos desarrollos, a los que dedicamos una gran parte de este libro, se basan en la
interacción estrecha con el usuario desde la interfaz del navegador, aparentando en
la medida de lo posible que no hay comunicación visible con el servidor. La idea es
que las aplicaciones Web se parezcan más aún a las de escritorio, sin recargas de
páginas y dando la sensación de que todo ocurre en el equipo del usuario.
Microsoft no puede permanecer ajena a esta tendencia que ellos mismos habían
creado (como se explica en el primer capítulo de AJAX), así que en enero de 2007
aparece una API de manera independiente al desarrollo de la línea principal de
.NET: ASP.NET AJAX, por aquel entonces conocida con el nombre en código de
“Atlas”.
Esta API para crear interfaces web de alta velocidad de respuesta y sin recarga
de páginas, es un éxito instantáneo y se puede instalar paralelamente a ASP.NET
trabajando sobre la versión 2.0 existente y con Visual Studio 2005.
Unos meses después, en noviembre de 2007, aparece Visual Studio 2008, que
incluye la versión 3.5 de la plataforma .NET. Esta versión sí que contiene cambios
sustanciales en los lenguajes C# y VB y en los correspondientes compiladores. La
mayor parte de dichos cambios tienen que ver con añadidos para dar soporte al nuevo
lenguaje integrado de consultas LINQ (Language INtegrated Queries). Es decir, el
núcleo de la plataforma sigue siendo el mismo (versión 2.0), pero con modificaciones
en los lenguajes y algunas API añadidas, en especial las relacionadas con LINQ.
ASP.NET y sus versiones  5

Para acabar de liar las cosas, aunque la versión de la plataforma es la 3.5, la de los
lenguajes no coincide. Por ejemplo, la versión de C# aparecida entonces es la 3.0, lo
que añade más confusión para los programadores.
En este momento se incorpora oficialmente el soporte para AJAX a la parte de
desarrollo Web, por lo que a partir de entonces podías contar con que la plataforma
tendría incorporado AJAX sin necesidad de instalarlo de manera separada. Además,
para dar soporte a LINQ desde aplicaciones Web se añadió una biblioteca llamada
System.Web.Extensions.dll que incluía tres nuevos controles: LinqDataSource,
ListView y DataPager.
Por lo tanto, lo que se dio en llamar ASP.NET 3.5 era en realidad lo siguiente:
ASP.NET 2.0 + ASP.NET AJAX + 3 controles
De nuevo importantes mejoras globales, gracias a LINQ para el acceso a datos,
pero el mismo núcleo en lo que al desarrollo Web respecta. La interfaz de trabajo de
Visual Studio 2008 ofrece también algunas características nuevas de productividad
para la Web, sobre todo el soporte de Intellisense y depuración para el lenguaje
JavaScript.

4.- Un Service Pack que es mucho más que un


parche
¿Qué tendrá de especial el mes de noviembre para Microsoft? No lo sé, pero si te has
fijado, es el mes en el que desde hace muchos años salen todas las grandes versiones
de sus plataformas de desarrollo :-).
Así, en noviembre de 2008 (como no podía ser de otra manera) aparece el Service
Pack 1 para .NET 3.5, es decir, la nueva versión de la plataforma: .NET 3.5 SP1.
Tradicionalmente, los Service Pack están pensados para agrupar en una sola des-
carga una recopilación de ajustes, resoluciones de fallos y parches de seguridad con
el objeto de facilitar su aplicación a los usuarios. En realidad no deberían incluir
nuevas funcionalidades. Incluso la propia Microsoft los define de esta manera: http://
support.microsoft.com/sp. Pero lo cierto es que en diversas ocasiones el gigante de
Redmond los ha utilizado para agregar grandes bloques de funcionalidad a algunos
productos. Este fue el caso del SP1 para .NET 3.5.
En esta actualización, Microsoft incluyó novedades en todos los ámbitos, siendo
la más visible de todas ellas la aparición de Entity Framework, su nuevo ORM para
acceso a datos.
En el ámbito del desarrollo Web aparecieron también varias características, aun-
que todas se sustentaban sobre ASP.NET 2.0. Entre ellas se encontraban:
• Dynamic Data: un nuevo sistema de creación automática de interfaces de
usuario Web a partir de modelos de datos.
• Control EntityDataSource: para dar soporte a acceso a datos declarativo a
través de Entity Framework.
6  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

• ADO.NET Data Services: creación y exposición automática de servicios


Web para acceso a datos en modo REST, fundamentalmente basándose en
Entity Framework.

• Enrutamiento: soporte para definición dinámica de rutas virtuales en el


servidor y su mapeado a archivos existentes o no. Especialmente pensado
para dar soporte a ASP.NET MVC, del que hablaré en breve. Muy sencillo de
utilizar. Toda la información aquí: http://msdn.microsoft.com/es-es/library/
cc668201.aspx.

• Mejoras en AJAX: en concreto, soporte para el historial del navegador, un


sistema de combinación de scripts para mejorar los tiempos de descarga de
estos archivos, y un control para mostrar vídeos.

No está mal para un simple parche.

5.- .NET 4.0 y Visual Studio 2010


Y por fin llegamos al final de la historia (por el momento). A finales de 2009 hacen
su aparición Visual Studio 2010 y la versión 4.0 de la plataforma.
En lo que se refiere a Visual Studio, los cambios son sustanciales. Para empezar,
el entorno es completamente nuevo y está basado en WPF, con interesantes mejoras
para la escritura de código, la depuración y las superficies de diseño.
En lo que se refiere al núcleo de la plataforma y las API relacionadas, hay mu-
chos perfeccionamientos y añadidos, si bien los más importantes, en mi opinión,
tienen que ver con la introducción de soporte en la plataforma para el desarrollo
paralelo.
En la actualidad se han popularizado los sistemas con más de un procesador
y, sobre todo, los que llevan procesadores de múltiples núcleos (conocidos como
manycore), que son hoy el estándar de la industria y que se incluyen hasta en los
portátiles de gama más baja. Estos sistemas abren grandes posibilidades para el
desarrollo de aplicaciones más eficientes y de mayor rendimiento, sacando partido
a la capacidad de ejecutar en paralelo múltiples hilos de trabajo. El desarrollo de
aplicaciones con múltiples hilos, sin embargo, reviste una gran complejidad para
el programador, y con frecuencia se da la paradoja de que aplicaciones con multi-
subproceso se ejecutan peor en sistemas manycore que en sistemas sencillos más
antiguos.
Gracias a las extensiones para el desarrollo paralelo incluidas en .NET 4.0, la
creación de este tipo de aplicaciones asíncronas y multi-subproceso se simplifica en
gran medida. El nuevo modelo permite a los desarrolladores la escritura de código
paralelo que es eficiente, granular y escalable, pero sin tener que lidiar directamente
con las dificultades de la creación y sincronización de hilos, y además usando un
lenguaje más natural para el programador.
ASP.NET y sus versiones  7

Otra importante novedad es el soporte para lenguajes dinámicos, del estilo de


Lisp, PHP, Ruby o incluso JavaScript, que ahora son mucho más fáciles de incorporar
a la plataforma.
En lo que respecta a la parte de desarrollo Web, la principal novedad es, sin
duda, el nuevo sistema de plantillas enlazadas a datos en el navegador, parte de la
nueva versión de ASP.NET AJAX. Esta característica abre la posibilidad de crear
aplicaciones AJAX más “puras” con gran trasvase de funciones desde el servidor al
cliente. Las plantillas de lado cliente se tratan con profundidad en esta obra. También
se ha añadido un nuevo control Chart para generar gráficos, y ciertas mejoras en
Dynamic Data. Estas últimas también las estudiaremos.
Adicionalmente, ASP.NET 4.0 incluye una nueva tecnología de desarrollo Web
llamada ASP.NET MVC, que se basa en el patrón de diseño Modelo Vista-Con-
trolador (de ahí el nombre). Éste constituye un sistema de desarrollo Web paralelo
al ASP.NET tradicional, usando otro tipo de paradigmas completamente diferentes.
Esta tecnología ha surgido como un proyecto independiente y ahora se incorpora con
pleno soporte a la plataforma en la versión 4.0. Hay quien la adora y le ve grandes
ventajas, y hay quien opina que es un paso atrás en el modo de desarrollar aplica-
ciones Web. En cualquier caso, se trata de una tecnología para el desarrollo Web
completamente aparte de la tradicional y que en cierta medida “va por su cuenta”.
Por este motivo en este libro, centrado en el desarrollo ASP.NET tradicional, no se
incluye ASP.NET MVC.
Existen en .NET 4.0 otros muchos otros añadidos y mejoras de diverso calado,
muchos de los cuales provienen del SP1 para .NET 3.5 y que se han incorporado ya
como base a la plataforma. En http://tinyurl.com/PDC2008-NETFX4PDF hay para
descarga un poster de alta resolución que permite ver todas las novedades de .NET
4.0 en cada una de sus áreas de una forma resumida y sencilla.

6.- De qué trata este libro (y qué deja fuera)


Como hemos podido comprobar en esta breve historia de .NET, existe una cierta
complejidad para entender sus versiones y lo que nos proporciona cada una de ellas.
Lo que sí podemos afirmar es que, en lo que respecta al desarrollo Web, todas las
recientes se sustentan en la funcionalidad proporcionada por la 2.0. Las versiones
3.0, 3.5, 3.5 SP1 y 4.0 incluyen añadidos y mejoras que trabajan con 2.0 por debajo
y aportan funcionalidades específicas, como el trabajo asíncrono desde el cliente o
la creación de interfaces automatizadas.
Por ello, este libro se va a centrar en las principales tecnologías aparecidas des-
de la versión 2.0 de ASP.NET y hasta la versión actual, que las contiene a todas.
Así, centraremos gran parte de la obra en el desarrollo de aplicaciones AJAX,
empezando por los fundamentos de esta tecnología, la creación de aplicaciones AJAX
basadas en el servidor (que es la más común pero la menos óptima) y por fin, el
cambio de filosofía que supone transferir gran parte de la lógica desde el servidor
al cliente, que es la parte más compleja (y también la más interesante).
8  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Con ello abarcaremos todo lo que es necesario saber de ASP.NET AJAX, desde
la versión descargable independientemente para .NET 2.0 hasta las novedades apa-
recidas con .NET 3.5 SP1 y las últimas técnicas avanzadas de ASP.NET 4.0, por lo
que se tendrá una referencia muy completa.
Otra tecnología que no podíamos dejar fuera es Dynamic Data. Aparecida en
.NET 3.5 SP1, es casi una desconocida para la gran mayoría de los programadores
Web. Esto es una verdadera lástima, pues esta tecnología ofrece grandes posibilidades
y mejoras de productividad. Con ASP.NET 4.0 se han incluido algunos perfeccio-
namientos adicionales y se espera que empiece a utilizarse de manera generalizada.
En este libro aprenderás lo necesario para sacarle todo el partido.
Para terminar he incluido también un completo capítulo sobre los nuevos contro-
les para generación de interfaces de filtrado.
Existen otras pequeñas mejoras de menor calado que han aparecido con ASP.NET
4.0 para la parte de desarrollo Web. No las he incluido aquí pues son una amalgama
de diferentes cosas no relacionadas y hay información exhaustiva en Internet sobre
ellas. En mi blog (www.jasoft.org) podrás encontrar información sobre la mayoría
en diversas entradas que he ido añadiendo y añadiré en el futuro.
Hay muchas otras tecnologías que se pueden utilizar en los desarrollos para
Internet con .NET, pero que no son específicas para este propósito: LINQ, Enti-
ty Framework, Windows Communication Foundation, Silverlight/WPF, Workflow
Foundation... Cualquiera de ellas es lo suficientemente extensa y compleja como
para merecer un libro dedicado, por lo que no tiene sentido incluirlas en esta obra.
Si tienes interés en aprender estas tecnologías te recomiendo que visites www.cam-
pusmvp.com, en donde encontrarás los mejores libros y cursos del mercado sobre
todas ellas.

7.- En resumen
Una vez que sabemos ubicarnos entre la maraña de versiones y tecnologías para el
desarrollo Web, ya estamos en condiciones de comenzar a aprender las principales
características de las últimas versiones de ASP.NET. ¡Comencemos!
capítulo
2

Fundamentos de AJAX

Desde sus comienzos y hasta hace relativamente poco tiempo las interfaces de usua-
rio de las aplicaciones Web han sido más o menos siempre iguales. Las limitaciones
propias del protocolo HTTP (Hyper Text Transfer Protocol) utilizado en las páginas
Web han impuesto el tradicional modelo de “petición-respuesta-procesado en el
navegador” (a partir de ahora PRP) y vuelta a empezar.
Los pasos que sigue una aplicación Web corriente para funcionar suelen ser los
siguientes:
1. El usuario solicita una página
2. El servidor devuelve el contenido HTML correspondiente a ésta, normal-
mente generado a partir de alguna tecnología de servidor (como ASP.NET
o PHP)
3. El navegador recibe este HTML, lo procesa y visualiza el contenido resul-
tante.
4. El usuario interactúa con el HTML, envía un formulario al servidor o pulsa
un enlace, y se repite el ciclo desde el paso 1: se solicita la página, se de-
vuelve su contenido, se procesa y se visualiza.
Este proceso es el más natural para HTTP —pensado desde luego para fun-
cionar así— pero tiene el problema de que para obtener una página prácticamente
igual a la inicial pero con pequeñas modificaciones es necesario recargar la página
completa. Esta situación es especialmente común desde que ASP.NET apareció en
escena hace ya unos cuantos años, con su novedoso sistema de “postback”. Gracias
a este concepto una aplicación Web se programa prácticamente igual que una de
escritorio, respondiendo a eventos y accediendo directamente a las propiedades de
los objetos. El problema del sistema es que cada uno de los postback al servidor
hace que se recargue la página completa, lo cual es percibido de manera evidente por
los usuarios (es decir “se nota” el refresco de la página) y crea una sensación poco
amigable. Además si el retorno de la página tarda más que unos pocos milisegundos
se pierde capacidad de respuesta de la página puesto que, durante el proceso de
petición-respuesta-procesado, la interfaz del navegador no responde.

9
10  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Si bien el concepto de “postback” es extremadamente útil, las actuales tendencias


en el desarrollo Web hacen que éstas sean cada vez más parecidas a aplicaciones de
escritorio. Esto implica que los molestos y a la vez inevitables viajes al servidor no
deberían ser percibidos por los usuarios. La sensación para éstos debe ser la de que
la aplicación está todo el tiempo en su equipo, dejando de lado al servidor, como en
una aplicación de escritorio tradicional.

1.- Interfaces de usuario avanzadas


Si has usado alguna vez Facebook, Hotmail, GMail o alguna aplicación web de
última hornada sabes perfectamente de qué estoy hablando. Toma por ejemplo el
caso de Facebook. Cuando actualizas tu estado o introduces un comentario para un
amigo, por detrás se está ejecutando el proceso PRP antes descrito, sólo que tú no lo
notas ya que se realiza de manera asíncrona (para no bloquear la interfaz) y actualiza
únicamente un trocito de la página, que es el que se ve afectado por tu acción.
Imagínate que cada vez que dices “Me gusta” en Facebook se recargara por completo
la página entera. Se haría insufrible y dudo mucho que tuviera el éxito actual. Lo
mismo se aplica a los clientes Web para correo electrónico como los mencionados.
Ya nadie concibe pulsar sobre una de tus carpetas de correo (o tags en el caso de
GMail) en el lateral, y que de repente se cargue de nuevo la página completa. La
experiencia debe ser integrada, como la de cualquier aplicación de escritorio.

Figura 1.- La interfaz de Facebook usa de manera intensiva técnicas AJAX.

Desde que el HTML Dinámico (HTML + JavaScript) y las hojas de estilo CSS
hicieron su aparición, cada vez más aplicaciones hacen uso de las posibilidades de
modificación “al vuelo” de contenidos que estas tecnologías brindan, consiguiendo
los resultados que acabo de describir. En la actualidad todos los navegadores ofrecen
soporte para DHTML por lo que si quieres escribir programas complejos de lado
Fundamentos de AJAX  11

de cliente, que interactúen con los contenidos de la página, no implica como antes
tener que dejar fuera a parte de tus posibles usuarios.
A la combinación de HTML dinámico con tecnologías de servidor se le denomina
de manera genérica AJAX. Este simpático acrónimo hasta hace poco asociado
con el apasionante mundo de la limpieza y con el fútbol, viene del concepto en
inglés Asynchronous JavaScript And XML. Se basa en el uso de un objeto llamado
XMLHttpRequest, presente en todos los navegadores modernos, que como es de
imaginar por su nombre sirve para hacer peticiones al servidor de documentos XML
a través del protocolo HTTP. Utilizado este objeto se solicitan al servidor datos en
formato XML que, una vez recibidos en el navegador, es posible manipular mediante
código JavaScript y mostrar el resultado dentro de los elementos de la página. Esta
es la idea original de esta técnica, pero como comprobaremos en breve, de este punto
inicial a lo que existe actualmente las cosas han cambiado mucho.
Aunque casi todo el mundo se piensa que esto de AJAX es un invento de Google
y su potente cliente Web de correo electrónico GMail (primera aplicación que en
verdad lo popularizó), el objeto XMLHttpRequest apareció originariamente junto a
las bibliotecas XML de Microsoft (MSXML) a finales de los años noventa del siglo
pasado. El concepto original de esta tecnología fue creado también por Microsoft (se
llamaba Remote Scripting), el primer navegador en soportarlo fue Internet Explorer
y la primera aplicación de este tipo fue Outlook Web Access (OWA), para acceder
a buzones Exchange.
Como veremos enseguida, aparte de que su nombre original haya perdurado por
ser simpático, la realidad es que AJAX no siempre es asíncrono ni tampoco siempre
usa XML. De hecho lo más habitual es que hoy en día utilice otro formato para
transferir los datos: JSON, que estudiaremos luego. Lo que de verdad constituye el
corazón de de esta técnica es el objeto XMLHttpRequest.
En este capítulo vamos a estudiar los fundamentos de la tecnología para que
no dependas de biblioteca alguna a la hora de implementar estas características,
y sobre todo -seamos realistas, casi nunca usarás esto “a pelo”- para que cuando
uses una de dichas bibliotecas comprendas lo que hay debajo y puedas determinar
posibles problemas. Conociendo bien los conceptos subyacentes tendrás muchas más
herramientas para sacarle partido a las bibliotecas construidas sobre ellos, como por
ejemplo la parte cliente de ASP.NET AJAX u otras muy conocidas como jQuery,
YUI, script.aculo.us, MooTools, Dojo, etc...

2.- Un poco de teoría: el objeto


XMLHttpRequest
Para sacar partido a AJAX, aparte de tener conocimientos de HTML y JavaScript,
el primer objeto que debemos conocer a fondo es el mencionado XMLHttpRequest.
Se trata de una clase disponible en todos los navegadores modernos que permite
lanzar desde JavaScript peticiones de recursos GET Y POST a través de HTTP.
Dicho en lenguaje simple, lo que esta clase nos permite es simular mediante código
JavaScript llamadas al servidor como si éstas hubieran sido hechas por los usuarios.
12  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

El efecto es el mismo que si el usuario hubiese enviado un formulario o pulsado


sobre un enlace, sólo que nuestro código es el que tiene el control sobre ello y lo
gestiona de manera independiente al contenido actual de la página (en la jerga se
suele decir que lo gestiona “por debajo”).
Aunque en el caso de Internet Explorer se sigue exponiendo su funcionalidad
como un objeto ActiveX en el resto de los navegadores (Firefox, Safari, Chrome,
Opera...) éste forma parte ya de sus clases nativas.
Los métodos y propiedades básicos de esta clase que debemos conocer son los
siguientes (los corchetes indican parámetros opcionales):
• open(método, URL, [asincrono], [usuario], [clave]): sirve para abrir una
conexión al servidor. No envía ni obtiene información, sólo se conecta.
El primer parámetro indica de qué manera queremos enviar la petición al
servidor: por GET o por POST. El segundo es la dirección Web a la que
vamos a llamar. El tercer parámetro es booleano y sirve para indicar si la
conexión se realizará de forma asíncrona (por defecto) o no. Los dos últimos
parámetros sirven para especificar un nombre de usuario y una contraseña
de acceso para recursos protegidos por autenticación básica, si bien esto es
bastante absurdo pues estarán escritos en claro en el JavaScript, por lo que
raramente se utilizan.
• send(contenido): envía una petición. Si es un envío por POST se pueden
incluir los datos a enviar en su único parámetro, si no se usa un nulo.
• abort(): cancela un envío/petición abierto previamente.
• onreadystatechange: a esta propiedad se le asigna una referencia a un mé-
todo JavaScript que será llamado automáticamente cuando se descargue del
todo la URL remota (cuando se llama a ésta asíncronamente).
• readyState: informa del estado de la petición:
o 0=no iniciada
o 1=cargando
o 2=terminada pero sin procesar
o 4=completada.
• status: código de estado HTTP resultado de la petición: por ejemplo 200
(éxito), 404 (no encontrado), etc... Se corresponden con los códigos de estado
de HTTP (los puedes consultar aquí: http://www.w3.org/Protocols/HTTP/
HTRESP.html).
• statusText: mensaje de información correspondiente al estado anterior.
• responseXML: documento DOM que representa el XML devuelto por la
petición. Sólo se utiliza si lo que esperamos obtener es un documento XML.
Fundamentos de AJAX  13

En la actualidad no se utiliza casi nunca pues el XML se ha abandonado en


AJAX a favor de JSON, que luego veremos.
• responseText: el contenido puramente textual del recurso remoto. Es la que
usaremos habitualmente para obtener el contenido del recurso solicitado.
Aunque dispone de algunos métodos y propiedades más, con estas tendremos sufi-
ciente para el 99% de los casos que nos vamos a encontrar.

3.- Basta de teoría: vamos a la práctica


Veamos cómo se usa la clase XMLHttpRequest en la práctica con un ejemplo sencillo.
Consideremos algo simple como el ejemplo de la figura:

Figura 2.- Ejemplo de lista desplegable auto-completada con AJAX

Hay una lista desplegable que contiene una serie de categorías. Al elegir una de
éstas, en la lista de al lado deberán aparecer los elementos que contiene rellanándolos
dinámicamente con lo que nos indique una página en el servidor, pero sin recargar
la página. Este ejemplo es muy sencillo pero nos ayuda a centrarnos sólo en la parte
que nos interesa, que es la de cliente y en cómo realizar las llamadas AJAX.
Lo que debemos hacer para que esto funcione es responder a los eventos de
cambio de selección de la primera lista y lanzar “por debajo” una petición a una
página del servidor que nos devolverá los elementos de la lista secundaria. En un
ejemplo real los elementos se obtendrían, en la página del servidor, seguramente
desde una base de datos.
14  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Lo primero que debemos hacer para lanzar una llamada al servidor desde nuestro
código JavaScript es obtener una referencia a un objeto de la clase XMLHttpRequest
que será el que utilizaremos. Dado que Internet Explorer es diferente a los demás na-
vegadores en este aspecto, debemos distinguir con quién estamos trabajando para poder
instanciarlo. Podemos encapsular esta funcionalidad con el código del listado 1:

Listado 1
function getHttpRequest()
{
var httpReq;
//Si es Mozilla, Opera, etc...
if (window.XMLHttpRequest)
{
httpReq = new XMLHttpRequest();
}
else //Internet Explorer lo expone como control Active X
{
httpReq = new ActiveXObject(“Microsoft.XMLHTTP”);
}
}

Así dispondremos de una función getHttpRequest que nos devolverá una instancia
del objeto XMLHttpRequest independientemente del navegador.
Una vez que tenemos una referencia al objeto usaremos sus métodos y propiedades
para lanzar una petición a la página de servidor que nos interese, por ejemplo así:
http = getHttpRequest()
http.onreadystatechange = finCarga;
http.open(“GET”, “http://www.miserv.com/misdatos.aspx”, true)
http.send(null);

En la segunda línea, y antes de llamar a la página del servidor, establecemos una


referencia a la función que se llamará automáticamente cuando cambie el estado de la
petición que vamos a lanzar. Ello no implica que ésta tenga éxito o que sea llamada
sólo cuando termine, como veremos enseguida.
La tercera línea abre el conducto de petición al servidor para cargar una deter-
minada URL de modo asíncrono (true en el tercer parámetro). En este caso usará el
método GET pues no enviamos datos al servidor.
Por fin debemos hacer la llamada (el open de la línea anterior no la hace sólo la
prepara), usando para ello el método send. Se le pasa un nulo porque no enviamos
ninguna información extra (es una petición GET). El hecho de que sea una llamada
asíncrona hará que se devuelva el control inmediatamente a JavaScript al pasar por
esta línea, por lo que podríamos tener más código a continuación para llevar a cabo
más tareas sin que se viera interrumpida la ejecución ni la interacción con el usuario.
Podríamos usar llamadas síncronas ( false en el tercer parámetro) para lanzar varias
Fundamentos de AJAX  15

llamadas seguidas con la certeza de que se ejecutarán en un determinado orden. Es


decir, como vemos, AJAX en realidad no siempre debe ser asíncrono.
Obviaremos de momento el código de la página del servidor que podría ser
cualquiera (acceder a una base de datos para obtener los elementos, leer un ar-
chivo o la memoria, etc...). Lo único verdaderamente importante es ponernos de
acuerdo en cómo la página del servidor va a devolver los resultados de la petición.
Podemos complicarlo todo lo que queramos usando XML o cualquier otra notación
que consideremos oportuna. Más tarde retomaremos este tema. De momento para
acabar con el ejemplo vamos a suponer simplemente que el servidor nos devuelve
una lista, separando con comas los elementos que se desean mostrar en el control
secundario.
Como hemos visto se define una función llamada ‘finCarga’ que es llamada de
manera automática al ir cambiando el estado de la petición. Veamos cómo es su
aspecto en el listado 2:

Listado 2
function finCarga()
{
if (http.readyState == 4) //4: completado
{
if (http.status == 200) //200: OK
{
res = http.responseXML;
Procesarespuesta();
}
else //Se produjo un error
{
alert(“No se pudo recuperar la información: “ + http.statusText);
}
}
}

Lo único que hacemos aquí es detectar cuándo se ha terminado la petición (ready-


State será igual a 4 como hemos visto antes) y que ésta haya sido una petición exitosa
(el código de estado HTTP debe ser 200). Si el código HTTP es 404 (no encontrado),
500 (error en el servidor) u otro cualquiera se advierte con un mensaje al usuario. En
caso positivo lo único que hacemos es anotar la respuesta del servidor en una variable
global de la página (res en el ejemplo) y procesar el resultado adecuadamente. En
este caso se separan los elementos con las comas y se carga la lista secundaria.
Dado que es un código JavaScript trivial y no aporta nada al tema que nos ocupa no
lo he incluido aquí, pero lo puedes ver en el archivo con los ejemplos del libro. Lo
único que se usa normalmente al mostrar los resultados son los conocidos métodos
getElementsByTagName y getElementByID de HTML dinámico y del DOM. Estudia
el ejemplo incluido en el ZIP para ver exactamente como se ha hecho.
16  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Este ejemplo tan sencillo constituye en realidad todo lo que es necesario saber
sobre los fundamentos de funcionamiento de AJAX.

4.- Problemas típicos de AJAX y sus


soluciones
Ahora que ya conocemos los rudimentos de AJAX vamos a ver cuáles son los princi-
pales problemas que nos podemos encontrar al usar estas técnicas, y que en ocasiones
pueden ser complicados de detectar. Probablemente, serán los mismos que nos encon-
traremos si utilizamos alguno de los paquetes específicos para AJAX como ASP.NET
AJAX y las otras mencionadas, por lo que debemos ser conscientes de ellos.
Los más importantes son los siguientes:
1. Llamadas fuera del dominio.
2. Llamadas que producen errores o que no vuelven jamás.
3. Envío de datos al servidor.
4. Contenidos no actualizados debido a cachés.

4.1.- Llamadas fuera de dominio


Una vez que uno empieza a juguetear con las posibilidades de AJAX enseguida se
nos ocurren ideas geniales para sacarle partido. La más obvia, claro está, es la de
utilizar las técnicas para acceder desde el cliente a ciertos Servicios Web ajenos
de utilidad ubicados en Internet. Así, dado que los Servicios Web están basados en
XML, es muy fácil procesar lo que devuelven con las técnicas descritas para, por
ejemplo, realizar búsquedas en Amazon con su API, seguir una subasta en eBay,
enviar “posts” a nuestro blog, consumir fuentes RSS, etc...
Todo esto es estupendo pero tiene un gravísimo problema: los navegadores,
por cuestiones de seguridad, bloquean todas las peticiones realizadas mediante
XmlHttpRequest a dominios que no sean el que aloja la página desde la que se
está usando.
En realidad se trata de una restricción bastante lógica y que aparece en otras par-
tes del navegador, como las cookies, el acceso a variables de JavaScript entre marcos,
los objetos Flash o los applets de Java. Pero esto, claro está, supone una limitación
importante para ciertos tipos de aplicaciones AJAX que podríamos desarrollar, como
las de los ejemplos comentados.
La pregunta ahora es entonces: ¿Cómo solventamos esta situación?
En Internet Explorer basta con bajar el nivel de seguridad para que ya funcione
correctamente, pero no es una buena solución (no le puedes pedir esto a tus usuarios).
En otros navegadores (Firefox, Opera, Chrome y Safari) no hay forma de saltarse
esta restricción. Existe una salvedad en Firefox que consiste en firmar digitalmente
el JavaScript que usas, pero tampoco vale de mucho pues sólo funcionaría en este
navegador.
La única forma de solucionarlo de manera independiente al navegador es, aunque
sea de Perogrullo, hacer que no dependa de éste, es decir, llevarnos el problema al
Fundamentos de AJAX  17

servidor. Para ello lo que debemos hacer es construir un servicio proxy que esté en
nuestro servidor (al que sí podremos llamar con AJAX) y que sea éste el que se
encargue de realizar la llamada a otros dominios devolviendo el resultado a nuestro
JavaScript (directamente o pre-procesándolo de algún modo).
En .NET esto implica generalmente crear un manejador de peticiones con exten-
sión .ashx o un servicio Web propio que se encargue de realizar por nosotros las
peticiones que nos interesen.
¡Mucho ojo con esto!. Normalmente este tipo de servicios -al igual que los que
se encargan de leer archivos de disco de manera genérica y otros similares- son
un verdadero peligro de seguridad si no los programamos bien. Si optas por esta
solución lo mejor es que tomes varias precauciones de cara a la seguridad: tener muy
acotados los servicios o URLs a los que se puede llamar desde el proxy. Lo mejor
es identificarlos a cada uno con un número o código decidiendo a cuál se llama (con
una cláusula switch en C# o Select Case en VB.NET), nunca poniendo la URL
directamente en la llamada desde JavaScript.
Otra medida adicional es tratar de identificar al Script llamante de alguna manera:
mediante una cabecera que te debe enviar, comprobando el dominio del “referer” y
cosas similares. Está claro que un cracker experimentado se puede saltar esto pero
le dará bastante trabajo y elimina de un plumazo a los aficionados que quieran hacer
uso ilícito de tu servicio. Si puedes limita el número máximo de llamadas seguidas
que se puede hacer desde una determinada IP o, mejor, en una determinada sesión
de servidor. Toda precaución es poca.

4.2.- Gestión de errores y llamadas que no vuelven


No podemos asumir que las llamadas que hagamos al servidor van a funcionar siem-
pre. Puede haber un error en el código del servidor, es posible que haya cambiado
la URL y que no aparezca la página que llamamos, o que haya errores de permisos,
etc... Lo que pase en el servidor está fuera de nuestro control. Ante eso hay que estar
preparado. La forma de controlar estas situaciones es, como en cualquier componente
de comunicaciones por HTTP, a través del código de estado que devuelva el servidor.
Todo esto ya se había apuntado antes y se había tenido en cuenta en el código del
listado 2. Podríamos afinar más en el mensaje de error y devolver uno diferente
según el código de estado.
Hay, sin embargo, una situación menos frecuente pero más peligrosa que se puede
producir: que la llamada asíncrona al servidor no vuelva o no lo haga en un tiempo
razonable, es decir que se produzca lo que se denomina un timeout. ¿Qué hacemos
en ese caso?
No podemos contar con la notificación de final de carga puesto que, al no regresar
la llamada no saltará, así que el listado 2 no nos sirve.
Lo que se hace en estos casos es establecer un temporizador con el tiempo
máximo que deseemos esperar, para que al cabo de ese intervalo la petición sea
anulada directamente, sin esperar más que llegue la respuesta. Podemos verlo en
el listado 3:
18  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Listado 3
http = getHttpRequest()
http.onreadystatechange = finCarga;
http.open(“GET”, “http://www.miserv.com/misdatos.aspx”, true)
var tmrAnular = setTimeout(“AnularPeticion()”, 20000); //20 segundos
http.send(null);

function AnularPeticion()
{
http.abort();
}

function finCarga()
{
if (http.readyState == 4) //4: completado
{
clearTimeOut(tmrAnular);
if (http.status == 200) //200: OK
{
res = http.responseXML;

Procesarespuesta();
}
else //Se produjo un
error
{
alert(“No se pudo recuperar la información: “ + http.
statusText);
}
}
}

Se ha modificado el código de llamada anterior para añadir la creación de un


temporizador que se encarga de anular la petición al pasar un tiempo determinado
(en este caso de 20 segundos pero puede ajustarse a cualquier otro valor). Nótese
también como en el evento de fin de carga eliminamos el temporizador (que ya no
nos hace falta) cuando la petición termina de procesarse, en caso de que regrese.

4.3.- Envío de datos al servidor


Normalmente cuando pensamos en AJAX, es decir, en llamadas asíncronas a servi-
cios, lo hacemos desde el punto de vista de obtener información: llamo a una página
que me devuelve unos valores y los muestro en la interfaz de usuario. Aunque este
Fundamentos de AJAX  19

es el uso más común de AJAX lo cierto es que también es muy útil usarlo en el
sentido inverso, para enviar datos al servidor. Las utilidades y necesidades que cubre
son múltiples y de hecho hay muchos sistemas que le sacan partido.
La forma más sencilla y directa de enviar datos simples al servidor es incluirlos
en la URL a la que llamamos como parámetros GET:
urldestino.aspx?Parametro1=1234&Parametro2=5
Aunque esto puede servirnos para cosas muy sencillas no es lo que necesitaremos
en la mayor parte de los casos.
Lo habitual es que la información haya que enviarla con el método POST. La
principal diferencia entre GET y POST estriba en que el método GET hace una sola
llamada al servidor, solicitando una página y enviando algunos parámetros de datos
en la propia petición. POST por el contrario realiza dos conexiones al servidor. En
la primera solicita una URL y en la segunda envía los datos. Por GET lo máximo
que se puede enviar son 2 kB de información, mientras que por POST no existe
esta limitación.
Para enviar datos al servidor mediante POST nuestro código AJAX sería similar
al siguiente:
http = getHttpRequest()
http.onreadystatechange = finCarga;
http.open(“POST”, “http://www.miserv.com/misdatos.aspx”, true)
http.send(‘Parametro1=1234&Parametro2=5’);

Con esto no hemos ganado demasiado. Ahora se envían los datos por POST
(sólo cambia el primer parámetro de open) pero los hemos tenido que introducir
en el método send en lugar de en la propia URL. Esto sólo simularía el envío de
parámetros mediante POST desde un formulario HTML, aunque por otro lado en
ocasiones puede ser lo que queramos.
Lo habitual sin embargo es que, en lugar de enviar parámetros, queramos enviar
información pura y dura del tamaño que sea preciso, que es para lo que suele usarse
POST. Esto se puede conseguir modificando ligeramente el código anterior para
incluir una cabecera que indique al servidor que lo que le llega son, precisamente,
datos (línea 3 del siguiente fragmento):
http = getHttpRequest()
http.onreadystatechange = finCarga;
http.setRequestHeader(‘content-type’, ‘application/x-www-form-
urlencoded’);
http.open(“POST”, “http://www.miserv.com/misdatos.aspx”, true)
http.send(‘Aquí ahora mando la información que quiera al servidor’);

Con esto nuestro problema queda resuelto.


20  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

4.4.- Contenidos no actualizados debido a cachés


Cuando se envía una petición HTTP es posible que, si la caché del lado servidor no
está correctamente configurada, el navegador realice su propia caché local. Por lo tanto
la próxima vez que realicemos una llamada a la misma URL, el navegador en lugar de
hacerla sacará el mismo resultado anterior de esa caché local, y por lo tanto la llamada
no llega al servidor jamás. O puede que exista un proxy-caché por el medio (Teléfonica
por ejemplo las ha utilizado tradicionalmente en sus servicios de acceso a Internet)
que almacena peticiones anteriores y por lo tanto obtenemos únicamente una copia,
sin realizar la llamada al servidor real. Eso muchas veces es lo que querremos para
ahorrar procesamiento y será maravilloso, pero lo habitual es que sea una maldición
ya que evitará que obtengamos datos actualizados.
A la hora de enviar datos por POST no hay problema porque no actúa nunca
la caché. El problema, si se da, está en las peticiones GET, por otro lado las más
habituales.
Si el servidor tiene bien configurada la caché (es decir, indica cuándo caducan
los contenidos o marcamos en IIS que éstos caduquen inmediatamente) no debe-
ríamos experimentar fallos, salvando lo comentado respecto a los proxy-caché de
algunos proveedores.
Si queremos asegurarnos de que la petición va a llegar a su destino podemos
hacer fundamentalmente dos cosas:
1. Agregar una cabecera que indique que se debe obtener el contenido siempre
que éste sea posterior a una fecha, por ejemplo así:
http.setRequestHeader(‘If-Modified-Since’, ‘Wed, 1 Jan 1972 00:00:00
GMT’);

Indicaremos siempre una fecha anterior a la actual como la del ejemplo y así
siempre se pedirá la última versión al servidor.
2. Añadir un número aleatorio (o cadena) a la URL de cada petición. En este
caso suele funcionar muy bien el agregarle una marca temporal, es decir,
añadir a continuación la fecha y hora actuales, de modo que cada una de las
peticiones que se hagan va a ser diferente y por lo tanto los caché que existan
por el medio tienen que repetir siempre la petición. Por ejemplo:
http.open(“POST”, “http://www.miserv.com/misdatos.aspx?pasacache=” +
new Date().getTime(), true);

Se le añade un parámetro que lleva como valor la fecha y hora en formato


numérico (es decir, un número muy largo y que varía varias veces cada milisegundo),
por lo que es muy difícil que se den dos peticiones idénticas incluso a través de
un proxy-caché. Además ese parámetro extra de nombre inventado que nosotros
le añadimos no debería afectar en absoluto a la llamada puesto que no está siendo
Fundamentos de AJAX  21

tenido en cuenta por la aplicación. Esta segunda técnica es la más fiable, aunque un
poco más tediosa de implementar.

5.- Devolución de información: JSON


En el ejemplo anterior hemos hecho que la página del servidor devuelva ciertos
valores que en este caso tenían formato XML, pero que podrían tener perfectamente
otra configuración distinta, como por ejemplo simples valores separados por comas.
Si bien esto puede ser suficiente en los casos más sencillos, en otras ocasiones
necesitaremos manejar estructuras de datos más complejas.
Dado que HTTP es un protocolo basado en texto, el recurso al que llamemos en
el servidor debe devolver siempre texto (o sea, no puede ser una imagen o un archivo
binario, que para transferirse se codifican de una forma especial -Base64- para
convertirlos en texto). Este texto devuelto puede tener cualquier formato: texto plano,
XML, código JavaScript o incluso HTML. En este último caso podemos obtener
desde el servidor un contenido HTML completo que se debe escribir en una zona de
la página (por ejemplo dentro de un <DIV> o un <SPAN>). Sin embargo la mayor
parte de las veces lo que tendremos que procesar es alguna estructura de datos.
Ya hemos mencionado que la ‘X’ de AJAX significa XML, pues era el formato
de moda a finales de los 90 y principios de los 2000 y se usaba para todo. Como se
demostró en nuestro sencillo ejemplo este formato no es necesariamente el que se va
a devolver. De hecho hoy en día el XML se usa muy poco a la hora de representar
los datos textuales devueltos desde el servidor en páginas AJAX.
El motivo de esto es principalmente que los datos representados con XML, si
bien son ricos en estructura, hacen que el resultado devuelto ocupe mucho debido a
las etiquetas de apertura y cierre de los diferentes nodos. Gracias al DOM es fácil
procesar la información jerárquica que se representa mediante XML, aún así debería
existir algún método más directo, más rápido y que ocupe menos ancho de banda.
Como alternativa a XML surgió un nuevo formato programable llamado JSON
(pronunciado “yeison”) que lo reemplaza con mucha ventaja en la mayor parte de
los casos. JSON es el acrónimo de JavaScript Object Notation, y como su propio
nombre indica permite representar objetos (en realidad estructuras complejas) en
forma de código JavaScript que luego podemos evaluar.
JSON tiene varias ventajas sobre XML, a saber:
1. Ocupa mucho menos al transmitirlo por la Red.
2. El acceso en el navegador a los elementos de datos representados es directo y
sin necesidad de procesamiento farragoso usando el DOM o expresiones regu-
lares, ya que se trata directamente de JavaScript que se puede interpretar.
3. Los datos pueden ir colocados en cualquier posición.
Consideremos el siguiente código XML que representa los datos de un cliente:
22  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

<cliente>
<nombre>José Manuel</nombre>
<apellidos>Alarcón Aguín</apellidos>
<empresa>Krasis</empresa>
<telefono>902 876 475</telefono>
<edad>37</edad>
</persona>

Ahora consideremos la misma representación en JSON:


{
“nombre” : “José Manuel”,
“apellidos” : “Alarcón Aguín”,
“empresa” : “Krasis”,
“telefono” : “902 876 475”,
“edad” : 37
}

Crearlo es muy fácil pues es sintaxis JavaScript normal. En www.json.org es


posible encontrar una explicación completa de esta notación. La Wikipedia también
tiene una información muy completa sobre ello.
Como se puede comprobar ocupa menos que el XML equivalente, es igual o
incluso más fácil de leer que éste, y permite usar datos nativos y no sólo cadenas
para representar los valores. En estructuras de datos más complejas se puede apreciar
más todavía el ahorro de datos que implica.
De todos modos lo más espectacular de JSON es lo fácil que resulta usarlo. Basta
con escribir lo siguiente:
var cliente = eval(res);

Siendo ‘res’ el nombre de la variable que contiene el JSON obtenido del servidor.
Es decir, lo único que hacemos es procesar la expresión JSON. Al hacerlo obtenemos
en la variable ‘cliente’ un objeto cuyas propiedades son los datos que queremos
manejar. De este modo lo único que tenemos que hacer para leerlos es escribir
directamente expresiones como esta:
alert(“El nombre de la empresa es “ + cliente.empresa);

Más fácil imposible. Nada de recorrer una jerarquía XML con el DOM o ir
buscando nodo a nodo en el contenido. Se convierte en JavaScript puro y utilizable
nada más llegar desde el servidor.
El uso de JSON como formato de intercambio sólo tiene dos problemas aparentes.
El primero de ellos es el más obvio: generar XML con C# o VB.NET es muy fácil
pero generar JSON ha requerido tradicionalmente un cierto trabajo por nuestra parte.
Para evitárnoslo las últimas versiones de .NET (3.5 o superior) incluyen la posibilidad
Fundamentos de AJAX  23

de que los servicios Web y WCF (Windows Communication Foundation) generen


sus resultados directamente en JSON. Existen además bibliotecas especializadas en
generar JSON si estamos interesados en hacerlo de manera manual. Por ejemplo,
una muy famosa es Jayrock (http://jayrock.berlios.de) que permite convertir objetos
.NET directamente a su representación JSON.
El otro problema es tal vez menos evidente pero más importante: la seguridad.
Dado que se usa una expresión ‘eval’ para convertir el texto en objetos JavaScript
podría utilizarse de manera malintencionada para inyectar código peligroso en la
página al efectuar la evaluación. Para evitarnos este peligro con código en el que no
confiamos, en JSON.org tenemos un script (http://www.json.org/json.js) que extiende
las cadenas de JavaScript para convertirlas a JSON verificando la sintaxis y evitando
todo aquello que no sean datos. Si incluimos este Script en nuestra página en lugar
de utilizar ‘eval’ podemos escribir:
var cliente = res.parseJSON();

para obtener el mismo resultado.


Además nos ofrece la funcionalidad inversa de la siguiente manera:
var miCadena = cliente.toJSONString();

Esto nos puede servir para enviar datos JSON al servidor, para almacenar un
objeto en una cookie, etc...

6.- En resumen
En este capítulo hemos aprendido los fundamentos de AJAX así como los principales
problemas que nos podemos encontrar al utilizarlo. La comprensión de todo ello
nos va a resultar útil aunque no usemos estas técnicas de “bajo nivel” sino que
recurramos a un kit especializado como ASP.NET AJAX, o cualquiera de las
múltiples bibliotecas de JavaScript con soporte AJAX existentes en el mercado.
Lo habitual será que siempre implementemos características AJAX en nuestras
páginas usando alguna biblioteca especializada. En el mundo Microsoft emplearás
con toda seguridad la parte cliente de ASP.NET AJAX, así como la biblioteca de
código abierto JQuery, que se soporta oficialmente a partir de Visual Studio 2010.
En el próximo capítulo vamos a estudiar las principales características de ASP.NET
AJAX en el lado de servidor, que se traducen en mejoras en el lado del cliente.
capítulo
3
ASP.NET AJAX
en el servidor

Como hemos visto en el capítulo anterior, AJAX es un gran aliado a la hora de


mejorar la usabilidad y la capacidad de respuesta de la interfaz de nuestra aplicación
Web. Las técnicas AJAX nos evitan tener que refrescar la página completa cada vez
que se genere un evento de servidor, lanzando las peticiones en segundo plano y
refrescando sólo las partes de la interfaz que sean apropiadas en cada caso.
Conseguir todo esto directamente con nuestro propio código JavaScript en el
navegador puede convertirse en una tarea tediosa, que necesita bastante trabajo y es
propensa a errores. Para evitarnos estos problemas y facilitarnos la tarea ASP.NET
incluye una biblioteca especializada en creación de aplicaciones AJAX. Microsoft, en
un alarde originalidad, llamó a esta biblioteca ASP.NET AJAX. Su nombre anterior,
cuando estaba todavía en versión Beta, era “Atlas” y la verdad es que a casi todo el
mundo le gustaba más :-)

Nota:
Como ya comenté en el capítulo de introducción, ASP.NET AJAX está incluido en.NET de ma-
nera nativa a partir de su versión 3.5 (y versión 2008 de Visual Studio) y en todas las versiones
posteriores. En ASP.NET 2.0 la biblioteca se debe descargar e instalar por separado para poder
utilizarla. Puedes encontrarla en www.asp.net/ajax. No hay soporte para AJAX en las versio-
nes 1.0 y 1.1 de ASP.NET, ya completamente desfasadas.

La biblioteca de AJAX se divide en dos partes claramente diferenciadas pero que


trabajan de manera conjunta para dotarnos de funcionalidad:
· Biblioteca de servidor: contiene un conjunto de controles Web que abstraen
de manera muy efectiva las tareas que deberíamos hacer normalmente en el cliente.
Generan todo el código necesario para gestionar de manera asíncrona las llamadas
al servidor y las actualizaciones. Los dos más importantes son el control ScriptMa-
nager y el UpdatePanel. En la figura se pueden observar los controles dentro del
cuadro de herramientas de Visual Studio.

25
26  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Figura 1.- Controles de servidor para AJAX

· Biblioteca de lado cliente: se trata de una serie de scripts en JavaScript que


extienden las capacidades de este lenguaje para facilitar la creación de código en
el navegador (código de lado cliente). La parte servidora se basa en estas funciones
para generar su funcionalidad. Ofrece, por ejemplo, capacidades avanzadas de orien-
tación a objetos para JavaScript, creación de controles reutilizables, capacidad para
crear scripts localizados, llamadas a servicios Web desde el cliente y, por supuesto,
características para el control de las comunicaciones con el servidor.
La funcionalidad de lado cliente es, en verdad, la parte más compleja de ASP.
NET AJAX y le dedicaremos dos capítulos del libro. En este nos vamos a centrar
en la funcionalidad generada desde el servidor.

1.- Un primer ejemplo: Mejora de una


aplicación básica con AJAX
Para empezar a abrir boca con ASP.NET AJAX vamos a reproducir el ejemplo
sencillo del capítulo anterior de fundamentos usando ASP.NET. Veremos cómo se
comporta al tratarse de una aplicación ASP.NET normal y luego lo fácil que es
convertirlo en una aplicación AJAX. Nos servirá para conocer los fundamentos de
la parte servidora de la API de AJAX.
Crea un nuevo sitio Web con Visual Studio y en la página por defecto añade
dos controles DropDownList, uno a continuación del otro separados con un par de
espacios en blanco. En el primero de ellos habilita el “Auto postback” de modo que se
genere automáticamente un evento SelectedIndexChanged en el servidor cada vez que
se seleccione un elemento diferente de la lista. Usa la opción de editar elementos en su
menú de tareas para añadir cuatro categorías: Revistas, Blogs, Empresas y Libros.

Figura 2.- El menú de tareas del control DropdownList


ASP.NET AJAX en el servidor  27

Ahora vamos a responder a su evento de selección para introducir en la segunda


lista los elementos correspondientes a la categoría seleccionada. Haz doble clic en
el control DropDownList1 para obtener la definición del manejador de eventos para
su evento SelectionIndexChanged y escribe el siguiente código:

Protected Sub DropDownList1_SelectedIndexChanged(ByVal sender


As Object, ByVal e As System.EventArgs) Handles DropDownList1.
SelectedIndexChanged
Select Case DropDownList1.SelectedValue.ToLower()
Case “revistas”
DropDownList2.Items.Clear()
DropDownList2.Items.Add(New ListItem(“dotNetMania”))
DropDownList2.Items.Add(New ListItem(“MSDN
Magazine”))
DropDownList2.Items.Add(New ListItem(“CodeProject”))
Case “blogs”
DropDownList2.Items.Clear()
DropDownList2.Items.Add(New ListItem(“www.jasoft.
org”))
DropDownList2.Items.Add(New ListItem(“www.geeks.ms”))
DropDownList2.Items.Add(New ListItem(“weblogs.asp.
net”))
Case “empresas”
DropDownList2.Items.Clear()
DropDownList2.Items.Add(New ListItem(“Krasis [www.
krasis.com]”))
DropDownList2.Items.Add(New ListItem(“Microsoft [www.
microsoft.com]”))
DropDownList2.Items.Add(New ListItem(“Plain Concepts
[www.plainconcepts.com]”))
Case “libros”
DropDownList2.Items.Clear()
DropDownList2.Items.Add(New ListItem(“Crimen y
castigo”))
DropDownList2.Items.Add(New ListItem(“Cien años de
soledad”))
DropDownList2.Items.Add(New ListItem(“El Quijote”))
End Select
End Sub

Con esto tenemos suficiente para demostrar la diferencia entre una aplicación
Web corriente y una con soporte para AJAX.
Ejecuta la aplicación pulsando F5 (acepta el diálogo que te avisa que se va a
modificar web.config para dar soporte a la depuración) y cuando se abra el navegador
juega un poco con la interfaz cambiando la selección en la primera lista.
28  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Como puedes comprobar, cada vez que eliges un elemento nuevo en la lista 1, se
produce un postback al servidor y al regresar la página, ésta se recarga y muestra
los elementos apropiados en la segunda lista. Los efectos molestos de esta aplicación
tan sencilla son fáciles de ver:
1. Durante la recarga de la página se produce un claro parpadeo, correspon-
diente al borrado de la página original y la subsiguiente recarga de ésta con
los nuevos valores. Si usas Internet Explorer además oirás un sonidito (como
un “clac”) en cada recarga.
2. La primera vez que haces una selección normalmente la página tarda un poco
más de lo habitual en ejecutarse. Durante quizá medio segundo el usuario
no tiene ni idea de si la acción que ha llevado a cabo en la interfaz (en este
caso seleccionar una categoría de la lista) ha tenido efecto o no. En algunas
aplicaciones reales en las que el código es más complicado y puede que haya
demoras de E/S debido a accesos a bases de datos, a disco o a redes con-
gestionadas, las esperas para recibir las respuestas a eventos de servidor
pueden tardar incluso varios segundos, durante los cuales el usuario no tiene
ni idea de qué está pasando.
3. Un efecto muy desagradable de los postback y del que muchos programadores
no se percatan es el de las entradas indeseadas en el historial de navegación.
En nuestro ejemplo cada vez que seleccionamos un elemento de la lista y se
provoca un postback aparece una nueva entrada en la historia del navegador.
Si el usuario pulsa la flecha para ir a la página anterior irá pasando por cada
una de las selecciones que haya hecho en la lista. Sin embargo el usuario ha
tenido la sensación de estar trabajando todo el tiempo en la misma página.
Para él o ella debería ser transparente el hecho de que nosotros por debajo
estemos reenviando la página. Cuando pulsa el botón de volver a la página
anterior lo que un usuario espera es realmente volver a la “pantalla” en la
que estuviese previamente, no a los sucesivos pasos de trabajo en la misma
página actual. Por supuesto existen excepciones y a veces será necesario todo
lo contrario: que incluso en aplicaciones AJAX se creen algunas entradas en
el historial. Lo trataremos en otro capítulo con detalle.
Vamos a retocar nuestro ejemplo sacándole partido a los controles AJAX que
se pueden ver en la primera figura. De momento no explicaremos sus funciones y
nos limitaremos a añadirlos sin más. En los siguientes epígrafes analizaremos con
detalle cada uno de ellos.
Abre la superficie de diseño de la página ASPX y desde el panel de herramientas
añade un control ScriptManager que encontrarás en el grupo de Extensiones AJAX.
Asegúrate de que el control se encuentra como primer elemento de la página, es decir,
arrástralo delante del primer control DropDownList que teníamos anteriormente.
Ahora arrastra, justo a continuación del anterior, un control UpdatePanel. Al
hacerlo verás que aparece una nueva área vacía de poca altura, que es la única pista
visual de que se ha arrastrado este último control.
ASP.NET AJAX en el servidor  29

Selecciona cada uno de las dos listas desplegables y arrástralas dentro del Up-
datePanel. Asegúrate de que quedan ubicadas dentro de éste. Como se observa en
la figura, el recuadrado sutil del control UpdatePanel nos permite ver sus límites y
saber si los controles se encuentran realmente dentro de él.

Figura 3.- Nuestra aplicación preparada para AJAX


con sólo arrastrar dos controles.

Podríamos haber incluido los controles dentro del UpdatePanel también desde
el código fuente de la página, en lugar de arrastrándolos, si cambiamos a la vista
HTML y nos aseguramos de que sus etiquetas están encerradas dentro de las eti-
quetas de tipo <ContentTemplate> del panel, como se ve en la figura 4.

Figura 4.- Las etiquetas de los controles en el código


fuente se encuentran dentro del UpdatePanel.

Ya está. No es necesario hacer nada más que arrastrar este par de controles.
Ejecuta de nuevo la aplicación. Ahora verás que al seleccionar una categoría en la
primera lista la segunda se recarga enseguida con los valores apropiados sin recargar
la página. El parpadeo ha desaparecido y en la historia del navegador no aparecen
nuevas entradas.
Más sencillo imposible.
30  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Nota:
La mayor parte de la gente se queda con esta idea de sencillez que el control UpdatePanel nos
brinda. Si bien es cierto que gracias a él la creación de aplicaciones AJAX se simplifica en
gran medida, también es verdad que hay multitud de pequeños detalles a tener en cuenta sobre
rendimiento y optimización que se le escapan al que simplemente se queda en la superficie.
Ello provoca que, luego en producción y sometidas a mucha carga, algunas aplicaciones AJAX
ofrezcan muchos problemas de rendimiento, ya que están hechas sin tener un buen conocimien-
to de lo que se hacía. En este capítulo trataremos de comentar conceptos e ideas clave que nos
ayudarán a comprender mejor toda esta tecnología. Por lo de pronto, si ya has leído el capítulo
anterior de fundamentos, ya tienes más herramientas que la mayoría de los programadores
ASP.NET AJAX que encontrarás por ahí.

2.- Postbacks parciales y repintados


parciales de página
El proceso que se lleva a cabo por debajo en las páginas AJAX como la del ejemplo
anterior se denomina postback con repintado parcial de página.
El funcionamiento de una página ASP.NET AJAX no difiere demasiado del de
una página ASP.NET normal y corriente. Ello es una gran ventaja puesto que no nos
obliga a aprender conceptos diferentes o a tratar a las páginas de un modo distinto
por el hecho de usar esta tecnología. Lo que ocurre durante un repintado parcial
de página (en el que sólo una parte de su interfaz se modifica) es en realidad un
postback completo de la misma. La diferencia con una página normal estriba en que
éste se realiza de manera asíncrona y “por debajo” usando JavaScript para solicitar
la página y procesar sus resultados. La otra diferencia es que en lugar del contenido
completo se devuelve sólo el HTML que compete a los contenidos del UpdatePa-
nel que se va a actualizar. El código JavaScript generado automáticamente por el
ScriptManager se encarga de gestionar ese resultado y repintar dinámicamente sólo
la parte apropiada de la interfaz, así como actualizar los datos de estado de los
controles para subsiguientes recargas de la página.
En el servidor todo esto significa que cuando se produce un evento en un control,
aunque se vea afectada sólo una pequeña parte de la página AJAX, en realidad se
está procesando un postback completo, con todos sus eventos de ciclo de vida. Es
más, se regenera totalmente el árbol de controles de la página, se lanzan todos los
eventos de ciclo de vida de éstos, se envía y recibe todo el Viewstate, etc... La única
diferencia es que se devuelve sólo el HTML de las partes de la página que se están
modificando dentro del UpdatePanel.
Olvidar esto es un error muy común que cometen los programadores, que no se
dan cuenta de que, aunque se vaya a actualizar sólo un pequeño control, en realidad
se está enviando el estado de todos los contenidos en la página (y hay ViewStates
que ocupan mucho), se ejecutarán todos los eventos de página tales como Page_Load
y los eventos de ciclo de vida de todos los controles aunque no intervengan en la
acción. O sea, exactamente igual que en una página normal.
ASP.NET AJAX en el servidor  31

Esto tiene mucha importancia ya que podemos tener una página enorme que se
construye a partir de un proceso complejo y costoso (por ejemplo obtener mucha
información de una base de datos o un servicio Web remoto), en la que hemos
añadido un UpdatePanel en el que se modificará solamente una pequeña cantidad
de información como respuesta a una acción. Podemos pensar que el refresco de ese
panel será una operación ágil y muy poco costosa ya que los datos recibidos son
minúsculos. Es muy fácil que nos equivoquemos y la actualización parcial aparen-
temente inocente lleve mucho tiempo y sea muy costosa en términos de red y de
proceso en el servidor si no hemos sido cuidadosos en nuestro código.
El nombre que se le suele otorgar a los postback de páginas AJAX tampoco
ayuda demasiado a aclarar este concepto, ya que normalmente se les denomina en
artículos y documentaciones como “postback parciales”, para distinguirlos de los
“postback normales” que se ejecutan cuando no hay funcionalidad AJAX. En mi
opinión deberían llamarse “postback asíncronos” o, directamente, no distinguir entre
unos y otros y sólo hablar de actualizaciones parciales, que es lo que realmente
ocurre. No te dejes engañar por el nombre cuando leas esta denominación y recuerda
que los postback son siempre completos.
La primera consecuencia de esto es que, aunque parezca que no lo necesitamos,
debemos seguir comprobando en la carga de la página si nos encontramos en un
postback o no, para lo cual usaremos la propiedad IsPostBack de la página. Así evi-
taremos operaciones de inicialización innecesarias en cada recarga, como haríamos
en cualquier página normal.
Si por el motivo que sea necesitamos saber si nos encontramos dentro de un
postback conducente a una actualización parcial de la página, o sea de un postback
asíncrono de AJAX, podemos saberlo consultando la propiedad IsAsyncPostback del
control ScriptManager, que tomará el valor True cuando este sea el caso.

3.- El control ScriptManager


El núcleo sobre el que se sustenta toda la funcionalidad AJAX de ASP.NET es el con-
trol ScriptManager. Como sabemos, la funcionalidad AJAX de cualquier aplicación
Web (sea con .NET o no) se basa en la interacción entre el navegador y el servidor,
la cual se gestiona a través de código JavaScript que realiza llamadas a recursos
remotos y maneja las respuestas obtenidas para actualizar la interfaz de usuario.
El control ScriptManager se encarga de generar todo el código JavaScript necesa-
rio para sustentar la funcionalidad AJAX en el navegador. Como veremos al analizar
la parte cliente de AJAX, este código JavaScript se ofrece en forma de librerías es-
pecializadas que habilitan todas las características especiales de AJAX mencionadas
al principio del capítulo: llamadas a servicios Web, funciones de comunicaciones...
y por supuesto, como funcionalidad estrella, la capacidad de efectuar postbacks en
segundo plano y hacer el repintado parcial de las páginas, que es lo que acabamos
de experimentar en el ejemplo.
32  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Siempre que deseemos utilizar funcionalidad AJAX debe existir un control Script-
Manager en la misma. También se pueden utilizar dentro de controles de usuario y
en Master Pages, aunque luego veremos las particularidades de usarlos ahí.
Entre las funciones de este control están:
• Generar los Scripts necesarios para sustentar la funcionalidad AJAX y las
extensiones de JavaScript para desarrollo en el lado cliente.
• Registrar nuestros propios scripts en el cliente y el envío de estos dinámica-
mente durante los repintados de página.
• Proporcionar acceso a servicios Web en el servidor.
• Soporte para localización y globalización de código de lado cliente en fun-
ción de los ajustes culturales del navegador.
• Dar soporte a los servicios de Membership, Roles y Profile de ASP.NET para
hacer uso de estos directamente desde el lado cliente, con JavaScript.
• Permitir la creación de controles y comportamientos basados en JavaScript
(extensiones) que otorgan de funcionalidad extra a los controles HTML nor-
males del navegador y a los controles Web de ASP.NET.
Dado que, como vemos, este control tiene muchas funcionalidades aparte de la
de permitir el repintado parcial de página, si no queremos utilizar esta característica
y sólo estamos interesados en hacer otras cosas con él en el lado cliente, podemos
desactivarla estableciendo a False su propiedad EnablePartialRendering.
Por otro lado es posible que en navegadores muy antiguos la funcionalidad de
repintado parcial no esté soportada. En esos casos el control ScriptManager se de-
grada elegantemente y deja que la página se comporte de la manera habitual, con
postbacks síncronos y repintado completo de la página. Podemos determinar esta
situación consultando su propiedad SupportsPartialRendering.

4.- El control UpdatePanel


Este control es una abstracción que nos permite indicar qué partes de nuestra interfaz
de usuario queremos que tengan la capacidad de actualizarse parcialmente, de forma
independiente al resto de la página.
Como hemos visto ya (figura 4) el marcado del control tiene una pareja de eti-
quetas <ContentTemplate> que definen una zona contenedora de otros controles.
Todos los controles definidos dentro de esta zona pueden participar de un repintado
parcial de la página. Esta región comprendida entre ambas etiquetas se corresponde
en el diseñador visual de Visual Studio con el área contenedora del UpdatePanel a
la que podemos arrastrar los controles.
Además de añadir controles en esta zona visualmente o incluyendo sus etiquetas
en el marcado de la página, es posible añadirlos (o retirarlos) también dinámicamente
ASP.NET AJAX en el servidor  33

mediante código. De esta forma se pueden variar en tiempo de ejecución los conteni-
dos de la zona de repintado parcial. Para ello se debe manipular la colección Controls
de la propiedad ContentTemplateContainer del UpdatePanel, así por ejemplo:

Dim Contenedor As Control = UpdatePanel1.


ContentTemplateContainer
Dim lbl As New Label()
lbl.Text = DateTime.Now.ToString(“hh:mm:ss”)
Contenedor.Controls.Add(lbl)

5.- Modos de actualización parcial


En una página podemos tener tantos UpdatePanel como necesitemos, para actua-
lizarlos parcialmente de forma individual, coordinada o todos a la vez. Un control
UpdatePanel puede contener a su vez a otros UpdatePanel, generándose una jerar-
quía de actualizaciones parciales que sigue determinadas reglas.
Lo más importante a tener en cuenta durante un repintado parcial es qué lo pro-
voca y qué efectos tendrá sobre los diferentes UpdatePanel que tenemos repartidos
por la página.
Cuando un UpdatePanel se encuentra dentro de otro, el del interior se actualiza
siempre que lo hace su contenedor. Cuando el UpdatePanel es independiente (la
situación más habitual) su comportamiento durante el repintado parcial de la página
viene determinado por el valor de su propiedad UpdateMode, que puede tomar los
siguientes valores:
• Always: si la propiedad UpdateMode tiene este valor el contenido se refresca-
rá después de cualquier postback que se realice a la página, sea éste generado
específicamente para este control o no. Esto es importante ya que este es el
valor por defecto de la propiedad. Si no somos cuidadosos y tenemos varios
UpdatePanel en la página veremos que, en muchas ocasiones, se actualizan
zonas que no contábamos que se iban a refrescar, ya que estábamos actuando
realmente en otro lugar.
• Conditional: el contenido del panel sólo se repintará si el control que lanza
el postback es uno de los disparadores para el UpdatePanel (ahora veremos
qué es esto) o bien cuando llamamos explícitamente al método Update del
panel desde un evento de servidor. Por supuesto, como ya se ha comentado,
se repintará también siempre que esté contenido dentro de otro panel que se
ha repintado.
Jugando con esta propiedad podemos obtener un gran control sobre en qué
momento y ante qué eventos se refrescará el contenido de una zona concreta de
la página.
34  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

6.- Disparadores
De manera predeterminada los controles que están dentro de los UpdatePanel
provocarán un repintado parcial de la página, mientras que los controles que se
encuentran fuera de éstos provocarán postbacks normales con la recarga completa de
la misma. Aunque este comportamiento pueda resultar adecuado para muchos casos,
normalmente vamos a necesitar un mayor control sobre las situaciones que provocan
la recarga de las páginas y qué zonas de ésta se ven afectadas.
Por ejemplo, si tenemos un control que queremos que provoque el refresco parcial
de una parte de la página alejada físicamente de donde está éste ubicado, tenemos
dos opciones:
1. Incluir el control y la zona que queremos actualizar dentro del mismo Up-
datePanel. Esto probablemente nos obligue a incluir muchos otros controles
en el panel, que no nos interesa en absoluto que participen en un repintado
parcial, puesto que no van a cambiar o queremos que se vean afectados
por otros eventos diferentes. Llevado al extremo algunos programadores con
poco conocimiento, lo que hacen es ¡rodear con un UpdatePanel todos los
controles de su página!. Esto es obviamente una solución muy ineficiente y
como se suele decir coloquialmente es “matar moscas a cañonazos”.
2. Definir qué controles actuarán como disparadores de cada panel, y por lo
tanto lanzarán postbacks asíncronos conducentes a refrescar el contenido
del mismo. Estos controles no tienen porqué estar dentro de un UpdatePa-
nel, sino en cualquier lugar de la página. Esta es la solución inteligente y
optimizada.
Para implementar esta funcionalidad se definen los disparadores o Triggers.
Por defecto todos los controles contenidos dentro de un UpdatePanel se compor-
tan como disparadores de éste. Es por ello que en el ejemplo anterior no hemos tenido
que hacer nada para que funcionara el repintado parcial de la segunda lista desple-
gable. Este comportamiento se controla a través de la propiedad ChildrenAsTriggers
del control UpdatePanel. Si la establecemos como False entonces los controles que
contiene no serán disparadores y tendremos que definirlos a mano.
El uso más común de los disparadores es el de asociar eventos de servidor de
controles externos a un panel para provocar repintados parciales de éste. Visual
Studio nos brinda un diálogo especial que facilita su definición.
Vamos a verlo en funcionamiento con un ejemplo.
Añade al proyecto de prueba una nueva página “Triggers.aspx”. Arrastra sobre
ella un ScriptManager y un UpdatePanel. Dentro de este último coloca una etiqueta.
Ahora, fuera del panel, justo debajo, inserta un botón. El aspecto final debería ser
como el de la figura 5:
ASP.NET AJAX en el servidor  35

Figura 5.- Aspecto de nuestra aplicación de prueba

En el manejador del evento de pulsación del botón simplemente vamos a mostrar


la hora actual dentro de la etiqueta:
Label1.Text = DateTime.Now.ToString(“HH:MM:ss”)

Ahora ejecuta la página. Al pulsar sobre el botón cabría esperar que la etiqueta
mostrase la hora sin necesidad de recargar la página. Sin embargo comprobarás que
en lugar de eso se provoca un postback y un refresco de toda la página. El motivo
es que el botón, al estar fuera del UpdatePanel no es automáticamente un disparador
para su repintado parcial.
Vamos a ponerle remedio. Selecciona el control UpdatePanel y en sus propiedades
(pulsa F4) verás que existe una colección Triggers.

Figura 6.- Colección de disparadores del panel


36  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Utilizando el botón con tres puntos situado a la derecha se abre el diálogo de


gestión de disparadores, que podemos ver en la figura 7.

Figura 7.- Editor de disparadores de un panel

El botón de añadir nos permite asignar uno o varios controles como disparadores del
panel, es decir, como controles que van a provocar un repintado parcial del mismo.
La rejilla de propiedades de la derecha nos facilita la selección de controles y
eventos. En nuestro ejemplo seleccionaremos como control disparador el botón de
actualizar la hora (cmdHora), y dentro de los posibles eventos de éste, el evento Click,
es decir su pulsación. Se podría hacer que el disparador fuese cualquier otro evento, e
incluso que varios eventos de un mismo control sirvan como disparadores del panel.
Una vez añadido nuestro botón como disparador, acepta para cerrar el diálogo
y vuelve a ejecutar la aplicación. Comprobarás como, ahora sí, al pulsar el botón la
hora se visualiza en la etiqueta sin necesidad de refrescar la página por completo.
Si nos fijamos en el código de marcado de la página, veremos que es posible
definir los disparadores del panel manualmente de forma sencilla, usando la etiqueta
<Triggers>:
<asp:UpdatePanel ID=”UpdatePanel1” runat=”server”>
<ContentTemplate>
<asp:Label ID=”Label1” runat=”server”></asp:Label>
ASP.NET AJAX en el servidor  37

</ContentTemplate>
<Triggers>
<asp:AsyncPostBackTrigger ControlID=”cmdHora”
EventName=”Click” />
</Triggers>
</asp:UpdatePanel>

Fíjate en que lo único que necesitamos es establecer las dos propiedades del
disparador como unos simples atributos de texto, indicando el nombre del control y
el del evento a asociar.
Los disparadores asíncronos como este se pueden utilizar también para lanzar
actualizaciones parciales a partir de controles de dentro del panel cuando Children-
AsTriggers es falso. También para forzar la actualización de un panel padre desde
un control ubicado en un panel anidado dentro de él.
Por defecto los controles que están dentro de los UpdatePanel harán un refresco
parcial asíncrono de la página. En ocasiones sin embargo es posible que necesitemos
que, si bien la mayoría de los controles del panel se comporten así, uno o varios de
ellos provoquen un refresco completo de la página. Por ejemplo cuando un cambio
dentro del panel afecta a datos que se están visualizando fuera del mismo.
Para ello existen un tipo especial de disparadores que se llaman simplemente
PostBackTriggers. Si te fijas bien en la figura y en el listado anteriores verás que
el disparador que hemos utilizado es de tipo AsyncPostBackTrigger porque desen-
cadena un postback asíncrono. En la figura 7 se ve como al desplegar el botón de
añadir disparador hay un tipo adicional sin el prefijo Async, siendo simplemente
PostbackTrigger.
Este tipo de disparadores te dejan seleccionar un control contenido en el Update-
Panel, y al asociarlo conseguiremos generar un postback con recarga completa de la
página aunque la propiedad ChildrenAsTriggers tenga el valor por defecto de True.

7.- Indicación de progreso de las llamadas


asíncronas
En los ejemplos que hemos hecho, las tareas realizadas como respuesta a los eventos
de servidor eran triviales para poder centrarnos en la funcionalidad AJAX única-
mente. En una aplicación real tendremos tareas de todo tipo que pueden ser más o
menos costosas. Por ejemplo ciertas labores de acceso a datos pueden tardar bastante
en realizarse, y si le sumamos la velocidad de la red que puede ser baja, una carga
importante de usuarios en el servidor, etc... es posible —y hasta frecuente— que
algunas de las llamadas asíncronas que se realizan al servidor tarden más de la
cuenta en regresar.
Mientras el navegador espera la respuesta de una llamada asíncrona para repin-
tado parcial, el usuario no tiene pista alguna de lo que está pasando. En cuanto pase
medio segundo y no haya un efecto visible en la página la mayoría de los usuarios
38  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

se empezarán a preguntar si ha fallado algo. Incluso muchos se pondrán a pulsar los


botones nuevamente varias veces para comprobar si responde, agravando el problema
pues estarán lanzando más peticiones al servidor.
Por eso, salvo en los casos más triviales, es importante utilizar algún tipo de
indicador visual para los usuarios que aparezca en pantalla mientras dura la actua-
lización (el viaje petición-respuesta-procesamiento desde el navegador al servidor y
vuelta otra vez). Pero tampoco es recomendable que aparezca inmediatamente pues si
la llamada es muy rápida se verá casi como un parpadeo que también desconcentrará
al usuario.
Para evitarnos la tarea de tener que programar algo así por nosotros mismos,
ASP.NET AJAX ofrece un control llamado UpdateProgress que sirve precisamente
para eso. Puedes verlo en la lista de controles de la figura 1.
Al arrastrarlo sobre la superficie de nuestra página ASPX se mostrará como un
control contenedor, de forma que podremos colocar dentro de él los elementos que
deseemos que aparezcan durante una actualización asíncrona de la página.
Aunque hay quien prefiere un espartano cartel de texto (mira sino los indicadores
de progreso que muestra GMail, que son unas simples letras sobre fondo granate), lo
más habitual es usar un gráfico animado en formato GIF que muestre una sensación
de progreso mientras carga la página por debajo. En la página www.ajaxload.info
dispones de una interesante utilidad gratuita que te permite generar online muchos
gráficos de progreso diferentes. Simplemente escoges el tipo de gráfico entre una
amplia lista, los colores y si quieres que sea transparente o no y el programa genera
el gráfico para ti y te permite descargarlo.

Figura 8.- Utilidad online para generar gráficos de progreso AJAX


ASP.NET AJAX en el servidor  39

Para probar su funcionamiento vamos a añadir uno de estos controles a la página,


justo a continuación del ScriptManager. Genera en la ajaxload.info un gráfico que te
guste para indicar el progreso, grábalo a disco en el proyecto, refresca el explorador
de soluciones y desde él arrástralo dentro del control de progreso.
Selecciona el control UpdateProgress y ve a sus propiedades. Establece la pro-
piedad AssociatedUpdatePanelID con el nombre del UpdatePanel, y su propiedad
DynamicLayout como False, tal y como se ve en la figura 9.

Figura 9.- Propiedades del control UpdateProgress

Con estas propiedades lo que conseguiremos es que el indicador de progreso se


muestre automáticamente durante las actualizaciones parciales del UpdatePanel1, y
que el espacio que ocupa en la página se conserve incluso cuando está oculto. Si
establecemos DinamicLayout como True, que es su valor predeterminado, cuando el
indicador aparezca reclamará su sitio en la página (ya que antes no ocupaba nada),
desplazando al resto de los controles hacia abajo, lo que puede crear un efecto feo
si no lo tenemos en cuenta. Con el valor que le hemos dado, el espacio que ocupa
está reservado y al aparecer no provoca desplazamiento alguno.
La representación en marcado del control UpdateProgress es como la siguiente:
<asp:UpdateProgress ID=”UpdateProgress1” runat=”server” AssociatedUpd
atePanelID=”UpdatePanel1”>
<ProgressTemplate>
<img alt=”Cargando...” src=”circulito.gif” style=”width:
16px; height: 16px” />
</ProgressTemplate>
</asp:UpdateProgress>

Lo que está dentro de sus etiquetas <ProgressTemplate> (en la superficie conte-


nedora si lo vemos en modo diseño) se convierte en tiempo de ejecución en el HTML
correspondiente encerrado dentro de una capa con la etiqueta <div>.
40  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Todavía queda por ver una propiedad muy importante: DisplayAfter. En ella
indicaremos el tiempo en milisegundos que tardará el indicador en aparecer si la
operación asíncrona no ha terminado. Por omisión este tiempo es de 500 milise-
gundos. Así, si la operación de postback tarda más de medio segundo en finalizar,
aparecerá el indicador automáticamente para que el usuario sepa que el proceso está
funcionando y que tardará un poco. Al terminar la operación se ocultará también
de manera automática.
Para probar el indicador en nuestro ejemplo sólo nos resta simular el efecto de
una operación lenta en el servidor. Así que en el evento de pulsación del botón
añadiremos una línea que hará que se bloquee la ejecución durante dos segundos
justo antes de actualizar la hora en la etiqueta:
System.Threading.Thread.Sleep(2000)

Ahora ejecuta la aplicación y pulsa el botón para mostrar la hora.


¡Eh! ¿Qué pasa? No se está mostrando el indicador de progreso. La cosa no
funciona...
El motivo es que en este caso nuestro disparador era un elemento externo al Up-
datePanel (el botón está fuera y lo asociamos con un Trigger, si recuerdas). Cuando
el control de progreso se asocia a un UpdatePanel concreto mediante su propiedad
AssociateUpdatePanelID, no es capaz de detectar el comienzo y finalización de
los postback generados por disparadores externos al panel. Ello se debe a que el
JavaScript asociado lo que hace es recorrer la jerarquía de elementos DOM que
hay bajo el UpdatePanel que debe controlar, para encontrar qué control ha lanzado
el repintado. Como un disparador externo no está en esa jerarquía no es capaz de
encontrarlo y por lo tanto el progreso no se muestra. No tendríamos problema si el
botón estuviese dentro del panel.
Para solucionarlo hay una acción muy simple que podemos llevar a cabo: elimi-
nar la propiedad AssociatedUpdatePanelID. Si tenemos en la página un control de
indicación de progreso que no está asociado a un panel, lo que conseguimos es un
indicador de progreso universal que se mostrará para cualquier actualización parcial
que se haga en la página.
Si ahora ejecutas de nuevo la página verás que ya se comporta como era de espe-
rar y el gráfico animado de progreso aparece al cabo de medio segundo, ocultándose
automáticamente al final.
De hecho es muy habitual tener un único control de indicación de progreso en la
página que sirva para el proceso de repintado de cualquiera de los paneles contenidos
en ésta.

8.- Refrescos parciales periódicos


Otro de los controles de AJAX disponibles es el control Timer. Éste nos permite
lanzar automáticamente procesos de actualización parcial con una frecuencia perió-
dica y sin que sea necesaria acción alguna por parte de un usuario.
Se puede utilizar para actualizar los contenidos de un UpdatePanel o por el
contrario actualizar la página completa. Otra aplicación interesante es la de ejecutar
ASP.NET AJAX en el servidor  41

periódicamente código en el servidor aunque no sirva para actualizar nada visible


en la interfaz. Es un control dependiente de AJAX y aunque se use para esto último
va a requerir siempre la presencia de un ScriptManager en la página (tampoco es la
mejor forma de hacer algo así, todo hay que decirlo).
La única propiedad interesante de este control es Interval. A través de ella se
establece el número de milisegundos que servirán como periodicidad del temporiza-
dor. Por defecto el periodo es de un minuto (60.000 segundos). No conviene usar un
valor demasiado bajo para Interval ya que correremos el riesgo de saturar el servidor
con el número de peticiones muy seguidas que se generarán.
Cada vez que se cumple el tiempo indicado en el intervalo se lanza un evento
de servidor para el temporizador. Este evento se denomina Tick. Como cualquier
otro evento de servidor (como el clic de un botón o cualquier otro), éste provocará
un postback de la página. Y al igual que con cualquier otro control, dependiendo
de donde esté colocado dentro de la página se producirá un refresco parcial o total
de la misma.
Así, si el temporizador está dentro de un UpdatePanel con ChildrenAsTriggers=
True (valor por defecto y el más habitual), el resultado será que se refrescan los
contenidos del panel asíncronamente con la periodicidad especificada. Si lo coloca-
mos fuera de un UpdatePanel se producirá un refresco completo de la página. Por
supuesto podemos asignarlo como un disparador para uno o varios UpdatePanel, en
cuyo caso lo situaremos fuera y nos servirá para hacer que se refresquen parcial-
mente cada cierto tiempo.
Si tenemos varios temporizadores en la página y todos tienen el mismo intervalo
es mucho más eficiente colocar uno sólo, fuera de todos los paneles, y asignarlo
como disparador para refrescarlos a todos.
Otra cosa a tener en cuenta es que para actuar sobre los controles de la página
durante el evento del servidor, es indistinto hacerlo en el evento Tick del control o
bien directamente en el evento Load de la página, ya que como sabemos durante el
postback (aunque sea asíncrono) la página ejecuta su ciclo de vida normal.
En el código descargable he incluido una página de ejemplo de uso del temporiza-
dor que se limita a actualizar la hora en una etiqueta cada segundo, consiguiendo un
reloj en funcionamiento. Es evidente que algo tan sencillo es muy fácil de conseguir
sin más que un poco de JavaScript en el cliente, así que hacerlo con un tempori-
zador es un verdadero derroche de recursos y, como decía antes, “matar moscas a
cañonazos” :-), pero nos sirve para ilustrar su funcionalidad.
Sólo hay una última cosa importante que debemos tener en cuenta si la precisión del
intervalo es importante para nosotros, y es que los tiempos pueden cambiar ligeramente
en función de si el temporizador está dentro de un UpdatePanel o fuera de él.
Si el Timer está fuera de cualquier UpdatePanel actuando como disparador
de alguno de ellos, su periodo de refresco se recarga automáticamente en cuanto
lanza el evento de servidor, sin esperar a que éste regrese, ya que el código de la
página que le afecta no se modifica. Sin embargo cuando lo colocamos dentro de
un panel, al refrescar su contenido, se regenera el código del temporizador y el
tiempo comienza a contar de nuevo. Esto ocurre tras haber regresado del postback
asíncrono. Por lo tanto hay una diferencia con el intervalo de la otra situación que
será tanto mayor cuanto más tarde en procesarse la llamada asíncrona al servidor.
42  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Debes tenerlo en cuenta porque es una sutil diferencia pero puede ser importante
en algunas aplicaciones.

9.- Unicidad del ScriptManager


Al trabajar con los controles de servidor de ASP.NET AJAX hay que tener en cuenta
una cosa muy importante, y es que cuando digo que debe existir un control Script-
Manager en la página, me estoy refiriendo a que debe haber uno y sólo uno en todo
el árbol de controles de la misma. Si intentamos añadir más de un control de este
tipo a la página el compilador no se quejará, pero recibiremos un error en tiempo
de ejecución como el de la figura.

Figura 10.- No es posible tener más de un control ScriptManager en una página

Esto tiene más importancia de la que parece a primera vista y es que afectará
a la forma en la que podremos reutilizar algunas partes de nuestra aplicación. Por
ejemplo, si tenemos una o varias Master Pages deberemos decidir si el control Script-
Manager se colocará en la MP o bien tendremos que colocarlo en cada una de las
páginas que hagan uso de funcionalidad AJAX. Lo que no podemos es tenerlo en los
dos sitios a la vez ya que en tiempo de ejecución, al fusionarse los controles de la
página plantilla y de la página funcional, nos encontraremos con dos controles y se
producirá un error. Lo mismo ocurre si encapsulamos funcionalidad AJAX dentro de
un control de usuario y en éste incluimos el ScriptManager. Si luego lo arrastramos
sobre una página que ya tenga su propio ScriptManager se producirá un conflicto
y la página no funcionará.
En general si nuestra aplicación hace un uso amplio de las funcionalidades AJAX,
como es cada vez más habitual, lo más recomendable es incluir el ScriptManager
directamente en la plantilla (Master Page). Así estará disponible para todas las pá-
ginas hija que hagan uso de la misma.
Si alguna de las páginas hija no utiliza refrescos parciales siempre podemos
desactivar esta característica obteniendo una referencia al control durante la carga y
usando su propiedad EnablePartialRendering, que ya hemos visto:
ASP.NET AJAX en el servidor  43

Dim sm As ScriptManager = ScriptManager.GetCurrent()


sm.EnablePartialRendering = false

El método estático GetCurrent de la clase ScriptManager nos permite obtener


una referencia a la instancia actual que se esté usando en la página, independiente-
mente de que esté ubicada en la propia página, en un control o en la Master Page.

10.- el control ScriptManagerProxy


Como hemos comentado (y veremos con detalle luego) el ScritpManager ofrece
soporte para multitud de cuestiones más allá del repintado parcial de las páginas. Si
incluimos uno de estos controles en una Master Page y luego requerimos servicios
adicionales del mismo en una de sus páginas hijas, tenemos un problema.
Si por ejemplo, queremos añadir una referencia a un script o a un servicio Web
que es de uso específico de la página actual, dado que no podemos incluir otro
control ScriptManager para hacerlo ¿cómo procederemos?.
Una posible opción sería incluirlo en el que está en la Master Page, pero así tendría-
mos que incluir todos los posibles servicios en la MP y ponerlos a disposición de todas
las páginas, los necesiten o no, lo cual es muy ineficiente y propenso a errores.
Para solucionar este problema existe el control ScriptManagerProxy. Este control,
disponible también en el panel de herramientas (ver figura 1), se arrastra a la página
como otro cualquiera y actúa como un apéndice del Script Manager único que está
en la Master Page para añadirle las características adicionales que necesitemos.
Por ejemplo, este fragmento muestra la sintaxis de uno de estos controles que se
usa para incluir en la página, en la sección <scripts> (luego la estudiaremos), un
código de Script específico que se necesita para dotarla de alguna funcionalidad de
lado cliente:
<asp:ScriptManagerProxy ID=”ScriptManagerProxy1” runat=”server”>
<Scripts>
<asp:ScriptReference Path=”scriptEspecifico.js” />
</Scripts>
</asp:ScriptManagerProxy>

Este tipo de proxy se puede utilizar también en controles de usuario que luego van
a ser utilizados en páginas que ya tiene un ScriptManager, bien porque lo incluyan
directamente, bien porque lo tenga la Master Page correspondiente.
Debes recordar bien este control porque lo usarás mucho más de lo que te ima-
ginas en cualquier aplicación grande.

11.- Gestión de errores AJAX


En condiciones normales todo va a funcionar de maravilla. Pero la dura realidad
es que siempre, en algún momento, pasa algo que hace que las cosas fallen. Si
durante un postback asíncrono se produce un error en el servidor ¿cómo podemos
gestionarlo?.
44  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Vamos a crear una pequeña aplicación de ejemplo que produzca errores para ver
cómo podemos gestionarlos.
Crea una nueva página en el proyecto, “ErroresAjax.aspx”, y añádele un Script-
Manager, un UpdatePanel y dentro de éste un simple botón con el texto “Provocar
Error”. En el manejador del evento click de este botón lanza una excepción así:
Throw New Exception(“Error provocado a mano!!”)

Ahora ejecútala pulsando CTRL + F5 para evitar que se trabaje en modo depu-
ración, pues en ese caso saltaría un punto de interrupción en Visual Studio y no
verías cómo funciona en la realidad, cuando esté en producción.
Verás que el error en el lado servidor se traduce en el navegador en forma de un
error de JavaScript. Si tienes la depuración de JavaScript activada verás un mensaje
como el de la figura 11. Como se puede comprobar en dicha figura, el mensaje
descriptivo del error original se visualiza en el error de JavaScript también.

Figura 11.- Error de JavaScript provocado en el servidor

En este caso el error, al ser manual, tiene una descripción amigable y neutra, y
no pasa nada porque se vea. Sin embargo esta situación no es la más recomendable.
Una de las reglas básicas de seguridad de aplicaciones dice que no se debe dar
información técnica de errores a usuarios finales. Generalmente los errores que se
producirán en el servidor incluyen en su descripción detalles internos de la aplica-
ción, por ejemplo nombres de campos en bases de datos o rutas en disco, que podrían
dar información valiosa a posibles atacantes.
Por este motivo se recomienda gestionar siempre los errores y devolver al usuario
un mensaje más amigable, sin dar detalles internos. Para ello el control Script-
Manager nos ofrece el evento AsyncPostBackError, en el que podremos gestionar
cualquier tipo de excepción que se produzca durante un postback asíncrono.
ASP.NET AJAX en el servidor  45

Figura 12.- Evento para gestión de errores en postbacks asíncronos.

Como segundo argumento de este evento tenemos un objeto de tipo Async-


PostbackErrorEventArgs. Su única propiedad interesante, Exception, nos permite
acceder a la excepción producida y trabajar con ella para actuar en consecuencia.
En nuestro ejemplo vamos simplemente a poner un mensaje más amigable inde-
pendientemente de cuál sea la excepción producida:
Protected Sub ScriptManager1_AsyncPostBackError(ByVal sender As
Object, ByVal e As System.Web.UI.AsyncPostBackErrorEventArgs) Handles
ScriptManager1.AsyncPostBackError

ScriptManager1.AsyncPostBackErrorMessage = “Se ha producido


un error en la operación. Si persiste contacte con el administrador”

End Sub

Utilizamos la propiedad AsyncPostBackErrorMessage del ScriptManager para


asignar un mensaje más amigable:

Figura 13.- El mensaje amigable en el error de lado cliente

No obstante, sería más adecuado que se mostrase de una forma más integrada
dentro de la página, y no traducido a una excepción de JavaScript.
46  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

La parte cliente de ASP.NET AJAX ofrece soporte para interceptar de manera


sencilla los distintos eventos en el ciclo de vida de las peticiones asíncronas. Vamos
a sacarles partido para detectar desde JavaScript que se ha producido un error
durante un evento de servidor asíncrono, y mostrar al usuario un mensaje dentro
de la propia página, mediante capas. En la siguiente figura se puede observar el
aspecto del mismo.

Figura 14.- Mensaje de error personalizado

Se muestra el mensaje de error dentro de una pequeña capa flotante, y se oscurece


la página para darle más protagonismo al mensaje, e impedir al mismo tiempo que
el usuario pueda pulsar sobre el resto de los elementos. Es una forma sencilla de
simular un diálogo modal dentro de una página usando únicamente capas y estilos.
El efecto de transparencia se consigue con un gráfico GIF semitransparente puesto
como fondo de una capa.
Para conseguirlo se debe incluir en la página un poco de código JavaScript que
se encargue de interceptar un posible error al volver del postback asíncrono, y que
luego se encargue de mostrar y ocultar la capa flotante sobre la que se mostrará
el mensaje.
ASP.NET AJAX en el servidor  47

Nota:
Esta parte es código de lado cliente, que forma parte de otro capítulo del libro, pero lo he in-
cluido aquí porque está directamente relacionado con la gestión de errores. No vamos a ver los
detalles del código JavaScript utilizado para conseguir toda la funcionalidad. Nos centraremos
únicamente en la parte relacionada con la gestión del error, que es la que nos ocupa. Puedes ver
el código completo del ejemplo en el ZIP con las demos del libro.

La clase PageRequestManager proporcionada en el lado cliente por ASP.NET


AJAX sirve para acceder a toda la funcionalidad de postbacks asíncronos. Podemos
obtener una referencia a la clase actualmente utilizada para realizar las peticiones
con esta expresión JavaScript:
Sys.WebForms.PageRequestManager.getInstance()

El método add_endRequest() de esta clase sirve para añadir nuevos manejadores


al evento de finalización de llamada asíncrona. Es decir, que podemos ser notificados
automáticamente de cuando un postback asíncrono regresa. Si definimos una función
JavaScript con el nombre de, por ejemplo, EndRequesthandler, podemos usarla para
interceptar el evento y así poder trabajar con los datos de la petición:
Sys.WebForms.PageRequestManager.getInstance().add_
endRequest(EndRequestHandler);

La función recibe dos parámetros, al más puro estilo .NET aún siendo JavaScript,
siendo el primero de ellos el “sender” y el segundo una referencia a un objeto de la
clase EndRequestEventArgs. Esta clase sólo tiene dos propiedades interesantes:
• Error: contiene información del error. Como JavaScript no tiene propiedades,
por convención se debe leer su contenido llamando al método get_error(). La
clase Error devuelta tiene tres propiedades (message, number y name) que
nos permiten averiguar respectivamente el mensaje, el número y el nombre
del error.
• errorHandled: se usa para indicar a la infraestructura de AJAX que el
error ya lo hemos gestionado nosotros y que por lo tanto no debe dejar
que se produzca. Se escribe en ella, por convención, llamando al método:
set_errorHandled().
En el código de ejemplo descargable están todos los detalles comentados, pero
basta indicar que lo que se hace es comprobar en un condicional si la propiedad
Error contiene o no una referencia válida, en cuyo caso se muestra el mensaje de
la figura anterior, y se indica que ya hemos gestionado nosotros el problema, para
evitar que se manifieste como una excepción de JavaScript. Descárgate y échale un
vistazo al código de ejemplo.
48  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

En aplicaciones que no usan AJAX lo habitual es que gestionemos los errores


usando alguna página especial, que configuramos en nuestro web.config para que
la infraestructura de ASP.NET se encargue de mostrarla. Por ejemplo, podríamos
tener esta configuración:
<customErrors mode=”RemoteOnly” defaultRedirect=”errorGenerico.aspx”>
<error statusCode=”500” redirect=”ErrorServidor.aspx” />
<error statusCode=”403” redirect=”Prohibido.aspx” />
<error statusCode=”404” redirect=”NoEncontrado.aspx” />
</customErrors>

Así, cuando se produzca un error en el servidor (código de estatus HTTP igual


a 500) se mostrará automáticamente al usuario la página “ErrorServidor.aspx”. De
similar manera se comportaría la aplicación ante intentos de acceso no autorizados
(403) o páginas no encontradas (404), redirigiendo a las páginas correspondientes. Si
se produce cualquier otra circunstancia no contemplada específicamente se muestra
una página de error genérica “errorGenerico.aspx”.
Por defecto el ScriptManager trata los errores producidos en el servidor para
que sean totalmente compatibles con el comportamiento indicado en el web.config.
Por lo tanto si hemos definido una página especial para errores de servidor en
nuestra configuración y se produce una excepción durante un postback asíncrono, la
infraestructura de ASP.NET AJAX se encarga de que todo funciones de la manera
normal, redirigiendo a la página indicada. Debemos tener en cuenta pues, que en
caso de haber configurado páginas de error personalizadas, no recibiremos jamás
en el cliente notificaciones de los errores, y las técnicas mostradas anteriormente
no funcionarán.
La propiedad AllowCustomErrorsRedirect del ScriptManager sirve para ajustar
este comportamiento. Por defecto vale True y obtenemos el funcionamiento habi-
tual. Si la establecemos a False se hará caso omiso del web.config para los errores
producidos durante repintados parciales.
En el ejemplo descargable de la Web puedes quitar los comentarios en el web.config
para habilitar una página personalizada para los errores 500. Verás cómo a partir de
ese momento el diálogo de la figura 14 se deja de mostrar y aparece la página de error
que se ha definido.

12.- Incompatibilidades de AJAX


A la hora de usar el repintado parcial de los controles y las llamadas asíncronas
debemos tener en cuenta algunas limitaciones. Son pocas pero pueden impactar
negativamente en nuestros desarrollos si no las hemos considerado de antemano.
ASP.NET AJAX en el servidor  49

Algunos controles Web de ASP.NET, dado su modo de trabajar, no son compa-


tibles con las actualizaciones parciales de la página y, por consiguiente, no están
diseñados para funcionar dentro de un control UpdatePanel. La mayoría no ofrecen
problemas, pero hay algunos que presentan incompatibilidades que, o bien impiden
su funcionamiento o limitan alguna de sus características:
• Los controles de envío de archivos al servidor: FileUpload y HtmlInputFile,
que sólo funcionarán de modo nativo si se realiza un postback síncrono
común. En el mercado hay algunos controles de publicación de archivos que
soportan asincronismo (por ejemplo el de Subgurim, gratuito y descargable
desde http://fileuploadajax.subgurim.net), si bien no usan UpdatePanel
para conseguirlo.
• Los controles TreeView y Menu.
• El control Substitution.
• Los controles Login, PasswordRecovery, ChangePassword y CreateUserWi-
zard cuyo contenido no ha sido convertido previamente en plantillas editables.
Es una limitación con poco impacto porque, o bien se convierten a plantillas
o bien se pueden utilizar las API de seguridad directamente desde JavaScript
para conseguir el mismo efecto, como veremos en el próximo capítulo.
• Los controles GridView y DetailsView cuando su propiedad EnableSortin-
gAndPagingCallbacks es True. Por defecto no tienen activada esta propiedad
por lo que no hay problema. Además, esta propiedad se utiliza precisamente
para conseguir el mismo efecto que con el UpdatePanel (sólo que utiliza otras
técnicas diferentes), por lo que no hay problema alguno en desactivarla dentro
del panel. No se puede considerar una verdadera limitación.
Adicionalmente si utilizas Visual Studio 2005 con ASP.NET 2.0, en esta versión
antigua del entorno de desarrollo y del Framework no están soportados en AJAX
los controles relacionados con WebParts. Tampoco funcionarán los controles de
validación, si bien se pueden hacer compatibles con sólo desconectar la validación
de lado cliente (propiedad EnableClientScript = False). No existen estos problemas
de compatibilidad con estos controles en versiones de Visual Studio superiores a la
2008, incluida ésta.
Todos los demás controles funcionan dentro de los controles UpdatePanel. Sin
embargo, en algunas circunstancias, un control podría no funcionar como era de
esperar si lleva a cabo alguna de estas acciones:
• Registrar scripts en la página usando métodos de la clase ClientScriptManager.
Esta clase se abandona en el caso de aplicaciones AJAX a favor de los méto-
50  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

dos equivalentes que ofrece el propio control ScriptManager, compatibles con


las actualizaciones parciales. Si necesitas registrar scripts como parte de un
postback usa éstos últimos. Lo veremos con detalle en el próximo capítulo.
• El uso directo de casi cualquier método de la clase HttpResponse no se
llevará bien en general con las llamadas asíncronas. Por eso debemos evitar la
llamada directa a los métodos: BinaryWrite, Clear, ClearContent, ClearHea-
ders, Close, End, Flush, TransmitFile, Write, WriteFile y WriteSubstitution.
Especialmente habitual es la generación directa de scripts o etiquetas HTML
durante el repintado de un control o durante el procesado de un evento me-
diante llamadas al método Response.Write, que es algo que debemos evitar.

13.- AJAX Control Toolkit


Microsoft, conjuntamente con la comunidad de desarrolladores, ha creado el Kit
de controles AJAX o AJAX Control Toolkit. Se trata de un conjunto de más de 30
controles que permiten extender y dotar de funcionalidad extra avanzada a controles
comunes de ASP.NET.
La funcionalidad de estos controles se centra en el lado del cliente, es decir, en
mejorar las características de los controles normales cuando ya están renderizados
en el navegador, pero se añaden y configuran desde el servidor, por eso los he
incluido en este capítulo. Así, por ejemplo, gracias al Toolkit es muy sencillo hacer
cosas como añadir un calendario desplegable a un TextBox para facilitar al usuario
que escoja una fecha, o mostrar diálogos de confirmación integrados en la página,
mejorar el aspecto de los mensajes de validación, crear animaciones, tener un editor
HTML, etc.
Por el mero hecho de utilizar estos controles obtendremos grandes mejoras en la
usabilidad de las interfaces de nuestras aplicaciones Web ASP.NET. Los podemos
usar en combinación con los repintados parciales o de manera completamente inde-
pendiente, o lo que es lo mismo, requieren la existencia de un control ScriptManager
en la página, pero no es necesario introducirlos dentro de un UpdatePanel ni utilizar
éste ni ningún otro de los controles ASP.NET AJAX que hemos estudiado.
Su uso es extremadamente sencillo. Lo más complicado sea tal vez instalar el
Toolkit, así que vamos a explicar cómo obtenerlo y ponerlo en marcha en nuestro
sistema.
El AJAX Control Toolkit está disponible para descarga gratuita en CodePlex (el
directorio de proyectos Open Source de Microsoft), y en concreto en la siguiente
dirección: http://ajaxcontroltoolkit.codeplex.com/Release/.
ASP.NET AJAX en el servidor  51

Figura 15.- Página de descarga del AJAX Control Toolkit

La última versión allí disponible funcionará sin problemas tanto en Visual Studio
2008 como en Visual Studio 2010. Si estás usando Visual Studio 2005 con la versión
2.0 de la plataforma deberás buscar, un poco más abajo, las versiones anteriores
específicas para este entorno.
Como se observa en la figura 15 la página ofrece tres variantes para descargar.
• La primera versión “Binary” contiene únicamente las DLL necesarias para
poder trabajar con el control desde Visual Studio. Es la variante que necesi-
tamos si queremos distribuir el Toolkit con alguna de nuestras aplicaciones
o para instalar en un servidor en el que se vaya a utilizar éste con una
aplicación allí desplegada.
• La variante “Source” contiene, además de los binarios compilados, todo el
código fuente de los diferentes controles. Es una gran fuente de información
y estudio si estamos interesados en cómo construir este tipo de controles. Es
la más apropiada para ser utilizada en nuestro equipo de desarrollo.
52  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

• Por fin la variedad “ScriptFilesOnly” contiene únicamente el código JavaScript


que utilizan los controles para su funcionalidad en el navegador. Se trata de la
funcionalidad de los controles en su estado puro, para usar directamente en el
navegador. Esta es la variante que usarán los programadores más avanzados
que emplearán directamente código JavaScript para instanciar los diferentes
Behaviours de estos controles en sus páginas.
En la mayor parte de los casos lo mejor es bajarse la primera o segunda va-
riedad.
Una vez descargado el ZIP correspondiente, descomprímelo en una carpeta de tu
disco duro en la que vaya a estar disponible todo el tiempo, ya que una vez añadidos
los controles a Visual Studio los usaremos siempre desde allí. La variante “Binary”
descomprimirá ya directamente la DLL y carpetas asociadas que necesitamos. En
el caso de la descarga con código fuente, la DLL que usaremos está dentro de la
carpeta “Binaries” una vez descomprimido el ZIP. Dentro de la carpeta de binarios
del Toolkit hay una DLL, AjaxControlToolkit.dll, que contiene toda la funcionalidad
de los controles, así como una serie de carpetas con identificadores de idiomas (como
“es”, “ar”, “cs”, “de”...) que contienen ensamblados satélites con los textos adaptados
a cada uno de estos idiomas.
Ahora lanza Visual Studio y crea una nueva aplicación Web o abre cualquier
aplicación Web que tengas ya hecha. Edita una página ASPX cualquiera para poder
desplegar la barra de controles Web, como si fueras a añadir uno sobre la página.
Al desplegar la barra de herramientas pulsa con el botón derecho sobre alguna
zona libre de ésta y en el menú contextual añade una nueva sección. Llámala “AJAX
Control Toolkit”. Vuelve a pulsar con el botón derecho dentro del área vacía de esta
nueva sección y elige la opción “Elegir elementos” (“Choose Items” en la versión
en inglés del entorno):

Figura 16.- Añadiendo controles a la nueva sección que hemos creado.


ASP.NET AJAX en el servidor  53

Al hacer esto aparecerá, tras una cierta espera, un diálogo como el de la figura
17, en el que podremos elegir qué controles .NET queremos que estén disponibles
desde la barra de herramientas, y en concreto dentro de la sección actual.

Figura 17.- Diálogo de gestión de controles

Lo único que resta por hacer es, con el botón Browse de la figura, localizar
la DLL del AJAX Control Toolkit (recuerda: AjaxControlToolkit.dll, en la carpeta
correspondiente de la descarga anterior) y aceptar para que los controles se añadan
automáticamente a la barra de herramientas.

Nota importante:
En Visual Studio 2010 este proceso fallará con un mensaje similar a este:
“Could not load file or assembly ‘file:///C:\AjaxControlToolkit\AjaxControlToolkit.dll’
or one of its dependencies. Operation is not supported. (Exception from HRESULT:
0x80131515)”
El motivo es el tratamiento especial que hace la versión 4.0 de .NET de los ensamblados baja-
dos de Internet. El sistema operativo marca con un indicador especial a los ensamblados des-
cargados de cualquier recurso remoto (Internet o una carpeta compartida en la red local). De
este modo pueden ser reconocidos como posibles amenazas de seguridad al proceder, a priori,
de un origen no confiable como es Internet. El sistema operativo los trata de una forma dife-
54  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

rente debido a esto aunque los tengamos copiados ya en una carpeta local. Sin embargo .NET
en sus versiones anteriores a la 4.0 hacía caso omiso de esta información sobre el origen y al
estar ubicados en local los trataba como ensamblados confiables. Como mejora de seguridad en
.NET 4.0 se tiene en cuenta esta particularidad y limita la capacidad de todos estos .exe o .dll
que se descarguen directamente o dentro de un ZIP desde una ubicación remota. Por este moti-
vo la DLL del AJAX Control Toolkit no es capaz de cargarse en el entorno de Visual Studio.
Para solucionarlo lo que tenemos que hacer es desbloquear su funcionalidad. Para ello localiza
la DLL en tu disco duro y en sus propiedades verás un botón “Desbloquear” que sirve preci-
samente para esto (ver figura 18). Necesitarás permisos de administrador para que el botón
funcione, así que si estás en una carpeta de sistema (como por ejemplo la de Archivos de Pro-
grama) mueve antes la DLL a otro lugar, cambia este atributo y devuélvela a su sitio original.

Figura 18.- Desbloqueo de ensamblado descargado desde sitio remoto.

Ahora repite la operación anterior y verás como el problema desaparece y los controles se incor-
poran a tu barra de herramientas.

Una vez terminada la operación verás que en tu barra de herramienta aparecen


multitud de controles nuevos, como se puede ver en la figura 19.
ASP.NET AJAX en el servidor  55

Figura 19.- Los controles del AJAX Control Toolkit

Aunque tengamos todos esos controles en la barra de herramientas, lo cierto es


que, al trabajar con Visual Studio 2008 o Visual Studio 2010, no vamos a usarlos
casi nunca desde ella. En Visual Studio 2005 sí que será necesario arrastrarlos
desde ahí.
En Visual Studio 2008 o superior las extensiones que ofrece el Toolkit están
integradas con el entorno una vez las hayamos instalado. Así, al seleccionar un
control en la página como un TextBox u otro cualquiera, éste presentará entre sus
tareas asociadas una nueva acción llamada “Añadir extensor”:

Figura 20.- Acción de un control para extenderlo con el Toolkit

Al pulsar sobre la acción se abre un diálogo en el cual podemos elegir qué


acción extensora, proporcionada por los diferentes controles del Toolkit, queremos
añadir a nuestro control. Para cada control ofrece las acciones más apropiadas. Por
56  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

ejemplo en la figura 21 estamos añadiendo un calendario desplegable a un control


TextBox, de forma que cuando los usuarios lo seleccionen se mostrará para ayudar
a introducir fechas.

Figura 21.- Extensión de un TextBox con un calendario desplegable.

Esta extensor se corresponde con la funcionalidad del control CalendarExtender


de la barra de herramientas (lo puedes identificar hacia la mitad de la columna de
la derecha en la figura 19.
A la hora de ejecutar la aplicación, todo el comportamiento que se observa en
el navegador (en este caso la aparición del calendario y su funcionalidad) se pro-
porciona directamente desde el lado cliente, mediante JavaScript. No hay postbacks
al servidor para ofrecer estas características. La funcionalidad es compatible con la
mayor parte de los navegadores modernos (Internet Explorer, Firefox, Safari...).

Figura 22.- El extensor para calendarios en funcionamiento básico


ASP.NET AJAX en el servidor  57

Por otro lado en la superficie de diseño en Visual Studio, a simple vista no ofrece
ningún signo distintivo de que el control está usando esta extensión. Si desplegamos
las acciones para el control, ahora además de tener la posibilidad de añadir extensio-
nes (podemos añadir cuantas necesitemos), también está la opción para quitar alguna
de las que se están usando.
Otro signo visible de que el control está siendo extendido es que en sus propie-
dades ahora hay una nueva sección denominada “Extensores” que ofrece, agrupadas,
las propiedades que controlan el comportamiento de cada uno de estas extensiones.
En la figura siguiente se ven las propiedades para el calendario desplegable, con las
que podemos amoldar su forma de funcionar a nuestras necesidades.

Figura 23.- Propiedades de controles extensores

En realidad la mayor parte de los controles del Toolkit son muy fáciles de utilizar
y en esta obra no vamos a entrar en detalles sobre el funcionamiento particular de
cada uno de ellos.
En la dirección:
http://www.asp.net/ajax/ajaxcontroltoolkit/samples/
se puede encontrar una aplicación de referencia en la que, uno a uno, se presenta
cada control, su sintaxis y sus posibilidades. Si visitas la página podrás verlos en
funcionamiento, y conocer los detalles de cómo sacarle partido.
58  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Figura 24.- Página de demostración y documentación del Control Toolkit

Esta misma aplicación de ejemplo se incluye dentro de la carpeta “SampleWeb-


Site” de la variante “Source” de las descargas del Control Toolkit, así que puedes
ejecutarla en local y mirar cómo está hecho cada ejemplo para aprender más todavía
sobre ellos.
En el código de ejemplo descargable desde la web del libro he incluido una
pequeña página de ejemplo (“ControlToolkit.aspx”) en la que un cuadro de texto
se extiende con un calendario y una marca de agua para indicar el formato a los
usuarios. El botón de envío se extiende con un control ConfirmButtonExtender de
manera que nos pregunte si queremos enviar los datos al pulsarlo, y los dos vali-
dadores incluidos se han extendido con sendos controles ValidatorCalloutExtender
para dotarlos de mayor vistosidad. Ejecútalo para ver cómo, con sólo añadir estos
extensores y sin código adicional, la interfaz se puede mejorar mucho.

Figura 25.- Un ejemplo de validador mejorado

Merece la pena dedicarle un tiempo para conocer bien el AJAX Control Toolkit,
ya que mejorarán mucho la calidad de tus interfaces con un mínimo esfuerzo.
capítulo
4
ASP.NET AJAX
en el navegador

Con todo lo visto hasta ahora en el capítulo anterior, ya tenemos herramientas sufi-
cientes para desarrollar impactantes aplicaciones Web de alta velocidad de respuesta,
sacando partido a los repintados parciales. Pero... ¿tendremos siempre realmente
respuestas rápidas en la interfaz? ¿Es realmente todo tan fácil como parece?
El ser humano no se caracteriza precisamente por la mesura en sus acciones.
La propensión natural en cualquier ámbito es a abusar de aquellas cosas que nos
gustan o nos reportan beneficio sin pararnos mucho a pensar en las consecuencias,
sobre todo si son fáciles y parecen no tener efectos negativos. Por ello cuando los
programadores Web con ASP.NET, acostumbrados a los postback, descubren las
posibilidades que les otorga el binomio ScriptManager-UpdatePanel, lo habitual es
que empiecen a usarlo en todas partes.
Al fin y al cabo es muy fácil y “no hay que saber demasiado”: se arrastran un
par de controles a una página normal y todo funciona de maravilla. Además, toda
aplicación Web moderna que se precie debe ofrecer en la medida de lo posible
este tipo de funcionalidad. El resultado habitual es que se colocan varios controles
UpdatePanel en todas las páginas o, peor todavía, se mete un gran UpdatePanel en
la página que contiene a todos los demás controles (créeme, lo he visto).
Tras desarrollar toda la funcionalidad se prueba la aplicación y todo funciona
de maravilla: rápido, sin molestos refrescos de la página y con un aspecto super-
profesional.
Los problemas vienen cuando la aplicación se pone en producción en el servidor.
En las primeras pruebas se nota todo algo más lento pero no parece realmente
preocupante. Al fin y al cabo estamos accediendo a través de Internet, no de la red
local, y es normal que la velocidad sea algo menor. Pasan los días y las semanas. A
medida que los usuarios de la aplicación aumentan, ya que tenemos más clientes o
visitas, la cosa empieza a ir cada vez peor. Llega un punto en el que trabajar con la
aplicación Web se vuelve insoportable de lo lenta que va. Miramos nuestro código
para ver si tenemos alguna consulta a la base de datos que nos esté cargando el
servidor y miles de posibilidades más, pero el problema de rendimiento no aparece.
¿Qué está pasando aquí?
Lo que está pasando es que, a pesar de la aparente ligereza de las llamadas
asíncronas con los Updatepanel, lo cierto es que (como ya se ha dicho) cada postback
59
60  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

asíncrono es un postback completo. Esto implica que se envía toda la información de


todos los controles de la página al servidor, incluyendo el ViewState. En la página
se recrea el árbol de controles, se procesan todos y cada uno de los eventos de éstos
así como los de la página. Al terminar se devuelve el nuevo ViewState y el nuevo
HTML para renderizar. La infraestructura de AJAX se queda sólo con el HTML
correspondiente al UpdatePanel que se va a refrescar, pero en realidad se ha tenido
que mover la página completa desde el cliente (el navegador) al servidor.
Así que, como dicen los anglosajones: “There’s no such thing as a free lunch”,
es decir, que nadie da algo a cambio de nada. Y en este caso la máxima es cierta
también.
No quiero decir que las técnicas que hemos visto hasta ahora sean dañinas para
el rendimiento. De hecho se pueden aplicar en sitios más o menos grandes sin pro-
blema. No obstante no se deben utilizar de cualquier manera, y en muchos casos
habrá que medir bien el uso que se hace de ellas. Su empleo indiscriminado sin
entender realmente lo que está pasando por debajo, conduce a muchos problemas
de rendimiento. Si tenemos claros los conceptos y buscamos la forma de hacer las
cosas lo mejor posible en cada circunstancia nos ahorraremos muchas frustraciones
y problemas en el futuro.
Es por esto que Microsoft no concibió ASP.NET AJAX únicamente como un
puñado de controles de servidor, sino que éstos se apoyan en una rica parte de
programación en el lado cliente, de la que nosotros podremos aprovecharnos también
para conseguir aplicaciones ágiles y optimizadas.
Este capítulo profundiza en las principales técnicas de trabajo en el lado del na-
vegador conducentes a mejorar mediante JavaScript las características de las páginas
AJAX así como su rendimiento.
Para seguir los próximos apartados es recomendable que tengas unos conoci-
mientos fundamentales del lenguaje JavaScript así como de HTML y manipulación
del DOM del navegador. Si no cumples estos requisitos puede que algún ejemplo
concreto te cueste comprenderlo, pero si investigas un poco el código por tu cuenta
no tendrás problema al final.
Vamos allá...

1.- Retrollamadas de red a métodos estáticos


Imagínate el siguiente ejemplo: tienes un formulario Web que contiene una rejilla
llena de datos, varios controles de texto para entrada de información, algunos bo-
tones, listas desplegables para filtrado así como algunas etiquetas y controles de
menor entidad. En una esquina hay una etiqueta que muestra un dato importante,
un simple número (por ejemplo el stock en un almacén), y que el usuario de vez en
cuando le gustaría ver actualizado.
Dado que el resto de la página permanece inalterado, sólo por actualizar esa
pequeña etiqueta no vamos a hacer un postback al servidor y recargar toda la enorme
página ¿verdad?. Aparentemente se impone el uso de un útil UpdatePanel para con-
ASP.NET AJAX en el navegador  61

seguir un repintado parcial de la etiqueta. Así que allá vamos nosotros: lo colocamos
rodeando a la etiqueta, establecemos el botón de refresco como disparador “et voilà”.
Listo para poner en producción.
Lo malo es que no nos hemos dado cuenta de que, cada vez que se refresca la
pequeña etiqueta, en realidad estamos procesando la página completa en el servidor
y transmitiendo a través de la Red enormes cantidades de información. Sólo el
ViewState de la página puede ocupar cientos de Kb. El resultado: un rendimiento
nefasto cuando lo ponemos al alcance de los usuarios.

1.1.- Análisis de tráfico con un UpdatePanel


Vamos a ver el efecto con un ejemplo muy sencillo. Desarrollaremos el mismo
ejemplo con una página común que utiliza el control UpdatePanel, y luego ve-
remos lo mismo usando código JavaScript super-eficiente para comprender las
diferencias. Ello nos dará una idea muy buena de cuándo merece la pena aplicar
una u otra técnica.
El proyecto completo está disponible en el ZIP con los ejemplos de código que
puedes descargar de la web de Krasis Press. Lo encontrarás en la carpeta “AJAX_
ServiciosCliente”.
Abre Visual Studio y crea una nueva aplicación Web. Elimina la página por de-
fecto “Default.aspx” y añade una nueva con el nombre “HoraConUpdatePanel.aspx”.
Arrastra sobre ella un ScriptManager, un UpdatePanel y, dentro de éste último, una
etiqueta (roja y con letras grandes) y un botón. El aspecto de la página debería ser
similar al de la figura 1.

Figura 1.- Aspecto de nuestra página de ejemplo

Como código para el evento de servidor que responde a la pulsación del botón
vamos a escribir simplemente una línea que mostrará, dentro de la etiqueta, la hora
actual en el servidor:
Protected Sub cmdDameHora_Click(ByVal sender As Object, ByVal e As
System.EventArgs) Handles cmdDameHora.Click
lblHora.Text = DateTime.Now.ToLongTimeString()
End Sub
62  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Ya está todo lo necesario. Ahora lanza la aplicación y pulsa el botón varias ve-
ces. Verás que la hora se muestra actualizada en la etiqueta a toda velocidad y sin
problema alguno.
Vamos a ir más allá de la superficie y veamos qué está pasando por debajo. Para
ello vamos a utilizar una de mis herramientas favoritas: Fiddler. Esta utilidad gratuita
ha sido creada por Eric Lawrence de Microsoft (aunque sin soporte oficial por la
compañía). Actúa como proxy en todas las peticiones Web que se hacen en el equipo
y permite inspeccionarlas de forma que podamos conocer sus datos, la información
devuelta en ellas, modificarlas al vuelo, y un sin fin de utilidades más. Se puede
descargar desde www.fiddler2.com.
Si ponemos en marcha Fiddler y pulsamos el botón que actualiza la hora en la
etiqueta veremos que se produce una llamada al servidor. Si la inspeccionamos para
ver sus contenidos obtenemos los datos que muestra la figura 2:

Figura 2.- Contenidos de la llamada al servidor con UpdatePanel

Aquí vemos que la llamada, dado que debe enviar el ViewState de los controles,
lleva 268 bytes de información. Y eso en esta página tan sumamente simple. Ima-
ASP.NET AJAX en el navegador  63

gínate lo que enviará en páginas más complejas como la del ejemplo al principio
de este epígrafe.
Pero eso no es todo. La respuesta que contiene el nuevo Viewstate así como
diversa información necesaria para actualizar el UpdatePanel ocupa 708 bytes.
Es decir que en total, para actualizar una información -la hora- que ocupa úni-
camente 8 caracteres (o sea, 8 bytes) estamos generando un tráfico de 976 bytes, es
decir, ¡122 veces mayor de lo necesario!.
Además a esto hay que añadir la sobrecarga en el servidor, en el que se ha
generado el árbol de controles y se han ejecutado todos eventos de la página y sus
controles tan sólo para poder asignarle el valor a la etiqueta. Esto usa procesador y
memoria y reduce la escalabilidad de la aplicación.
Parece que al final lo de usar siempre un UpdatePanel no es tan buen negocio
después de todo.

1.2.- Ejemplo optimizado con retrollamadas de red a métodos


estáticos
Está claro que en este ejemplo, a pesar de lo espectaculares que son los números
relativos, el impacto es mínimo. Y en muchos otros casos puede que también sea
así, y el uso de un UpdatePanel está más que justificado gracias a la productividad
que obtenemos.
No obstante vamos a ver cómo podemos crear una versión de esta misma página
que esté optimizada y nos genere el mínimo tráfico posible. Así aprenderemos a
solventar casos más graves en los que esta sobrecarga adicional no esté justificada
o nos produzca problemas graves de rendimiento.
Para ello vamos a explicar lo que se conoce como Retrollamadas de Red a
Métodos Estáticos, o de manera más común, Métodos de Página.
Esta técnica, como su propio nombre indica, nos permite definir dentro de la
página uno o más métodos que serán accesibles directamente a través de Internet
desde el navegador, siendo sólo necesario el uso de código JavaScript. De este modo
en lugar de llamar a la página completa, que es lo que hacíamos antes, ahora llama-
remos exclusivamente al método en cuestión, obteniendo una respuesta optimizada
como enseguida veremos con el ejemplo.
Añade otra página al proyecto con el nombre “MetodosDePagina.aspx”. Arrastra
a su superficie de diseño un control ScriptManager. Vete a la vista de código fuente
de la página e introduce una etiqueta <div> para definir una capa HTML. Otórgale
el nombre “divHora” y en su estilo asígnale un color rojo y un tamaño de texto
grande. Nos servirá para mostrar la hora y hará las veces de la etiqueta en nuestra
versión optimizada del ejemplo anterior. Arrastra también, desde el grupo HTML,
un botón HTML normal. En su atributo onclick asígnale el código JavaScript “Mos-
trarHora();”, que enseguida definiremos.
64  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

El código final debería tener este aspecto:

Figura 3.- Código HTML de nuestro ejemplo optimizado

A excepción del ScriptManager no estamos utilizando controles de servidor, pero


en cualquier caso daría igual porque, como veremos, ya no será necesario enviar el
ViewState ni otros elementos de formulario que hubiese en la página ASPX.
En el archivo Code Beside de la página (el .vb o .cs correspondiente) vamos a
definir una sencilla función llamada DameHora() que devolverá una cadena con la
hora actual en el servidor:
<WebMethod()> _
Public Shared Function DameHora() As String
Return DateTime.Now.ToLongTimeString()
End Function

Al igual que si de un miembro de un servicio Web ASMX se tratara decoramos


este método con un atributo WebMethod. Esto es indispensable para llamarlo desde
el lado cliente.
Otra cuestión importante a tener en cuenta es que el método que definamos para
ser llamado desde el cliente debe ser un método estático (o Shared en Visual Basic).
El motivo es que así no será necesario instanciar la página, lo que provocaría la
misma carga que en el caso anterior al reproducir el árbol de controles, etc...
El último detalle que nos queda es asignar a True la propiedad EnablePageMe-
thods del ScriptManager, que por defecto contiene un False.

Nota:
Esta propiedad hay que establecerla sobre el verdadero ScriptManager que vayamos a uti-
lizar. No se puede establecer en un ScriptManagerProxy por lo que si el nuestro está en un
Master Page debemos marcarlo en ésta directamente y quedará habilitado para todas las
páginas que la usen.
ASP.NET AJAX en el navegador  65

Cuando se cree el código HTML final resultante de ejecutar la página, la pro-


piedad EnablePageMethods hará que se genere automáticamente en el navegador el
código JavaScript necesario para llamar al método de la página. De hecho la llamada
en JavaScript será tan directa y fácil como escribir:
PageMethods.NombreMetodo

siendo NombreMetodo el nombre de alguno de los métodos de página que hayamos


definido.
A este método de JavaScript se le pasan dos argumentos: la función que se usará
para recibir los resultados de la llamada, y la función a la que se llamará si se ha
producido algún error.
Con estas premisas, todo el código JavaScript que necesitaremos escribir en el
navegador será el siguiente:
<script language=”javascript” type=”text/javascript”>
<!--
function MostrarHora() {
PageMethods.DameHora(finLlamada, gestorErrores);
}
function finLlamada(resultado) {
$get(“divHora”).innerHTML = resultado;
}
function gestorErrores(excepcion) {
$get(“divHora”).innerHTML = “Error en la llamada: “ +
excepcion.get_message();
}
// -->
</script>

Recuerda que la función MostrarHora es la que definimos para ser llamada


cuando se pulse en el botón.
La función $get() es una extensión global al lenguaje JavaScript de las muchas
que define ASP.NET AJAX y que nos permite acelerar la escritura de código. En
este caso $get es equivalente a llamar al método estándar del DOM, document.
getElementById, es decir, devuelve una referencia al elemento HTML cuyo nombre
le pasemos como parámetro. En el ejemplo nos devuelve una referencia a la capa
divHora de la página. Con su propiedad innerHTML podemos introducir el HTML
que necesitemos en su interior, en este caso la hora o el mensaje de error según lo
que ocurra.
Ejecuta la nueva página y pulsa el botón de actualizar la hora del servidor. Verás
como de inmediato se muestra dentro de la capa en color rojo y funciona tan bien
como la versión anterior.
En apariencia no hemos ganado gran cosa puesto que todo parece ir igual de bien
que en el ejemplo precedente. Como antes, vamos a analizar las peticiones al servidor
66  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

para ver qué información estamos intercambiando. Si capturamos con Fiddler la


petición generada por el método de página veremos el resultado de la figura 4.

Figura 4.- Contenidos de la llamada al servidor con un método de página

¡Ahora sí que hemos optimizado el trabajo! La petición inicial ocupa 0 bytes, o


sea va vacía porque no hay dato alguno que enviar. La respuesta nos devuelve en
este caso el resultado de ejecutar el método en el servidor, codificado en formato
JSON, listo para ser usado desde JavaScript. Su tamaño: 16 bytes. Y eso porque hay
8 bytes extra que se añaden a todas las respuestas debido a la definición del objeto
JSON devuelto. Si el resultado fuera un dato más largo seguirían siendo sólo 8 bytes
más y su importancia relativa sería menor aún.
Es decir, hemos afinado al máximo el trasiego de información entre cliente y
servidor, así como el código que ha de ejecutarse en el servidor, ya que no hay
sobrecarga alguna por la ejecución de la página.
Las ganancias de tamaño y rendimiento son mucho más espectaculares aún en
páginas más grandes (reales) con más controles.
Si vemos el código fuente de la página generada podremos analizar con detalle
el código JavaScript autogenerado por el ScriptManager para conseguir toda esta
funcionalidad. En la figura 5 se puede ver un fragmento del mismo.
ASP.NET AJAX en el navegador  67

Figura 5.- Código autogenerado para habilitar los métodos de página

Es evidente que para ciertos casos esta técnica ofrece grandes ventajas.

2.- Llamadas a servicios Web


La técnica anterior está bien para trabajar dentro del ámbito de una misma página,
pero presenta algunas limitaciones.
La más importante de ellas es que nos dificulta la reutilización del código, ya
que los métodos están atados a una página concreta. Si necesitamos utilizarlo en
más de una página del sitio nos veremos obligados a definir otros tantos métodos
estáticos de página.
Otra limitación es que si usamos Master Pages en nuestro desarrollo y éstas son
las que contienen el ScriptManager tendremos que habilitar la propiedad EnablePa-
geMethods en éste, sea necesario o no para cada una de las páginas. Ello provocará
que se genere código JavaScript para llamar a métodos de página que es innecesario
en muchas de nuestras ASPX, ya que no los utilizarán.
Una evolución mejorada de lo anterior es la capacidad del ScriptManager para
habilitar las llamadas a servicios Web desde JavaScript. Ello nos permite tener en
una única ubicación común todo el código reutilizable que vamos a llamar desde el
navegador, eliminando la primera de las limitaciones anteriores. Además se puede
habilitar selectivamente el acceso a estos servicios desde el control ScriptMana-
gerProxy, por lo que sólo generaremos código JavaScript extra en las páginas que
realmente lo necesiten.
68  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Nota:
Por supuesto sólo podremos hacer llamadas a servicios Web que se encuentren ubicados en
el mismo dominio que nuestras páginas. Esto no es una limitación de ASP.NET AJAX sino
una característica de seguridad de todos los navegadores del mercado. Si no existieran esta
y otras limitaciones similares (como la que restringe el acceso sólo a las cookies de nuestro
mismo dominio), nuestra privacidad y seguridad como usuarios de Internet se verían seria-
mente amenazadas.

2.1.- Definición de un servicio Web ASMX para ser llamado


desde JavaScript
Vamos a aprender a utilizar los servicios web desde JavaScript mediante el desarrollo
de un ejemplo práctico.

Nota:
No voy a explicar aquí los fundamentos de los servicios Web y su creación, ya que es algo que
se sale del ámbito de este capítulo. Se supone que si estás leyendo este libro deberías saberlo
ya. Si no es así te recomiendo mi libro de fundamentos de ASP.NET donde viene explicado.
También podemos usar del mismo modo servicios creados con WCF (Windows Communica-
tion Foundation), algo que haremos en otros ejemplos.

Crearemos un pequeño proyecto que nos permita calcular la longitud de cual-


quier cadena con un método en un servicio Web, llamando a éste desde JavaScript.
Obviamente hacer esto en el servidor es un sinsentido y es sencillísimo hacerlo
directamente con JavaScript, pero lo utilizaremos únicamente para ilustrar los con-
ceptos necesarios para consumir servicios Web desde el lado cliente.
Agrega al proyecto un nuevo servicio Web llamado “Contador.asmx”. Elimina
el método de ejemplo que trae y crea uno nuevo llamado ContarPalabras, cuyo
objetivo será el de devolver el número de palabras contenidas en la cadena que se le
pase como parámetro. El código es extremadamente sencillo pues sólo nos interesa
centrarnos en cómo llamarlo desde JavaScript, pero podría ser todo lo complicado
que quisiésemos, con llamadas a bases de datos o lo que fuera necesario. En la figura
6 se puede ver el resultado.
ASP.NET AJAX en el navegador  69

Figura 6.- El código de nuestro servicio Web de ejemplo

Tal y como está ahora mismo se trata de un servicio Web normal y corriente
que acepta peticiones HTTP y devuelve sus resultados con XML. Por lo tanto sería
factible realizar llamadas al mismo desde JavaScript usando el objeto XMLHttpRe-
quest y procesando en el navegador el XML devuelto para operar con él. De hecho,
como hemos visto en el capítulo de fundamentos, esta es la esencia original de AJAX
(Asynchronous JavaScript And XML).
Sin embargo también hemos visto que existen formas mejores de utilizar este tipo
de recursos remotos. En concreto utilizando JSON (JavaScript Object Notation) para
devolver los resultados. Y además sería fantástico si de alguna manera no tuviésemos
que escribir a mano todo el código necesario para realizar la llamada y procesar los
resultados ya que, como hemos estudiado, es algo engorroso y propenso a ciertos
errores y situaciones problemáticas.
Si te fijas en la parte de arriba del código en la figura anterior verás que está
comentado un atributo de la clase que representa al servicio Web. Este atributo se
llama ScriptService. Su propósito es habilitar la generación automática de código
JavaScript especializado en el manejo del servicio con el que se decore. Este código
autogenerado permitirá realizar las llamadas a los métodos del servicio directamente
desde el navegador sin tener que preocuparnos de los detalles de bajo nivel.
Esta “magia” se consigue gracias a un nuevo manejador para archivos .asmx que
viene definido en ASP.NET AJAX y que sustituye al manejador habitual. Si abrimos
70  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

el web.config de nuestra aplicación y buscamos en el XML veremos que tenemos


unos nodos como los siguientes:
<httpHandlers>
<remove verb=”*” path=”*.asmx”/>
<add verb=”*” path=”*.asmx” validate=”false” type=”System.Web.
Script.Services.ScriptHandlerFactory, System.Web.Extensions,
Version=4.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35”/>
</httpHandlers>

Seguramente tendrá algunas entradas <add> más, pero nos quedamos con esta
que es la que nos interesa. Lo que hace esta sección de la configuración es eliminar
el manejador de servicios Web por defecto (nodo <remove>) y añadir uno nuevo
especializado, ScriptHandlerFactory, que sabe interpretar este nuevo atributo que
estamos estudiando y generar los formatos adecuados. No es necesario que toques
nunca estos elementos. Simplemente quería llamar tu atención sobre ellos para que
sepas cómo funciona esto por debajo.
Para seguir con el ejemplo, descomenta el atributo ScriptService en el código del
servicio para que pueda funcionar en modo JavaScript. Lanza la aplicación y navega
hasta el servicio.
Si escribimos el sufijo /js a continuación del nombre del .asmx, se generará au-
tomáticamente el código JavaScript necesario para llamarlo y podremos descargarlo
a disco para su examen. La figura 7 muestra cómo hacerlo:

Figura 7.- Generación automática de un proxy JavaScript para el servicio.


ASP.NET AJAX en el navegador  71

Este será el JavaScript que usaremos de manera transparente en nuestra página


para llamar al servicio cuando acabemos con el ejemplo. Es útil saber que podemos
obtenerlo así, ya que nos permitirá examinarlo y aprender sobre su modo de trabajar.
De hecho existe la opción de obtener una versión de depuración del mismo, mucho
más apropiada para su análisis ya que contiene comentarios y el código está orde-
nado, si en lugar de /js utilizamos el sufijo /jsdebug.
En la figura siguiente se puede ver un fragmento de los contenidos de este código
que como vemos incluso lleva comentarios XML para generar documentación y
obtener ayuda Intellisense desde el entorno de Visual Studio.

Figura 8.- Código JavaScript de la clase proxy para acceso al servicio

Este código hace uso de clases especializadas de la parte cliente de ASP.NET


AJAX, como por ejemplo Sys.Net.WebServiceProxy, que encapsulan toda la funcio-
nalidad necesaria. Su análisis va mucho más allá del ámbito de este libro pero te
resultará fácil seguirlo si tienes buenos conocimientos de JavaScript.

2.2.- Creación de la página cliente para llamar al servicio


Recapitulemos lo que hemos hecho hasta ahora, que es bien sencillo: hemos creado
un servicio Web y lo hemos decorado con el atributo ScriptService para poder lla-
marlo desde JavaScript directamente.
Ahora sólo nos resta crear una página de ejemplo que haga llamadas a nuestro
flamante servicio y que ilustre la parte cliente del proyecto.
Agrega una nueva página al proyecto, “UsarContador.aspx”. Arrastra a su su-
perficie desde la barra de herramientas un ScriptManager y, desde el grupo de
controles HTML, un control <input> de texto, un <input> de tipo botón y un <div>
para contener el resultado de contar las palabras con el servicio web. Como vemos,
72  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

al igual que antes, no hemos utilizado ningún control de servidor a excepción del
ScriptManager y todo el código que crearemos será de lado cliente.

Figura 9.- Nuestro ejemplo de uso del servicio Web

Lo único que tenemos que hacer para poder utilizar el servicio desde el JavaScript
de nuestra página es registrarlo en el ScriptManager. Para ello utilizaremos una
colección especial de este control llamada Services, y que hasta ahora no habíamos
visto. Su misión es indicar al ScriptManager nuestra intención de utilizar uno o más
servicios Web desde el navegador, consiguiendo que se genere automáticamente el
código necesario para ello al renderizar la página.
Si seleccionamos el control y vamos a sus propiedades veremos que hay un editor
para esta colección, en el que podemos añadir referencias a servicios visualmente.

Figura 10.- El editor visual de referencias de Scripts

Los elementos introducidos en este editor se reflejan como entradas dentro de


un nodo <Scripts> en el código HTML de la página. De hecho, los programadores
ASP.NET AJAX en el navegador  73

experimentados, dado que todo el código que van a escribir es de lado cliente,
normalmente editan directamente este nodo en la vista HTML de la página, sin usar
el diálogo visual anterior. Visual Studio nos ofrece soporte Intellisense para hacerlo
(figura 11), por lo que es incluso más rápido.

Figura 11.- Establecimiento manual de referencias a servicios

Esta colección está disponible también en el control ScriptManagerProxy, por lo


que podemos usarlas en entornos con Master Pages.
A partir del momento en que hemos registrado el servicio, Visual Studio nos
ofrece soporte Intellisense para su uso desde JavaScript (figura 12), obteniendo
ayuda a medida que escribimos sobre sus métodos y propiedades (1) y sobre los
diferentes parámetros de los mismos (2). Esto nos facilita sobremanera el trabajo
con este sistema.

Figura 12.- Soporte Intellisense para el uso de los servicios con JavaScript
74  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

El código necesario para usar el servicio es directo y sencillo. Accedemos al


mismo a partir del nombre de la clase y el nombre de sus métodos, exactamente
igual a cómo haríamos desde código de servidor. Como parámetros se le pasaran los
mismos que el método original en el servicio Web y, a mayores, una referencia a la
función para procesar los resultados y otra a la función para gestionar los posibles
errores. Exactamente igual que hemos visto en para los métodos de pagina.
El último parámetro posible (userContext, como se ve en la figura 12) es opcional
y sirve para pasar información específica de la llamada de forma que podamos
distinguir unas de otras si hacemos varias llamadas seguidas al servicio, o si usamos
los mismo manejadores de éxito y fracaso para varios servicios diferentes.
Nuestro código final es tan sencillo como este:
<script type=”text/javascript” language=”javascript”>
<!--
function LlamarServicioContador() {
Contador.ContarPalabras($get(“txtFrase”).value, finLlamada,
gestorErrores);
}
function finLlamada(resultado) {
$get(“divRes”).innerHTML = resultado;
}
function gestorErrores(excepcion) {
$get(“divRes”).innerHTML = “Error en la llamada: “ +
excepcion.get_message();
}
//-->
</script>

Como vemos prácticamente idéntico al que vimos cuando trabajábamos con


métodos de página.
Ahora ya podemos ejecutar la aplicación. Introduciremos una frase en el campo
de texto y al pulsar el botón veremos como de inmediato y sin refrescar la página
obtenemos el resultado correcto en el div habilitado a tal efecto.

Figura 13.- Nuestro ejemplo en funcionamiento

El usuario no notará en absoluto que se ha ido al servidor a realizar la tarea.


El trasiego de información entre cliente y servidor es mínimo, ya que sólo se
envía y se recibe información codificada como JSON (Figura 14). Además tenemos
toda la funcionalidad concentrada en un solo punto y accesible para toda la aplica-
ASP.NET AJAX en el navegador  75

ción. Al tratarse de un servicio Web podremos sacarle partido a características de


éstos como, por ejemplo, la caché de datos para mejorar el rendimiento.

Figura 14.- Análisis de la petición realizada al servicio web

El uso de servicios Web y servicios WCF diseñados para comunicación desde


JavaScript en el cliente es realmente útil. Los podemos utilizar para intercambiar
entre cliente y servidor todo tipo de datos complejos y no sólo simples cadenas y
números, como hemos visto en los ejemplos. Desde el cliente además se pueden
enviar datos al servidor (por ejemplo un registro de la base de datos para actualizar)
y no sólo limitarnos a recibir información.
De hecho las principales novedades de ASP.NET 4.0 en lo que respecta a AJAX
tienen que ver con llevar cada vez más parte del procesamiento al lado del cliente y
aprovechar la capacidad de llamar a servicios Web para generar las interfaces de datos
puramente en JavaScript. En el siguiente capítulo profundizaremos en todo ello.

3.- Servicios de aplicación: Membership y


Roles desde el navegador
Uno de los servicios más útiles de ASP.NET es el de seguridad, encarnado en las
clases Membership y Roles de System.Web.Security. Gracias a estas clases y el mode-
lo de proveedores subyacente podemos gestionar de manera sencilla y flexible todas
las operaciones de autenticación de nuestras aplicaciones Web.
76  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Con lo que hemos visto hasta ahora sería muy sencillo construir un servicio Web
propio que nos permitiera hacer uso de todas las características de estas dos API
directamente desde el navegador. De esta manera podríamos hacer autenticación de
usuarios con JavaScript, verificar si el usuario actual pertenece o no a un rol y en
función de eso tomar decisiones ya desde el cliente, etc...
Lo cierto es que, sí, sería muy fácil, pero ni siquiera tendremos que hacerlo. ASP.
NET AJAX ofrece “de serie” un completo juego de clases JavaScript especializadas
que nos permiten utilizar las API de seguridad directamente desde el navegador.
En realidad de lo que nos provee es de dos elementos que funcionan en coope-
ración. Por un lado, la primera pieza la tenemos en forma de dos nuevos recursos
especiales en el servidor:
• /Authentication_JSON_AppService.axd
• /Role_JSON_AppService.axd
Cada uno de ellos es un “endpoint” especial en el servidor al que nos podemos
conectar para realizar llamadas con JSON a diferentes funciones que, por debajo,
hacen uso de Membership y Roles. Ambos funcionan como un servicio Web normal
y corriente basado en JSON, como el que nos podríamos haber construido nosotros
mismos con lo visto hasta ahora.
La otra pieza está, por supuesto, en el lado cliente. Se trata de las dos clases
JavaScript complementarias de los dos servicios anteriores que hacen uso de éstos
para proveer de funcionalidades de seguridad al navegador. Estas clases son:
• Sys.Services.AuthenticationService
• Sys.Services.RoleService
Ambas clases disponen ya de los métodos apropiados para manejar los servicios
Web correspondientes, por lo que no tenemos que conocer el funcionamiento de
éstos para poder utilizarlos. De hecho los dos recursos .axd del servidor que he
mencionado no están documentados, lo que es una forma de forzarnos a utilizarlos
únicamente desde el JavaScript de ASP.NET AJAX.

Nota:
Si bien no están documentados ni tienen un WSDL para el servicio, resulta muy sencillo averi-
guar su funcionamiento usando alguna herramienta de intercepción de llamadas como Fiddler.
Con ella podemos ver rápidamente qué métodos se llaman y con qué parámetros por lo que
resultaría fácil sacar partido a estos servicios de seguridad desde aplicaciones escritas en otros
lenguajes o usando framewoks de desarrollo JavaScript que no sean ASP.NET AJAX, como
jQuery u otros. Una posible idea es integrar la autenticación de una aplicación antigua escrita
en ASP 3.0 clásico con la seguridad de ASP.NET. Hay quien lo está utilizando para implemen-
tar la seguridad de sus aplicaciones Silverlight o PHP.
ASP.NET AJAX en el navegador  77

Estos servicios vienen desactivados por defecto por lo que, antes de pasar a la
parte práctica de cómo usarlos, debemos explicar cómo los activamos para que estén
disponibles desde el lado cliente.
La forma de poner en marcha los servicios de Membership y Roles para lado
cliente es desde el archivo de configuración de la aplicación Web, web.config, aña-
diendo los siguientes nodos:
<system.web.extensions>
<scripting>
<webServices>
<authenticationService enabled=”true”/>
<roleService enabled=”true”/>
</webServices>
</scripting>
</system.web.extensions>

Por lo demás la configuración es la misma que haríamos para un uso normal de


estas API, es decir, establecer qué proveedores queremos usar para la autenticación
y los roles y crear los usuarios y roles iniciales pertinentes. Generalmente en una
aplicación pública accesible desde Internet usaremos el proveedor por defecto, que
maneja los datos de usuarios y los roles dentro de una base de datos SQL Server.
En realidad funcionaría con cualquier proveedor que queramos, como por ejemplo
el que valida los datos contra el Directorio Activo y que se entrega también con
ASP.NET.
Por supuesto podemos utilizar las API desde el cliente (con JavaScript y AJAX)
y desde el servidor (llamadas normales a las clases) al mismo tiempo y de manera
indistinta. Si autenticamos desde AJAX estaremos autenticados para código de ser-
vidor, y viceversa.
Para ver el funcionamiento en la práctica de estas clases crearemos un ejemplo.
El código completo puedes verlo en el archivo “Autenticacion.aspx” dentro de la
carpeta “AJAX_ServiciosCliente” en el ZIP con las descargas del libro. En el ejem-
plo incluido existe una base de datos con tres usuarios (Admin, Tipo1 y Total) y
tres roles (Administradores, Usuarios Tipo 1 y Usuarios Tipo 2), y en la página de
ejemplo usaremos JavaScript para autenticar y cerrar la sesión de estos los usuarios,
mostrando información sobre los mismos. Además hay una página protegida decla-
rativamente con su correspondiente web.config dentro de la carpeta “Privado”, a la
que sólo podremos acceder si antes nos hemos autenticado.
Lo que hace el ejemplo es autenticar a los usuarios con AJAX y mostrar en
la misma página información sobre a qué roles pertenecen. Cambia también la
función del botón, de autenticar a cerrar sesión, según si el usuario está o no
autenticado. En la figura 15 se puede ver el aspecto (más bien espartano) de esta
página de ejemplo.
78  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Figura 15.- Usuario autenticado con AJAX mostrando información sobre sus roles.

Nota:
Sería recomendable que descargases el código de ejemplo y lo tuvieses delante cuando leas
lo que viene a continuación para ver in situ el código descrito y comprenderlo mejor. De otro
modo te resultará mucho más difícil seguirlo.

En la página tenemos un par de controles de texto HTML para introducir el


usuario y la clave de acceso, así como un botón que hará la llamada al servicio, para
intentar autenticar al usuario o para cerrar la sesión si ya está autenticado. Cuando
se pulsa este botón tiene lugar las siguientes instrucciones:
function LogInLogOut() {
if (Sys.Services.AuthenticationService.get_isLoggedIn())
Sys.Services.AuthenticationService.logout(null,
HazLogout, HuboError, null);
Else
{
Sys.Services.AuthenticationService.
login($get(“txtLogin”).value,
$get(“txtClave”).value, false, null, null, HazLogin,
HuboError, null);
}
}

Lo primero que se hace es utilizar la propiedad isLoggedIn de la clase Authen-


ticationService para comprobar si hay algún usuario actualmente autenticado en el
sistema. Devuelve un valor booleano para informar de ello. En caso de que lo haya,
procedemos a cerrar su sesión con una llamada al método logout de la clase. Si no
lo está, llamamos por el contrario al método login para efectuar la autenticación
ASP.NET AJAX en el navegador  79

validando las credenciales. En este caso, por simplificar, no hemos comprobado si


los campos están vacíos o no antes de hacer la llamada.
Para efectuar la autenticación y registro en el sistema del usuario el método login
necesita varios parámetros aparte del nombre de usuario y la contraseña. En concreto
la lista de parámetros que debemos pasarle es la siguiente:
• userName: el nombre de usuario a validar.
• password: la clave correspondiente.
• isPersistent: booleano para indicar si la cookie de seguridad generada será
persistente o no. Es equivalente a marcar la casilla de “recordar al usuario”
en el control Login. Hará que la validación perdure aún después de haber
cerrado el navegador y mientras no se llame al método de cerrar la sesión o
pase el tiempo de caducidad estipulado.
• customInfo: este parámetro es de uso reservado y debe pasarse siempre
un null.
• redirectUrl: Dirección URL a la que redirige el navegador cuando la auten-
ticación se realiza correctamente. Si le pasamos un null, no hay redirección
y la devolución de la llamada se gestiona en la página actual, que es el caso
que veremos en el ejemplo.
• loginCompletedCallback: función a la que se llama automáticamente cuan-
do el inicio de sesión termina. En ella determinaremos si la autenticación ha
tenido éxito o no y realizaremos las acciones pertinentes en cada caso.
• failedCallback: referencia a la función JavaScript que se llamará cuando la
autenticación falle o se produzca un error.
• userContext: datos de contexto de la llamada. Generalmente se le pasará
un null.
Lo más interesante es ver cómo son las funciones que gestionan la devolución de
la llamada. Dado que las llamadas AJAX a los servicios son asíncronas, el código
JavaScript se sigue ejecutando y se notifica de manera automática su finalización
llamando a la función de éxito o de fracaso según proceda.
La función para gestionar los errores recibe como parámetros una referencia a
la excepción, el contexto de la llamada (parámetro userContext, que normalmente
será nulo) y el método que se ha llamado para llegar aquí (para poder reutilizar la
función con varios métodos y así distinguir de cuál viene):
function HuboError(excepcion, contexto, metodo) {
MostrarInfo(“se ha producido un error:<br/>” + excepcion.
get_message());
}

MostrarInfo es una función auxiliar que he definido y que muestra por pantalla
la cadena HTML que le indiquemos.
80  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

La función de resultado de autenticación tiene un delegado similar, pero en lugar


de la excepción recibe como primer parámetro un valor booleano para indicarnos
si la autenticación ha tenido éxito o no. Así, en nuestro caso el código es de la
siguiente manera:
function HazLogin(credencialesValidas, contexto, metodo) {
if (credencialesValidas) {
nomUsuario = $get(“txtLogin”).value;
Sys.Services.RoleService.
load(MostrarInfoUsuarioActual, HuboError, null);
//Vacío los campos
$get(“txtLogin”).value = “”;
$get(“txtClave”).value = “”;
$get(“cmdLogin”).value = “Cerrar sesión”;
}
else {
MostrarInfo(“Credenciales no válidas”);
}
}

Si en el primer parámetro se pasa un true, es que las credenciales eran válidas y


el usuario está dentro del sistema. Es lo que controlamos en el condicional. En ese
caso anotamos el nombre de usuario utilizado en una variable para poder guardarlo y
provocamos la carga de la información sobre los roles a los que pertenece el usuario
recién autenticado (enseguida vamos sobre eso).

Nota:
desde mi punto de vista es una pena que el servicio no disponga de un par de métodos para ob-
tener el nombre y el identificador único del usuario actual. Ello ayudaría mucho a relacionarlo
con otras entidades de la aplicación directamente desde el cliente. No existe, por tanto, forma
alguna de averiguar el nombre del usuario actual una vez este se ha autenticado. Si lo necesi-
tamos deberemos habilitar por nuestra cuenta alguna forma de obtenerlo desde el servidor. No
obstante como la autenticación se hace tanto en cliente como en servidor podemos usar todos los
controles habituales —como el LoginName, LoginStatus, LoginView y similares— para mostrar
esta información en otras páginas.

En el código anterior la única línea relevante es la que efectúa la carga de in-


formación sobre los roles de usuario. Hace una llamada al método load de la clase
RoleService, el cual toma como parámetros la función a llamar al terminar la carga
asíncrona de esta información (MostrarInfoUsuarioActual en este ejemplo), la que se
llamará cuando haya un error (reutilizamos la que ya usamos para el método login),
y un nulo para los datos de contexto.
Este método load llama de manera asíncrona al servicio de manejo de roles en
el servidor y al devolverse la llamada ejecuta la función que le indiquemos, en este
caso MostrarInfoUsuarioActual. A esta función le pasa un array de cadenas de texto
ASP.NET AJAX en el navegador  81

que contiene la lista de roles a los que pertenece el usuario actual. En este caso nos
limitamos a mostrarlos por pantalla recorriéndolos en un bucle:
function MostrarInfoUsuarioActual(roles) {
var s;
s = “Usuario actual: “ + nomUsuario + “<br/>”;

roles.sort();
for(var i = 0; i < roles.length; i++)
{
s += “Rol: “ + roles[i] + “<br/>”;
}
MostrarInfo(s);
}

Como ya he dicho, si el usuario está autenticado lo está tanto para el código de


cliente como para el de servidor. Por ello la mayor parte de las verificaciones de
seguridad se harán en el servidor. La clase AuthenticationService no ofrece método
alguno para verificar la autorización de usuarios (por ejemplo para comprobar si
tiene acceso a un determinado archivo), teniendo que hacerlo de la manera habitual
en el servidor. La única función disponible para comprobar autorizaciones es la
propiedad isUserInRole de la clase RoleService, a la que podemos llamar de la
siguiente manera:
Sys.Services.RoleService.get_isUserInRole(“Administrador”)

obteniendo verdadero o falso en función de la pertenencia del usuario actual al grupo


que se le pase en el parámetro.
En definitiva, los servicios automáticos de Autenticación y Roles de ASP.NET
AJAX nos facilitan el acceso desde el propio navegador al trabajo más común de
seguridad. Son más eficientes, en cuanto al uso de ancho de banda y carga del
servidor, que utilizar dentro de un UpdatePanel los propios controles de seguridad
que nos ofrece ASP.NET. No obstante tienen sus limitaciones y generalmente los
usaremos sólo para hacer la autenticación y cierre de sesión, usando el resto de
características desde el lado servidor. Esto no supone una gran limitación ya que el
objeto de este tipo de funcionalidades de lado cliente es el de reducir el trasiego de
datos entre cliente y servidor, y las funciones de seguridad y control de acceso, una
vez hecha la autenticación, no suelen afectar en absoluto al tráfico intercambiado,
así que suelen ser apropiadas.

4.- Referencias a Scripts en páginas y


controles
La principal función del ScriptManager es, tal y como se puede deducir de su
nombre, la de registrar en el cliente diferentes bibliotecas de funciones JavaScript
82  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

que dotan de la funcionalidad necesaria para hacer uso de las características de ASP.
NET AJAX.
Tradicionalmente en ASP.NET ha existido para este propósito una clase especiali-
zada llamada ClientScriptManager que permite registrar scripts de manera única en
una página, de forma que nos aseguremos de que no se registran dos veces y que se
genera el código HTML apropiado. Esta clase dispone de los siguientes métodos:
• RegisterClientScriptBlock: permite enviar, desde código C# o VB de ser-
vidor, un bloque de JavaScript al cliente, otorgándole un nombre único, de
forma que cuando se intente enviar más de una vez no se permita su dupli-
cación. Genera un bloque de tipo <script></script>.
• RegisterClientScriptInclude: registra en el cliente una referencia a un script
contenido en un archivo externo, normalmente con extensión .js. Genera una
etiqueta de tipo <script src=...”/>.
• RegisterClientScriptResource: registra en el cliente una referencia a un
script guardado como recurso en un ensamblado, de forma que no haya que
distribuir el código en un archivo independiente. Genera una etiqueta de tipo
<script src=”Webresource.axd?...” />.
El problema de esta clase tradicional accesible directamente desde las páginas, es
que no funciona cuando participa en un repintado parcial de página.
Si por ejemplo tenemos un control que, dependiendo de los datos que maneja,
genera JavaScript usando los métodos anteriores en cada petición, al colocarlo en
una página tradicional recibiremos nuevo JavaScript en cada postback. Como cada
repintado de la página es completo y por lo tanto el HTML se vuelve a procesar
entero, estos nuevos Scripts se interpretan en cada ocasión y todo funciona perfec-
tamente. Si decidimos introducir un UpdatePanel para hacer repintados parciales de
la página, de repente, todo ese código JavaScript dejará de funcionar. El motivo es
que al repintar sólo una parte de la página, aunque llegase el nuevo JavaScript al
navegador, éste no se interpreta y no tiene efecto alguno.
El resultado de esto: muchos controles de terceros que funcionan perfectamente
en páginas tradicionales dejan de hacerlo al usar ASP.NET AJAX.
Para solucionarlo la clase ScriptManager ofrece exactamente los mismos tres
métodos que acabamos de ver pero con la salvedad de que, por el mero hecho
de usarlos desde ésta, ya funcionarán sin problema dentro de un UpdatePanel. Se
trata de métodos estáticos de la clase, por lo que no tenemos que tener siquiera un
ScriptManager en la página para poder utilizarlos.
Por lo tanto, en lugar de utilizar este código para registrar uno de nuestros
Scripts:
Page.ClientScript.RegisterClientScriptInclude(“Utilidades”,
“~/Scripts/Utils.js”)

deberíamos usar mejor este otro:


ASP.NET AJAX en el navegador  83

ScriptManager.RegisterClientScriptInclude(Page, GetType(Page),
“Utilidades”, “~/Scripts/Utils.js”)

En este caso se debe especificar una referencia a la página o control que registra
el script así como a su tipo (los dos primeros parámetros).
Para facilitar más aún el registro de scripts compatible con repintados parciales,
el control ScriptManager ofrece una colección Scripts en la que podemos incluir las
referencias de manera directa, sin escribir código en el evento Load de la página.
Esta colección funciona de modo similar al de la colección Services que hemos visto
en el apartado anterior, y tiene su propio diálogo visual (figura 16) así como un nodo
(<ScriptReferences>) en el código HTML de la página.
Por ejemplo:
<asp:ScriptManager ID=”ScriptManager1” runat=”server”>
<Scripts>
<asp:ScriptReference Path=”~/Scripts/Utils.js” />
<asp:ScriptReference Assembly=”Recursos” Name=”Recursos.
Scripts.Utils.js” />
</Scripts>
</asp:ScriptManager>

En este caso estamos registrando una referencia a un script albergado en un


archivo .js externo (primer ScriptReference), y otra a uno embebido como recurso en
un ensamblado de forma que no tengamos que distribuirlo como archivo suelto.

Figura 16.- Diálogo de referencia de Script

Estas colecciones están disponibles también en el control ScriptManagerProxy,


por lo que podemos usarlas en entornos con Master Pages.
84  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Nota:
Hasta la versión 3.5 de la plataforma (incluida) la clase ScriptReference heredaba directamente
de la clase Object. Al aparecer el Service Pack 1 de .NET 3.5 hubo un cambio interno en el
código que hace que la clase ahora herede de System.Web.UI.ScriptReferenceBase. El efecto
negativo de esto es que si en tu equipo de desarrollo tienes Visual Studio 2008 SP1 y despliegas
la aplicación, pre-compilándola antes, a un servidor que tiene .NET 3.5 sin el Service Pack
aplicado, obtendrás errores al intentar cargar las páginas con referencias a Scripts. En Visual
Studio 2010 con.NET 4.0 aunque esto es así también no tendrás problemas porque ya deberás
tener .NET 4.0 en el servidor para funcionar, aunque puedes compilar para la versión 3.5 de la
plataforma, en cuyo caso lo anterior aplica también.

5.- Optimización de uso de bibliotecas en


ASP.NET 4.0
Aunque una página esté vacía sólo por el hecho de colocar un, aparentemente inocuo,
control ScriptManager en ésta, obtenemos en el lado cliente un buen puñado de
código de Script, como se puede observar en la figura 17.

Figura 17.- Scripts generados por defecto al incluir un ScriptManager


ASP.NET AJAX en el navegador  85

La funcionalidad de cliente de ASP.NET AJAX está en realidad separada en diversos


apartados, por ejemplo, extensiones globales de JavaScript, utilidades de formularios,
operaciones de comunicaciones, globalización, seriación de objetos (JSON), etc...
Si nuestra aplicación no requiere alguna de estas partes ¿por qué tenemos que
descargarlas por completo en todas las ocasiones?. Lo ideal sería poder descargar
selectivamente aquella funcionalidad que nos interese en cada caso.
Hasta la llegada de ASP.NET 4.0 no había posibilidad de conseguir esto. Un
ScriptManager en la página implicaba que teníamos toda la funcionalidad dispo-
nible en el cliente, la necesitásemos o no. A partir de .NET 4.0 ya no es necesario
descargar los scripts completos aunque por defecto lo sigue haciendo para asegurar
la compatibilidad con aplicaciones anteriores.
El comportamiento se establece a través de la nueva propiedad Microsoft-
AjaxMode, que puede tomar los siguientes valores:
• Enabled: se envían todos los scripts. Es el comportamiento por defecto para
ser compatible con lo anterior.
• Explicit: cada script que necesitemos se debe añadir explícitamente con una
sección <ScriptReference>.
• Disabled: todos los scripts propios de AJAX están deshabilitados.
Normalmente sólo usaremos el segundo valor para indicar manualmente qué
partes de AJAX queremos usar.
Así, por ejemplo, si sólo queremos usar las extensiones básicas para JavaScript
que ASP.NET AJAX nos ofrece, podemos escribir esto:
<asp:ScriptManager ID=”ScriptManager1” runat=”server”
EnablePartialRendering=”False”
MicrosoftAjaxMode=”Explicit”>
<Scripts>
<asp:ScriptReference Name=”MicrosoftAjaxCore.js” />
</Scripts>
</asp:ScriptManager>

Incluyendo exclusivamente el script correspondiente al núcleo de funcionalidad.


Fíjate en que también he marcado EnablePartialRendering como False para evitar
los scripts que generan la funcionalidad de los repintados parciales.
Los módulos de funcionalidad que nos ofrece ASP.NET AJAX para el lado
cliente son los siguientes:
• MicrosoftAjaxCore.js: extensiones base para el lenguaje JavaScript y el
DOM.
• MicrosoftAjaxComponentModel.js: soporte de controles JavaScript y
behaviours.
• MicrosoftAjaxSerialization.js: seriación y de-seriación de datos.
• MicrosoftAjaxGlobalization.js: globalización y localización de scripts.
86  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

• MicrosoftAjaxHistory.js: soporte para manipular el histórico de navegación


(nuevo en ASP.NET 3.5 SP1).
• MicrosoftAjaxNetwork.js: operaciones de comunicación a través de la Red.
• MicrosoftAjaxWebForms.js: la funcionalidad para repintado parcial necesa-
ria para que funcionen los UpdatePanels.
• MicrosoftAjaxWebServices.js: soporte para manejo de servicios Web desde
el navegador.
• MicrosoftAjaxApplicationServices.js: servicios de autenticación, roles y
perfiles.
• MicrosoftAjaxTemplates.js: plantillas de datos de lado cliente (nuevo en
ASP.NET 4.0).
• MicrosoftAjaxAdoNet.js: soporte para ADO.NET Services (nuevo en ASP.
NET 4.0).
Aun pudiendo elegir cuáles queremos incluir en la página no podemos hacerlo
de cualquier manera, puesto que algunos dependen de otros para hacer su trabajo.
Por tanto deberemos ser cuidadosos y tener en consideración las subordinaciones
existentes entre ellos a la hora de decidir cuáles incluimos.
En la figura 18 se muestran las distintas relaciones existentes entre los módulos.

Figura 18.- Relaciones de dependencia entre módulos de AJAX.


ASP.NET AJAX en el navegador  87

Nota:
Los módulos Templates y AdoNet tienen un tono ligeramente distinto para indicar que son
soportados sólo en ASP.NET 4.0 (los demás son de ASP.NET 3.5). El módulo de Templates,
que veremos en el próximo capítulo, depende del módulo WebServices únicamente cuando
se quiere utilizar el control DataContext para enlazado a datos con JavaScript (aparecido en
4.0). Del mismo modo este módulo sólo dependerá de AdoNet si se usa el control AdoNetDa-
taContext. El módulo WebForms se ha puesto en un tono diferente porque para incluirlo basta
con establecer la propiedad EnablePartialRendering del ScriptManager como True, que es
su valor por defecto.

Como ya he dicho, el orden de inclusión es importante, y debemos asegurarnos


de que los módulos de los que dependen otros módulos se colocan en primer lugar.
Cuanto más arriba o más a la izquierda en la figura anterior, antes deben ponerse
en la lista de referencias.
También existe la posibilidad de descargarse de www.codeplex.com/aspnet, en su
apartado para AJAX, el código fuente de todos estos scripts e incluirlos directamente
como referencias <script> de HTML normales.

5.1.- Combinación de scripts


Otra cuestión importante a la hora de optimizar la descarga de Scripts es tener en
cuenta el número de referencias a scripts que incluye la página, ya que ¿qué es más
eficiente: descargar 100 Kb en un sólo archivo, o el mismo tamaño de información
en 10 archivos diferentes? Obviamente lo primero porque nos estamos ahorrando 9
viajes de ida y vuelta al servidor.
Como hemos visto en la figura 16, cuando utilizamos el ScriptManager así como
otros controles basados en AJAX, como los del Control Toolkit, éstos generan una
serie de referencias de tipo <script> en la página. Cada una de ellas supone una
petición al servidor por cada referencia, por lo que al final la carga de la página se
traduce en multitud de llamadas al servidor.
Para tener en cuenta esta realidad y permitirnos optimizar la descarga de código
JavaScript al navegador el ScriptManager, a partir de ASP.NET 3.5 SP1, ofrece una
característica de combinación de scripts muy útil. Lo único que tenemos que hacer
es rodear nuestro nodo <Scripts> en el ScriptManager con un nuevo nodo padre
llamado <CompositeScript>, así por ejemplo:
<asp:ScriptManager ID=”ScriptManager1” EnablePartialRendering=”false”
MicrosoftAjaxMode=”Explicit” EnableHistory=”true” runat=”server”>
<CompositeScript>
<Scripts>
<asp:ScriptReference Name=”MicrosoftAjaxCore.js” />
<asp:ScriptReference Name=”MicrosoftAjaxComponentModel.js” />
<asp:ScriptReference Name=”MicrosoftAjaxSerialization.js” />
<asp:ScriptReference Name=”MicrosoftAjaxHistory.js” />
88  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

</Scripts>
</CompositeScript>
</asp:ScriptManager>

Al hacer esto todos los archivos incluidos en la colección de scripts se combi-


nan de manera automática en uno solo, de modo que ello se traduce en una única
llamada al servidor para descarga de Scripts. El tamaño sigue siendo el mismo pero
al reducirse mucho el número de viajes hemos optimizado bastante el tiempo de
respuesta de la página.
En una página normal que use AJAX hay muchos más Scripts de los que pueda
parecer a simple vista. En la página de Facebook de campusMVP (http://go.krasis.
com/Facebook) podrás encontrar un vídeo práctico en el que el autor de este libro
explica paso a paso cómo averiguar exactamente qué Scripts están siendo usados en
una página, y cómo podemos posteriormente generar un script único como combi-
nación de todos ellos para optimizar su descarga. El acceso directo al vídeo tiene
esta URL:
http://www.facebook.com/video/video.php?v=101426592913
Si usamos la capacidad de indicar manualmente qué módulos de Script queremos
utilizar es que nos preocupa el rendimiento, así que deberíamos usar esta caracte-
rística de combinación de Scripts también. Tenla en cuenta para tus páginas más
importantes.

6.- En resumen
En este capítulo hemos estudiado las cuestiones más importantes para el trabajo con
AJAX en el lado del cliente. Al no ser este un libro de JavaScript, no hemos entrado
en los detalles relacionados con la programación pura y dura en este lenguaje, que
ASP.NET AJAX mejora mucho. Por el mismo motivo tampoco hemos visto algunas
otras cuestiones relacionadas, como el uso de recursos JavaScript localizados o la
creación de behaviours de cliente.
El objetivo ha sido mostrar los fundamentos que nos van a ayudar a conseguir una
visión más “pura” del desarrollo con AJAX, en el que el navegador cobra una gran
importancia, responsabilizándose de la mayor parte de la generación de la interfaz.
Esta visión es en la que se basan las características AJAX introducidas en ASP.NET
4.0 y que vamos a estudiar en el próximo capítulo.
capítulo
5
Enlazado a datos
en el navegador

El modelo tradicional de trabajo de las tecnologías Web (ASP, PHP, ASP.NET...) ha


sido siempre el de generación de HTML en el servidor.
Vamos a repasar el funcionamiento normal de una página ASPX que muestra
datos enlazados desde algún origen (como una base de datos):
1. El usuario solicita la página al servidor.
2. Se obtienen los datos necesarios, se enlazan y se genera el HTML final de
la página a partir de los diferentes controles Web que tengamos en la misma
(renderizado del HTML).
3. El HTML se recibe en el navegador que se encarga de visualizarlo. Cuando el
usuario efectúa cualquier acción que cambie el estado de la página (una orde-
nación, selección, borrado...), se envía el formulario al servidor (postback) y
una vez allí se genera de nuevo todo el HTML para devolver al navegador.
Esto es así independientemente de que la página utilice AJAX con controles
UpdatePanel o no.
El principal problema de este modelo (tampoco exento de ventajas) es la cantidad
de datos que hay que mover constantemente entre el cliente y el servidor: el Views-
tate de la página y el propio HTML generado. Ello provoca lentitud en la respuesta,
carga en el servidor y multitud de “viajes” entre el cliente y servidor que muchas
veces serían innecesarios.
Un modelo alternativo sería el de traspasar más responsabilidades al navegador.
En esta visión del desarrollo Web el servidor se encargaría de devolver únicamente
dos cosas: las páginas HTML estáticas (es decir, no generadas dinámicamente) que
solicite el usuario, y los datos que deban manejar éstas. El resto de la acción se
desarrollaría en el navegador. Éste generaría dinámicamente la interfaz de usuario,
gestionaría los eventos en local (en contraste con los eventos de servidor) y pediría
o enviaría datos al servidor según la necesidad.
Para entenderlo mejor comparemos una situación común como es la de mostrar
los datos de una base de datos en una tabla.
En el modelo tradicional, como hemos visto, obtendríamos los datos en el servidor
y los enlazaríamos a un control de servidor (por ej. un GridView), el cual generaría
la tabla y enviaría el HTML final al navegador.
89
90  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

En el modelo alternativo propuesto, la página prácticamente vacía llegaría al


navegador. Éste solicitaría los datos a visualizar al servidor (mediante un servicio
Web o similar) y se encargaría de generar la tabla correspondiente a partir de ellos,
creando dinámicamente con JavaScript los elementos necesarios.
Si se edita un registro de la tabla, en el primer caso se enviaría de nuevo la
página al servidor, que actualizaría los datos y generaría de nuevo la página. En el
segundo modelo el navegador enviaría únicamente los datos a actualizar al servidor,
sin necesidad de hacer nada más porque el resto de la página permanece inalterada.
Incluso podríamos acumular varios cambios en los datos y luego enviarlos todos
juntos para su actualización en bloque, ahorrando viajes al servidor.
La figura 1 muestra la comparación entre ambas concepciones del desarrollo
Web.

Figura 1.- Modelo tradicional vs modelo “puro” de aplicaciones Web AJAX

Este modelo alternativo de AJAX sería más “puro”, en el sentido de que sólo se
intercambian datos y no información de la interfaz. Acerca todavía más el mundo
de las aplicaciones web al de las de escritorio, ya que en ambos casos la parte
importante de la interacción con el usuario transcurre en el equipo de éste.
Al usar esta técnica de trabajo es evidente que se obtienen muchas ventajas,
sobre todo en lo que respecta al rendimiento del servidor, capacidad de respuesta
de la interfaz y disminución radical de la cantidad de información que se trasiega
por la Red.
Las tecnologías que habilitan esta visión del desarrollo AJAX “puro” son dos:
los servicios Web y las plantillas de lado cliente. En el capítulo anterior ya hemos
podido comprobar lo sencillo que resulta emplear desde JavaScript los servicios Web
habilitados a tal efecto con ASP.NET AJAX. Las plantillas de lado cliente son una
Enlazado a datos en el navegador  91

innovación aparecida con ASP.NET 4.0 que nos facilitan la gestión de interfaces
complejas enlazadas a datos, directamente en el navegador.

Nota:
Este apartado es tal vez el más complicado de todo el libro, por lo que se recomienda seguirlo
con calma y apoyándose en el código de ejemplo. Al trabajar con JavaScript en el navegador,
conseguir que todo funcione correctamente a la primera suele costar un poco, pero los resulta-
dos son muy interesantes. No desesperes si al principio algunas cosas no te funcionan, es muy
habitual en este caso.

1.- Concepto de plantillas de lado cliente


Por regla general cuando tenemos que mostrarle al usuario datos homogéneos,
utilizamos algún tipo de patrón de visualización que se repite de manera idéntica
para cada elemento, particularizando sólo los valores de éstos. Así, por ejemplo, si
tenemos que mostrar una lista de productos por pantalla podemos utilizar una tabla
similar a esta:
<table cellpadding=”5” cellspacing=”0” border=”1”>
<thead>
<tr>
<th>Nombre</th>
<th>Precio</th>
....
</tr>
</thead>
<tbody>
<tr>
<td>Producto 1</td>
<td> 10 €</td>
.....
</tr>
<tr>
<td>Producto 2</td>
<td> 20 €</td>
.....
</tr>
......
</tbody>
</table>

Tenemos la cabecera de la tabla con los títulos de las columnas y lo que hay en
el cuerpo es una fila por cada uno de los productos que visualiza la información
de cada uno de ellos. Del mismo modo podríamos haber elegido cualquier otra
92  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

distribución para los elementos: con listas desordenadas <ul><li> o con elementos
<span>, etc... La cuestión aquí es que claramente hay un patrón que se repite a la
hora de mostrar los datos. Este patrón que podemos identificar fácilmente es lo que
va a constituir nuestra plantilla. En controles de servidor como el Repeater o el
ListView trabajamos de una forma similar, pero el HTML se genera en el servidor
para enviarlo al cliente.
Imaginemos ahora que, tras haber identificado la parte común a los datos, pudié-
semos escribir lo anterior de una forma parecida a la siguiente:

<table cellpadding=”5” cellspacing=”0” border=”1”>


<thead>
<tr>
<th>Nombre</th>
<th>Precio</th>
....
</tr>
</thead>
<tbody id=”plantilla” class=”sys-template”>
<tr>
<td>{{ ProductName }}</td>
<td>{{ UnitPrice }}</td>
.....
</tr>
</tbody>
</table>

Fíjate que lo que hemos hecho es marcar una zona de nuestro HTML (lo que
está dentro de la etiqueta <tbody>) como una zona genérica, indicando en ella los
lugares donde irán los datos. Ahora será el propio navegador el que identifique esta
zona y la utilice como un patrón a repetir para generar la tabla que necesitamos de
manera automática a partir de los datos a visualizar, traídos desde el servidor. ¿No
es realmente cómodo?
Esta capacidad de autogenerar la interfaz a partir de plantillas nos brinda un gran
control sobre el HTML generado y sobre todo nos desata del servidor a la hora de
generar las interfaces de usuario.
La definición de las plantillas en ASP.NET AJAX 4.0 es tan sencilla como lo que
acabamos de ver, pues basta con identificar el elemento HTML que las contendrá, y
marcar dentro del código interior los lugares en los que irán los campos. Hacer uso
de ellas tiene algo más de dificultad, sobre todo porque disponen de capacidades
avanzadas como el enlazado bidireccional, refresco automático, actualización de
datos en el servidor, etc.
Para aprender utilizarlas en la práctica vamos a crear un ejemplo y analizaremos
el código poco a poco.
Enlazado a datos en el navegador  93

2.- Las bases para trabajar


Para el ejemplo vamos a partir de la base de datos “Northwind” que hemos usado
a lo largo del libro y vamos a crear una página que muestre información sobre los
productos que vende esta empresa ficticia.
Lo primero que haremos es crear un servicio Web que facilite información so-
bre los productos. Nuestro servicio, de momento, tendrá un único método llamado
GetAllProducts, que devolverá una colección enumerable de objetos Product con
la información de todos los productos en la base de datos. El servicio se llama
“NorthwindService.svc” y lo tenemos dentro de la carpeta “Servicios”.

Nota:
En el ejemplo descargable desde la Web el servicio se ha implementado con Windows Com-
munication Foundation, y la capa de datos con Linq2SQL. Tú puedes hacerlo como mejor te
parezca, por ejemplo con ADO.NET “clásico” y un servicio Web ASMX de ASP.NET, como
se ha visto en el capítulo anterior. En cualquier caso el servicio debe ser compatible con AJAX
y debe devolver y aceptar objetos serializados en formato JSON. No nos pararemos en la im-
plementación del servicio ya que lo que nos interesa es centrarnos en la funcionalidad AJAX.
Consulta el código fuente en la descarga para ver los detalles.

Añadiremos una página al proyecto que contendrá únicamente un control Script-


Manager. Como sabemos éste se encargará de generar todo el JavaScript necesario
para sustentar la funcionalidad de ASP.NET AJAX. Hay que tener en cuenta una
cosa, y es que el control no envía al cliente el código necesario para que funcionen
las plantillas a menos que se lo indiquemos, para lo cual habrá que usar la referencia
de script correspondiente:
<asp:ScriptManager ID=”ScriptManager1” runat=”server”>
<Scripts>
<asp:ScriptReference Name=”MicrosoftAjaxTemplates.js” />
</Scripts>
</asp:ScriptManager>

Nota:
Es importante señalar que como toda la funcionalidad que vamos a crear es exclusivamente en
el navegador, en realidad no nos hace falta siquiera este control. Lo usamos por comodidad en
Visual Studio 2010. Basta con tener los scripts de ASP.NET AJAX en archivos .js externos (des-
cargables desde http://www.codeplex.com/aspnet, en el apartado AJAX) y añadir referencias
a los mismos en la página. Por ello todo lo explicado podría usarse con cualquier otra tecnología
como PHP o incluso con páginas HTML puras y duras, sin código de servidor. Las referencias
necesarias son las siguientes:
<script type=”text/javascript” src=”Scripts/MicrosoftAjax.js”>
</script>
<script type=”text/javascript” src=”Scripts/
MicrosoftAjaxTemplates.js”></script>
94  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

3.- Definición de la plantilla de productos


Ahora que disponemos de la infraestructura necesaria (servicio + JavaScript) vamos
a definir cómo queremos visualizar nuestra información. Ello requiere que escriba-
mos el HTML de la plantilla a mano, en el código de la página. El editor de Visual
Studio nos ayudará con la sintaxis. En este caso usaremos una tabla tal y como
vimos hace un momento en el apartado 1.
Debemos marcar el elemento contenedor de nuestra plantilla para que luego
ASP.NET AJAX sepa qué partes del HTML se deben repetir. La forma de hacerlo
es ponerle un identificador cualquiera, y asignarle una clase CSS especial llamada
“sys-template”, así en nuestro caso:

<tbody id=”Productos-template” class=”sys-template”>

Es importante que el nombre de la clase CSS sea exactamente ese, “sys-template”,


ya que es la forma que tiene AJAX de identificar a este tipo de elementos. Debemos
asegurarnos de que nuestra página, internamente o en un archivo .css externo, define
la clase de esta forma:

.sys-template { display:none; }

Con ello conseguimos que la plantilla no se vea en la página aunque falle la


generación dinámica de la tabla. Si necesitamos utilizar otro estilo para el elemento
(el estilo que normalmente tendría aplicado), podemos ponerlo también simplemente
separándolo con un espacio, y se conservará tras el procesamiento:

<tbody id=”Productos-template” class=”miEstilo sys-template”>

Dentro de este elemento meteremos todo el HTML que queramos que se renderi-
ce para mostrar los datos, marcando la posición de éstos con una doble llave con el
nombre del campo o propiedad del objeto que queremos visualizar. Así, en nuestro
ejemplo que devolvemos información de un producto sacado de la base de datos, la
plantilla podría quedar así:

<tbody id=”Productos-template” class=”sys-template”>


<tr>
<td>{{ ProductName }}</td>
<td>{{ UnitPrice }}</td>
</tr>
</tbody>
Enlazado a datos en el navegador  95

4.- La clase DataView


En estos momentos ya hemos decidido cómo vamos a visualizar la información, pero
para que sirva de algo tenemos que indicar a AJAX cómo queremos utilizarla. La
clase Sys.UI.DataView definida en la infraestructura de cliente de ASP.NET AJAX
es la clave para activar las plantillas.
Un DataView es un control de lado de cliente que nos permite relacionar un ori-
gen de datos y una plantilla para generar, de manera automática, el HTML necesario
para visualizar la información.
Los datos manejados por esta clase son muy variados: pueden ser matrices de
objetos creadas en la propia página, datos resultantes de llamar a un servicio Web
en el servidor o clases especializadas que ofrece AJAX para conectarse automática-
mente a estos servicios Web (y que luego estudiaremos).
Cuando se le asigna una matriz o colección de objetos, el DataView actúa como
un repetidor de la plantilla, generando una instancia por cada elemento de la matriz.
Se asemejaría a un control ListView o un Repeater de ASP.NET pero en el lado
cliente. Si por el contrario se le asigna un único objeto la plantilla sólo se instancia
una vez y el control actúa como un visualizador de entidades (se parecería a un
control de servidor DetailsView).
Existen tres maneras fundamentales de asociar datos a este control:
1. Asociarle directamente una matriz de objetos o un objeto en su propiedad
data. Si, por ejemplo, tenemos una matriz de objetos ya definida en la propia
página pues no se obtiene dinámicamente de un servicio externo. Es la forma
menos habitual.
2. Indicarle la dirección relativa de un servicio Web que proporciona los datos,
para lo cual la propiedad adecuada es dataProvider. Además en este caso es
necesario indicarle mediante otra propiedad llamada fetchMethod el nombre
de la operación del servicio que debe llamar para recuperar los datos.
3. Utilizar un objeto especializado para comunicarse con el servidor y usar éste
como proveedor de datos para el DataView. Se asigna el objeto especializado
directamente a la propiedad dataProvider. Estudiaremos estos objetos en un
apartado posterior.
Existen dos maneras de instanciar el control DataView y asignarle los datos y la
plantilla: por código y de manera declarativa.
Para instanciarlo mediante código lo más sencillo es utilizar el método especial de
ASP.NET AJAX llamado $create. El mejor sitio suele ser el evento init de la clase
Application, y que se ejecuta al cargar la página. La sintaxis sería la siguiente:
Sys.Application.add_init(function()
{
$create(
Sys.UI.DataView,
96  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

{
data: “Servicios/NorthwindService.svc”,
fetchOperation: “GetAllProducts”
},
null,
null,
$get(“Productos-template”)
);
});

La creación toma como parámetros el tipo de clase a crear, las propiedades del
objeto que queramos establecer durante la creación, los manejadores de eventos que
queramos asignar (en este caso ninguno, por eso se pasa un nulo), las referencias a
otros componentes que se necesiten (ninguna en el caso de este control), y finalmente
la plantilla a la que queremos asociar el DataView. ¡Buff!. Parece complicado pero
no lo es tanto una vez que te acostumbras.
De todas maneras lo más habitual es utilizar el control DataView de manera
declarativa, es decir, sin necesidad de escribir código. Esto es mucho más fácil
y rápido, pero es importante conocer la técnica anterior (en código) por si fuera
necesario en algunos casos complejos.
Lo primero que necesitamos para poder usar el control declarativamente es indi-
car a la página nuestra intención, incluyendo el espacio de nombres apropiado dentro
del cuerpo del HTML. Lo conseguimos con estos atributos:
<body
xmlns:sys=”javascript:Sys”
xmlns:dataview=”javascript:Sys.UI.DataView”
sys:activate=”*”>

Lo que estamos declarando son dos espacios de nombres que nos servirán para
escribir atributos especiales en los elementos HTML sin que el navegador “se queje”,
y que la infraestructura de AJAX sepa interpretarlos. Los nombres después de xmlns
son los prefijos para los atributos. Así tendremos atributos sys y dataview. Puedes
pensar en esto como en las directivas <@Register> de una página ASPX que luego
nos permiten usar controles de usuario en ellas.
El atributo sys:activate sirve para indicar en qué controles queremos utilizar
los diferentes atributos (y por tanto controles) que estamos definiendo. Si ponemos
un asterisco, como en el ejemplo, estamos indicando que los queremos usar con
cualquier elemento de la página. La otra opción es utilizar una lista de identifi-
cadores de elementos separados por comas (por ejemplo: sys:activate=”productos-
template,txtNombre,txtApellidos”), que es más conveniente en páginas con mucho
HTML ya que ofrece mejor rendimiento.
Al definir los espacios de nombres apropiados ya podemos asociar controles
DataView a plantillas con atributos HTML directamente, sin código. Por ejemplo
nuestra tabla de productos quedaría así:
Enlazado a datos en el navegador  97

<tbody id=”Productos-template” class=”sys-template”


sys:attach=”dataview”
dataview:dataprovider=”Servicios/NorthwindService.svc”
dataview:fetchoperation=”GetAllProducts”
dataview:httpverb=”GET”
dataview:autofetch=”true”>
<tr>
<td>{{ ProductName }}</td>
<td>{{ UnitPrice }}</td>
</tr>
</tbody>

Fíjate que lo único que hay que hacer es adjuntar un control DataView a nuestro
elemento contenedor de la plantilla gracias al atributo sys:attach. El resto son asig-
naciones de propiedades para que el nuevo control pueda funcionar, las cuales se
establecen con el prefijo dataview definido antes, seguido del nombre de la propiedad
y el valor adecuado para cada una. Así, en este ejemplo lo que estamos haciendo
con las propiedades es:
• Indicar que el proveedor de los datos es el servicio Web ubicado en “Servi-
cios/NorthwindService.svc”, relativa a la página actual.
• Que el método que debe llamar para obtener los datos es “GetAllProducts”.
• Que la llamada debe hacerla usando el método GET, y no el método POST
que se usa por defecto. El motivo es que el servicio Web que hemos creado
es de tipo REST (http://es.wikipedia.org/wiki/REST) y así lo requiere.
• Finalmente con la última propiedad le estamos diciendo que debe recoger
automáticamente los datos y visualizarlos nada más crearse el control, para
que no tengamos que forzarlo mediante código. Así se obtendrán y enlazarán
los datos nada más cargarse la página.
El orden en el que se asignen es indiferente. Hacer esto es equivalente al código
de inicialización que vimos antes, por lo que nos lo ahorramos y podemos hacer el
enlazado de manera más sencilla y directamente en el HTML.
Recapitulemos un momento todo lo que hemos necesitado:
1. Definimos el HTML de la plantilla indicando donde van sus campos y mar-
cando con la clase sys-template al elemento contenedor de la misma.
2. Declaramos en el cuerpo que queremos usar declarativamente el espacio de
nombres Sys y el control Sys.UI.DataView, activando de paso los elementos
que nos interesen.
3. Activamos la plantilla asignando a su contenedor un control DataView y
estableciendo las propiedades necesarias.
98  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Como vemos es más sencillo y directo de lo que parece con todas las explicacio-
nes intermedias, necesarias por otra parte. Con esto ya tendríamos la funcionalidad
deseada.
Si ahora ejecutamos la página veremos que se visualizan los datos de los productos
de manera asíncrona tras cargar la página. Lo único que hemos transferido desde el
servidor son la página original (prácticamente vacía) y los datos que necesitábamos
mostrar. Todo lo demás se ha hecho en el cliente con JavaScript.

Figura 2.- La tabla resultante generada en el cliente

De momento no es muy espectacular, pero en los siguientes apartados iremos


mejorando paulatinamente el ejemplo a medida que aprendemos más características
del enlazado de datos en el cliente.

5.- Pseudo-columnas y atributos especiales


Vamos a mejorar un poco la visualización de nuestra tabla de datos con varias carac-
terísticas adicionales. En primer lugar vamos a hacer que las columnas pares tengan
un color diferente a las columnas impares. Con este objetivo vamos aprenderemos
a usar las pseudo-columnas de datos.
Estas pseudo-columnas son campos especiales que se pueden utilizar en las
plantillas y que nos ofrecen información de contexto sobre los datos que estamos
enlazando. En concreto en la versión actual disponemos de las siguientes pseudo-
columnas:
• $index: nos indica el número (basado en cero) del registro que el DataView
está procesando actualmente.
Enlazado a datos en el navegador  99

• $dataItem: se trata de una referencia al objeto de datos que se está usando


en este momento para generar la plantilla, por lo que podremos usarlo para
obtener información sobre el mismo con reflexión, llamar a alguno de sus
métodos o lo que pudiésemos necesitar.

• $id(“identificador”): nos permite generar un identificador único para cada


elemento de una plantilla, generado a partir de la cadena que se le pase.

• $element: permite el acceso al elemento de la plantilla que se está proce-


sando.

Así, el primer añadido que vamos a hacer a nuestra tabla de datos es colocar
una columna extra al principio de la misma que nos muestre el número de cada
registro. Tras añadir la cabecera <th> correspondiente podemos incluir este código
en la plantilla:
<td>{{ $index + 1 }}</td>

Como $index está basado en cero le sumamos 1 para que tener una numeración
más apropiada para el usuario.
Otra característica que necesitaremos son unos atributos especiales para plantillas
llamados atributos class. Antes de poder usarlos debemos declararlos en el cuerpo
de modo análogo a como lo hicimos antes con los prefijos sys y dataview. La etiqueta
del cuerpo quedaría así:
<body
xmlns:sys=”javascript:Sys”
xmlns:dataview=”javascript:Sys.UI.DataView”
xmlns:class=”http://schemas.microsoft.com/aspnet/class”
sys:activate=”*”>

A partir de este momento tenemos la capacidad de usar atributos class para


establecer clases CSS condicionadas en los elementos de las plantillas. Lo que se
consigue es asignar una clase CSS al elemento sólo en el caso de que se cumpla
una determinada condición. En el ejemplo que nos ocupa vamos a usar una clase
CSS para las columnas pares y otra diferente para las columnas impares, por lo
que combinándolo con la pseudo-columna $index que acabamos de ver, la plantilla
quedaría así:
<tr class:impares=”{{ $index % 2 != 0 }}”
class:pares=”{{ $index % 2 == 0 }}”>
<td>{{ $index + 1 }}</td>
<td>{{ ProductName }}</td>
<td>{{ UnitPrice }}</td>
</tr>
100  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Fíjate que ahora la fila de la tabla tiene un par de atributos class. El primero
asigna la clase CSS de nombre “impares” a aquellas filas cuyo índice no sea divisible
entre dos (es código JavaScript normal y corriente que usa el operador % para
calcular el módulo de una división), y asigna la clase “pares” en caso contrario. De
esta forma conseguimos un aspecto diferente en filas alternas de la tabla.

6.- Atributos sys condicionales


Del mismo modo que podemos asignar clases de manera condicional, existen unos
atributos especiales del espacio de nombres sys que trabajan de manera parecida.
En este caso lo que nos permiten es establecer algunos atributos comunes de los
elementos HTML. Se trata de los siguientes:

• sys:checked: se usa para generar el atributo checked de los elementos HTML


que lo soportan, indicando una condición para ello. Lo usaremos ahora mis-
mo en nuestro ejemplo.

• sys:disabled: idem que el anterior pero para el atributo disabled que permite
desactivar controles.

• sys:id: se usa para generar identificadores de elementos mediante código Ja-


vaScript. Por ejemplo, para generar un identificador a partir del identificador
de producto podríamos escribir: sys:id=”{{ ‘Prod_’ + ProductID }}”.

• sys:src: para generar atributos SRC a partir de código. Muy útil por ejemplo
para colocar rutas a imágenes sacadas de una base de datos.

Sabiendo esto vamos a añadir una columna más a nuestra plantilla para que se
visualice el estado de disponibilidad de cada producto. Hay un campo en la base
de datos llamado Discontinued, que es verdadero si el producto está descatalogado.
Podemos aprovecharlo para mostrar esta información usando un checkbox de HTML,
mediante un atributo sys condicional del siguiente modo:

<td>
<input type=”checkbox” disabled=”disabled” sys:checked=”{{
Discontinued }}” />
</td>

Es decir, que sólo aparecerá marcado si el valor del campo Discontinued es


true.
Con estos cambios nuestra nueva plantilla mostrará la lista de productos de la
manera que se observa en la figura 3.
Enlazado a datos en el navegador  101

Figura 3.- tabla resultante de nuestra plantilla mejorada.

7.- Atributos code para renderizado


condicional
Una cuestión habitual al generar HTML en el cliente es que necesitemos que algunos
elementos se creen sólo al cumplirse ciertas condiciones. Por ejemplo, al enlazar
datos a una lista desplegable que actuará de filtro para otra tabla, si nos encontramos
en el primer elemento que también se renderice un elemento adicional vacío que sirva
para deshacer el filtro. Este ejemplo en concreto lo tienes en el código del ZIP, en
la página “Default.aspx”.
Las variantes de este atributo son tres:

• code:if: si el resultado del código de este atributo es verdadero se renderizará


el elemento, obviándolo de la interfaz en caso contrario.

• code:before: ejecutará, justo antes de procesar el elemento para la plantilla, el


código JavaScript que le asignemos como valor. Útil para hacer cosas durante
el procesamiento que puedan afectar al resto de la plantilla.

• code:after: idem que el anterior, pero ejecutando el código al terminar de


procesar el elemento actual.

Estos atributos son muy útiles cuando los combinamos con las pseudo-columnas
que estudiamos anteriormente.
102  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Para poder utilizarlos hay que declarar su espacio de nombres en la etiqueta


body, con lo que nos quedaría de la siguiente forma (ahora es el tercer espacio de
nombres declarado):
<body
xmlns:sys=”javascript:Sys”
xmlns:dataview=”javascript:Sys.UI.DataView”
xmlns:code=”http://schemas.microsoft.com/aspnet/code”
xmlns:class=”http://schemas.microsoft.com/aspnet/class”
sys:activate=”*”>

Vamos a aprovechar estos atributos para añadir a nuestra tabla de datos una
indicación visual sobre el stock actual de los productos. Vamos a indicar tres niveles
diferentes: verde, naranja y rojo, según si tenemos mucho stock, poco o ninguno en
absoluto. El valor del inventario lo conocemos gracias al campo de la base de datos
llamado UnitsInStock.
Añadiremos una nueva columna a la tabla que contendrá la definición de los tres
niveles de stock con sendas imágenes:
<td>
<img src=”Images/stock_ok.gif” code:if=” UnitsInStock >= 20 “ />
<img src=”Images/stock_m.gif” code:if=” UnitsInStock < 20 &&
UnitsInStock > 0” />
<img src=”Images/stock_ko.gif” code:if=” UnitsInStock == 0 “ />
</td>

Hemos incluido en cada una de ellas un atributo code:if con una condición que
define cuándo debe generarse el elemento en la plantilla. De este modo si el stock
actual del producto es superior a 20 unidades, consideramos que tenemos mucho y se
mostrará una marca de verificación de color verde. Si es mejor de 20, indicaremos un
stock mermado con una admiración naranja. Finalmente si no hay stock se indicará
mediante un aspa roja.

Figura 4.- Nuestra tabla con indicadores de nivel de stock.


Enlazado a datos en el navegador  103

Estas expresiones nos dan mucho juego para determinar qué elementos queremos
mostrar en cada momento y también para actuar sobre variables globales o sobre
elementos que se van a procesar a continuación o que acaban de renderizarse.

8.- Enlazado de datos en tiempo real


Hasta ahora hemos enlazado objetos usando la sintaxis de la doble llave {{ }}, cono-
cida como evaluador de expresiones en línea. Como deducimos de su nombre, su
objetivo es evaluar las expresiones JavaScript que hayamos escrito dentro. Una vez
hecho esto no hay ningún tipo de relación posterior con los elementos subyacentes.
De hecho podemos utilizarlas para evaluar cualquier tipo de expresión JavaScript,
no es necesario que sea una propiedad del objeto que estamos enlazando.
El uso de esta sintaxis nos otorga un gran poder, pero ofrece una limitación
fundamental: el enlace se realiza sólo una vez, al procesar la plantilla. Una vez
visualizada la información, si se produce cualquier cambio en los datos subyacentes,
éste no se verá reflejado en la interfaz.
La funcionalidad de plantillas de ASP.NET AJAX permite mantener sincroniza-
dos, en tiempo real, los datos enlazados y la interfaz de usuario. De esta manera
si la información varía (porque la hemos editado en otro lugar de la página, por
ejemplo), sus nuevos valores se ven reflejados de inmediato en todas las plantillas
que los usen. Esta funcionalidad se basa en el uso del patrón de diseño Observer,
gran conocido por los programadores aficionados a la arquitectura de software, y en
especial los que vienen del mundo Java.

Nota:
No vamos a entrar en detalle aquí sobre el funcionamiento de este patrón, pero baste decir que
es suficiente utilizar una llamada al método makeObservable de la clase Sys.Observer (inclui-
do por las extensiones de JavaScript de ASP.NET AJAX), para que automáticamente nuestro
objeto genere eventos ante todos los cambios que se produzcan en sus propiedades. Y esto es
independiente de la funcionalidad de plantillas por lo que podemos sacarle partido en nuestros
propios desarrollos para otras cosas.

El enlazado a datos en las plantillas tiene una sintaxis alternativa que habilita el
refresco en tiempo real de la interfaz. En lugar de usar una doble llave, se emplea
una llave simple junto con la palabra clave binding. Es decir, en lugar de escribir:
{{ ProductName }}

escribiremos
{binding ProductName }

y automáticamente, a partir de ese momento, si cambia el valor del nombre de


producto, este cambio se verá reflejado automáticamente en la interfaz generada a
partir de la plantilla. Y todo esto sin tener que escribir ni una línea de código.
Esta característica se denomina Live Binding o Enlazado en Tiempo Real.
104  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Dependiendo del elemento HTML al que enlacemos los datos, el enlace puede
ser en un solo sentido (single-way) o bidireccional (two-way).
Por ejemplo si enlazamos un campo a un control input de texto, lo que escribamos
en éste hará que se modifique el valor subyacente, modificando los datos originales.
Sería un enlace bidireccional, puesto que los cambios en el objeto se reflejan en el
control y viceversa. Sin embargo si lo enlazamos a un elemento de sólo lectura (por
ejemplo a un <span>), el enlace sólo será posible en un sentido, ya que el elemento
al no cambiar no puede afectar a los datos originales.
Todo esto nos resultará de gran ayuda para enviar información modificada al
servidor como veremos enseguida.
Mientras tanto vamos a ver un ejemplo rápido de uso que, si bien no será muy útil,
nos dará una idea precisa de cómo funciona el Live Binding. Para ello enlazaremos
una plantilla a un elemento de la página, y no a un origen de datos. Así también
veremos cómo se puede usar para muchas más cosas que para enlazarnos a datos
traídos desde el servidor.

Nota:
Este ejemplo lo tienes en las descargas del libro, dentro de la carpeta correspondiente a
las plantillas de datos (VS2010_PlantillasDatosCliente_AJAX), en el archivo llamado
“BindingEnDosSentidos_EjBasico.aspx”.

Arrastremos un control de texto HTML (<input>) a la página. A continuación,


dentro de un <span> que actuará de contenedor de nuestra plantilla, definiremos
otro <span> y un nuevo control de texto. La sintaxis final será esta:

<input type=”text” id=”txtOrigen” value=”” />&nbsp;


<span id=”textoEnlazado” class=”sys-template”
sys:attach=”dataview”
dataview:data=”{{ $get(‘txtOrigen’) }}”>

<span>{binding value}</span>&nbsp;
<input type=”text” id=”txtCopia” value=”{binding value}”
/>
</span>

En este caso usamos el dataView para enlazarnos directamente contra el primer


cuadro de texto. Aquí no hay datos traídos del servidor ni código alguno, sólo sin-
taxis declarativa. Fíjate que no hay tampoco código de servidor, y que podríamos
haber usado una página con extensión .htm siempre que incluyamos los Scripts de
ASP.NET AJAX como archivos externos y no con un ScriptManager.
En la parte correspondiente a la plantilla hemos usado Live Binding para mos-
trar el valor del campo de texto (propiedad value de éste, por eso se escribe
Enlazado a datos en el navegador  105

{ binding value }) en una etiqueta (<span>) y en otro cuadro de texto. ¿Qué


ocurrirá al ejecutarlo?
Abre la página y juega con ella un poco. Verás que todo lo que escribas en el
primer cuadro de texto se verá automáticamente reflejado en los otros dos elementos
en cuanto quites el foco de él. Al mismo tiempo, como el segundo cuadro de texto
tiene un enlace bidireccional con el primero, al escribir algo en él y quitarle el foco,
el cambio aparecerá de inmediato en el primero y en la etiqueta.
En la práctica lo que hemos conseguido es mantener sincronizados ambos cua-
dros de texto y la etiqueta. Fíjate además que la propia infraestructura de AJAX se
ocupa de evitar que entremos en un bucle de cambios infinitos cuando uno cambia
al otro.
Esta funcionalidad nos otorga un enorme poder para el manejo de datos en el clien-
te, y en los próximos apartados vamos a ver cómo sacarle todo el partido posible.

9.- Vistas maestro-detalle: preparar el


maestro
Vamos a seguir mejorando nuestro ejemplo para, al mismo tiempo, aprender nuevas
características de las plantillas de lado cliente. En esta ocasión vamos a añadirle la
posibilidad de mostrar un detalle de los registros al pulsar sobre ellos. Se trata de
la típica vista maestro-detalle que hay en casi todas las aplicaciones: facturas-líneas
de factura, proveedor-artículos, etc... En esta ocasión vamos a mostrar en otro lugar
de la página, con una nueva plantilla, los datos seleccionados en nuestra lista de
productos.
Lo primero que vamos a ver es de qué manera podemos provocar la selección.
Existen tres atributos especiales del espacio de nombres Sys que están especializados
en generar acciones para los elementos de las plantillas. Se trata de:
• sys:command: el nombre del comando que queremos lanzar. Actualmente
el único comando estándar existente es “Select” para seleccionar elementos.
Podemos, no obstante, emplear cualquier otra cadena arbitraria para definir
comandos propios, y así generar eventos adaptados a nuestras necesidades.
• sys:commandargument: argumentos que queremos pasarle al comando, para
particularizarlo. Quizá un identificador, una referencia a un elemento, etc...
• sys:commandtarget: el elemento sobre el que queremos actuar. Normalmente
no se utiliza.
En nuestro ejemplo vamos a hacer que se seleccione el producto actual en el
momento en el que el usuario pulse sobre cualquier punto de fila de la tabla de
productos. Redefinimos la fila (elemento <tr> en nuestro ejemplo) añadiéndole el
atributo sys:command, así
<tr sys:command=”Select” .... />
106  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Esto provoca que el procesador de plantillas incluya el JavaScript necesario para


conseguir ese efecto. El evento de selección es automáticamente reconocido por el
control dataView.
Si ahora probamos el ejemplo, lo cierto es que no notaremos diferencia alguna
con lo que ya teníamos. Necesitamos algo más para que se manifieste visiblemente
la selección.
El control dataView está diseñado para trabajar en equipo con sys:command, y
dispone de una propiedad llamada selectedItemClass. Ésta sirve para asignar auto-
máticamente un estilo CSS al elemento que provoca el comando.
Define en la página del ejemplo una nueva clase CSS, de nombre “.seleccionado”,
que establezca el color de fondo en un tono que destaque sobre los restantes utiliza-
dos en la página. Ahora añade este atributo al elemento contenedor de la plantilla:
dataview:selecteditemclass=”seleccionado”

Ejecuta de nuevo la aplicación. Ahora sí que seleccionarás de manera visible


cada fila, ya que al hacer clic sobre una de ellas ésta adoptará el estilo de nombre
“seleccionado”.
Los datos que queremos visualizar como vista de detalle de nuestros productos
son los campos del producto actualmente seleccionado. Los dataView tienen una
propiedad llamada selectedData que devuelve precisamente esta información. Ne-
cesitamos alguna manera de identificar al dataView de los productos para poder
acceder a sus propiedades y así poder leer los datos del elemento seleccionado.
Aquí es donde entra el último atributo que veremos dentro del espacio de nombres
de sistema: sys:key. Con él podemos asignar de manera declarativa identificadores
JavaScript a los componentes, como el dataView, que activemos en los elementos
HTML.
Para otorgar un nombre a nuestro dataView de productos añadiremos el siguiente
atributo al elemento contenedor de la plantilla de productos (míralo en el código de
ejemplo):
dataview:sys-key=”TablaProductos”

De este modo a partir de ahora podremos acceder a este dataView desde otros
elementos de plantillas utilizando el identificador que le hemos asignado, en este
caso “TablaProductos”. Fíjate en que como se está aplicando un sys:key como atri-
buto secundario de dataView, se usa un guión y no dos puntos en la declaración
anterior: dataview:sys-key.

10.- Vistas maestro-detalle: preparar los


detalles
Recapitulemos lo que hemos hecho hasta ahora:
• Hemos usado sys:command para indicar qué elemento hará la selección.
Enlazado a datos en el navegador  107

• Establecemos el estilo de los elementos seleccionados con la propiedad se-


lectedItemClass del dataView.
• Empleamos sys:key para darle un nombre accesible al dataView de productos
que actuará de maestro en esta visualización maestro-detalle.
De acuerdo. Ahora que ya sabemos seleccionar visualmente un elemento de las
plantillas, veamos cómo podemos enlazarlo con otra plantilla que visualice los con-
tenidos correspondientes al detalle.
En este caso para visualizar los detalles usaremos un conjunto de etiquetas y
controles de texto que además nos permitirán editar los campos de datos. Para
simplificar el ejemplo sólo mostraremos los campos correspondientes al nombre del
producto y a su precio.
El código necesario para obtener esta vista es el que se muestra en la figura 5.

Figura 5.- Código de la vista de detalle

Estamos usando etiquetas estándar de HTML 4.0 (<fieldset>, <legend>, <label>,


etc...), pero podríamos haber usado otros elementos cualquiera según nuestras nece-
sidades. Se trata de una plantilla normal y corriente.
Lo único importante aquí es que nos fijemos en cómo estamos asociando los
datos a visualizar por el control dataView de la plantilla. Hemos usado una sintaxis
especial de las etiquetas {} de Live binding:
dataview:data=”{binding selectedData, source={{TablaProductos}} }”

Esta expresión hace que, como datos para visualizar en la plantilla, se utilice la
propiedad selectedData (o sea, una referencia al elemento seleccionado) en el control
llamado “TablaProductos”, que es el primer dataView.
¡Listo! Si ahora ejecutas la página verás que al pulsar en cualquier producto, éste
se selecciona y se visualizan su nombre y su precio en los campos de la segunda
plantilla de detalles, a la derecha (figura 6).
108  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Figura 6.- Vista maestro-detalle de nuestros datos.

Lo más interesante de la vista de detalles que hemos hecho es que nos permite
editar los datos. Si seleccionas cualquier elemento y lo editas en su vista de deta-
lles, podrás comprobar que al modificar el título o el precio, el cambio se refleja
inmediatamente en la tabla de productos usando Live Binding. ¡Para conseguir esto
no hemos tenido que escribir ningún tipo de código!.

Nota:
Si no te funciona la actualización en la tabla principal asegúrate de que has usado en ésta la
sintaxis {binding NombreCampo} para enlazar los datos, y no las dobles llaves que, como sa-
bemos, no habilitan el Live binding.

11.- Devolver los datos modificados al


servidor: contextos de datos
Si juegas un poco con el ejemplo anterior verás que modificas los datos pero en
cuanto cargas la página de nuevo todos los cambios se pierden. Esto es normal ya
que el código JavaScript está atado a la vida de cada página, y además no hemos
definido ninguna forma de enviar los datos modificados al servidor.
Sería perfecto tener un botón disponible en algún lado de la página que, tras haber
editado tantos registros como queramos, podamos pulsar para enviar los cambios al
servidor y persistirlos en la base de datos. Esto cerraría el círculo de nuestra visión
de un desarrollo AJAX “puro”. Como veremos es muy fácil de conseguir gracias a
ASP.NET AJAX.
Dentro del espacio de nombres Sys.Data existe una clase especial orientada a
trabajar con datos del servidor desde el lado cliente: DataContext.
Esta clase permite encapsular toda la funcionalidad necesaria para comunicarse
con el servidor, para obtener datos y también para devolverlos. Además, tiene la
capacidad de llevar un registro de todos los cambios efectuados en los datos ori-
Enlazado a datos en el navegador  109

ginales traídos desde el servidor. Podemos saber cuáles se han modificado, cuáles
son nuevos o qué datos se han borrado. Lo más impresionante es que un sólo objeto
DataContext es capaz de llevar el control de cambios de todos los datos obtenidos
del servidor, aunque éstos hayan sido obtenidos de métodos diferentes y devuelvan
objetos de diferentes clases.
Posteriormente sólo con una llamada a su método saveChanges(), todos los cam-
bios que haya habido en el cliente se envían de una sola vez al servidor para ser
persistidos. Ello convierte la operación en un juego de niños.
Existe una versión especializada de esta clase orientada para trabajar con ADO.
NET Services en lugar de con servicios Web: AdoNetDataContext.
Sabiendo esto vamos a rematar nuestro ejemplo añadiéndole la capacidad de
enviar al servidor todas las modificaciones que hagamos en los registros. En este
caso no le vamos a añadir la capacidad de borrar y añadir registros, pero sería algo
muy sencillo y se deja como ejercicio para el lector.
Supongamos que tenemos un método ya creado en nuestro servicio que se llama
SaveProducts (suelo usar siempre todos los nombres de métodos públicos en inglés),
y que sirve para guardar los cambios enviados desde el cliente. Luego volveremos
sobre él para explicar la forma de crearlo porque es lo más difícil, pero de momento
vamos a dar por sentado que ya existe.
Lo primero que tenemos que hacer es utilizar como origen de datos para nuestras
plantillas un objeto DataContext, ya que éste se encargará de traer la información y
luego mantener un registro de cambios producidos sobre ella.
Lo podemos declarar instanciando por código un objeto Sys.Data.DataContext
y luego ir estableciendo sus propiedades a mano. Sin embargo es más robusto e
integrado usar una llamada al método especial $create dentro del evento de ini-
cialización de la aplicación, como ya vimos en el apartado 4. Por ello añadiremos
este código a la página (en el ejemplo descargable se encuentra dentro del archivo
auxiliar “apoyoBinding.js”):
var dc;

Sys.Application.add_init(function() {
dc = $create(Sys.Data.DataContext,
{
serviceUri: “Servicios/NorthwindService.svc”,
saveOperation: “SaveProducts”
});
});

Lo único que hemos hecho es crear el contexto de datos y decirle cuál es la


ruta del servicio que debe utilizar para sus operaciones, así como el nombre de
la operación de guardado de datos (propiedad saveOperation), que en este caso
se llama SaveProducts como ya hemos comentado. Dejamos una variable global
llamada dc que nos servirá para tener acceso a este contexto de datos y así poder
utilizarlo desde el código.
110  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

El otro cambio que debemos hacer es indicar a nuestro dataView —encargado


de mostrar los datos de los productos— que debe usar ese contexto como proveedor
de datos. Hasta el momento le habíamos asignado directamente la ruta del servicio,
pero ahora le vamos a decir que use nuestro contexto de datos para obtener la
información:
<tbody id=”Productos-template” class=”sys-template”
sys:attach=”dataview”
dataview:httpverb=”GET”
dataview:autofetch=”true”
dataview:dataprovider=”{{ dc }}”
dataview:fetchoperation=”GetAllProducts”
dataview:selecteditemclass=”seleccionado”
dataview:sys-key=”TablaProductos”>

Fíjate como en la propiedad dataProvider le hemos asignado nuestro recién crea-


do contexto de datos. Si ahora ejecutas la página veremos que, en apariencia, nada
ha cambiado en su funcionamiento. Sin embargo, lo que no se percibe es que ahora
todos los cambios que hacemos en los datos quedan registrados por el DataContext,
lo que sí es un cambio significativo aunque no se manifieste visiblemente.
Gracias a esto lo único que nos queda es enviar los resultados al servidor. Co-
locaremos un botón en la página, y como único código necesario para su evento
click escribiremos:
dc.saveChanges();

¡Listo! Esto enviará los datos que se hayan modificado al servicio web, el cual
deberá encargarse de almacenar los cambios en la base de datos. Más sencillo
imposible.

Nota:
En el ejemplo de código que puedes descargar de la web de Krasis Press he incluido un ejemplo
más completo en el que se define un comando personalizado para el botón (así aprenderás a usar
mejor sys:command). Además se amplía la capacidad de la operación saveChanges para detec-
tar actualizaciones con éxito, errores y cuando no se han enviado datos al servidor. Todas estas
circunstancias se informan a través de un mensaje auto- ocultable en la interfaz de usuario. La
página completa es “BindingPorCodigo.aspx”

12.- La definición del método de guardado


de cambios
No existe documentación sobre cómo se debe crear un método para un servicio Web
que se use para recibir la información de datos modificados desde un dataContext de
lado cliente. El motivo es que todos los ejemplos y documentación que puedes en-
contrar por Internet usan la clase AdoNetDataContext y un servicio autogenerado de
Enlazado a datos en el navegador  111

ADO.NET Data Services. Por ello si quieres crear un servicio WCF o ASMX normal
que pueda trabajar con contextos de datos normales, estás “solo ante el peligro”.
Si analizamos con Fiddler u otra utilidad los datos JSON que se envían desde el
cliente al utilizar el método saveChanges de un dataContext, veremos algo como lo
siguiente para la actualización de un producto:
{“changeSet”:[{“action”:1,”item”:{“__type”:”Product:#Datos”,”ProductI
D”:3,”ProductName”:”Aniseed Syrup”,”SupplierID”:1,”CategoryID”:2,”Qua
ntityPerUnit”:”12 - 550 ml bottles”,”UnitPrice”:”20”,”UnitsInStock”:1
3,”UnitsOnOrder”:70,”ReorderLevel”:25,”Discontinued”:false}}]}

Lo que se envía, por tanto, es una colección de objetos en un parámetro llamado


changeSet. Estos objetos no tienen un nombre de tipo definido, así que vamos a
llamarles nosotros como queramos, por ejemplo Change. Lo que sí tienen son dos
propiedades llamadas action e item, que contienen respectivamente el tipo de acción
a realizar con el objeto y el objeto en sí que hay que modificar.
Investigando un poco al enviar diferentes tipos de cambios al servidor se llega a
la conclusión de que los valores para las acciones pueden ser solamente 3: inserción
(0), actualización (1) y borrado (2).
El objeto contenido en la propiedad item es un objeto del tipo que le hayamos
pasado al cliente inicialmente, en nuestro caso un objeto Product del contexto de
datos de Linq2SQL que hemos utilizado para el acceso a datos.
Así que podemos crearnos en el servidor una clase genérica llamada Change,
que mapee los miembros de estos objetos JSON con cambios que nos llegan desde
el cliente. En lugar de crear una específica para manejar cambios de productos, la
haremos genérica y así nos valdrá para todas las entidades que queramos manejar
en el futuro. El código es el siguiente:
Public Class Change(Of T)
Public item As T
Public action As TipoOperacion

Public Enum TipoOperacion As Integer


Insercion = 0
Actualizacion = 1
Borrado = 2
End Enum

End Class

Es decir, es una simple clase con los mismos campos que nos manda el cliente
en formato JSON. Para la acción, en lugar de tener que acordarnos de qué significa
cada número, simplemente lo definimos en una enumeración y así nos resultará más
fácil utilizarlos luego en el código.
Bien, sólo nos queda definir entonces el aspecto que debe tener un método de
actualización capaz de recibir estos datos, que será el de la figura 7.
112  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Figura 7.- Código del método de actualización

Como vemos toma como parámetro una lista genérica de objetos Change parti-
cularizados para manejar objetos de nuestra entidad Producto. No te dejes confundir
por el encadenamiento de “Of” para particularizar el código genérico. Es una simple
lista de objetos que a su vez contienen a otro objeto con los datos a modificar.
En C# la sintaxis sería:
Public int SaveProducts(List<Change<Product>> changeSet)

En realidad esta función podemos definirla para que no devuelva ningún resulta-
do (convirtiéndola en un Sub en VB o devolviendo un void en C#) y todo funcionará
igual. Lo importante es el parámetro que hay que pasarle para que pueda recibir con
éxito los valores modificados.
Una vez definida la función en el servicio ya podremos actualizar desde el cliente
conjuntos de datos con el método saveChanges del DataContext.

13.- Historia del navegador


La principal ventaja de las interfaces AJAX es que todo se produce, aparentemente,
en el navegador y eliminamos los refrescos de página. Sin embargo esta ventaja en
ocasiones se puede convertir en un problema.
Consideremos por ejemplo la página de productos que hemos estado desarrollan-
do en este capítulo, e imaginemos que sirve para mostrar la información detallada
de cada producto a los visitantes de nuestra Web. Cuando el usuario pulsa sobre un
producto se visualizan sus detalles, pero como todo se hace desde el lado del cliente,
de repente nos damos cuenta de que hemos perdido dos cosas muy importantes: la
capacidad de usar el botón de volver hacia atrás en el navegador, y la capacidad de
que el usuario guarde en sus favoritos (o envíe a un amigo) un producto concreto
en el que esté interesado.
Imagina que eres un cliente que está buscando un producto en la página y que
has estando seleccionando varios para ver sus características. De repente deseas
volver a uno de los anteriores que ya habías visto y al darle al botón “atrás” del
navegador descubres que éste no hace nada o, lo que es peor, si habías estado antes
Enlazado a datos en el navegador  113

en otra página ésta aparece sustituyendo a la actual. Le vuelves a dar hacia adelante y
regresas a la página en la que estabas, pero todo se carga de nuevo y no tienes forma
de saber en qué producto estabas. No parece una situación muy deseable ¿verdad?
Ahora llegas a un producto que te interesa y decides enviárselo a un amigo para
que le eche un vistazo y te dé su opinión. Copias y pegas la URL de la página y
se la mandas por correo electrónico. Lo malo es que como la página es la misma
para todos los productos (gracias a AJAX), al abrir la otra persona el enlace en su
navegador se encuentra con la lista de productos, pero ninguno seleccionado, por lo
que no tiene forma de saber a cuál de ellos te estabas refiriendo.
Como ves, no son problemas tan banales como parecen a simple vista. ¿Cómo
podemos solucionarlos?
Desde la aparición del Service Pack 1 de .NET 3.5, está disponible una nueva
característica destinada a atajar este problema. Se trata de la propiedad EnableHis-
tory de la clase ScriptManager.
Cuando la establecemos a True en una página, automáticamente disponemos,
tanto en el cliente como en el servidor, de un método AddHistoryPoint que nos per-
mite añadir entradas a la historia del navegador, y un evento Navigate, que se llama
automáticamente cuando el usuario pulsa los botones de navegación para moverse
por los puntos que hemos definido.
Estos dos miembros del ScriptManager se usan en eventos de servidor para crear
y restaurar el estado de la página cuando trabajamos con controles UpdatePanel.
En el lado cliente debemos decidir en qué momentos queremos añadir entradas a la
historia y cómo vamos a actuar cuando se intente navegar por ellas. En realidad la
parte de servidor lo que hace es enviar al navegador el mismo código que usaríamos
nosotros para hacerlo desde JavaScript.
Vamos a retocar nuestro ejemplo del listado de productos para que, cada vez
que seleccionemos un producto, se cree una entrada en el historial de navegación.
Veremos que, de este modo, habilitaremos tanto la historia como el poder crear
enlaces directos a productos seleccionados.
Lo primero es establecer la propiedad EnableHistory del ScriptManager como
True. Ahora ya podremos utilizar toda la funcionalidad necesaria.
Vamos a capturar el evento de selección de un producto en la tabla, para lo cual
añadimos el siguiente atributo al dataView asociado con ésta:
dataview:oncommand=”{{ gestionarSelect }}”

Ahora, cada vez que se seleccione una columna se llamará a esta función de
JavaScript que tendremos que definir. Además añadiremos al comando Select alguna
información de contexto que nos ayude a distinguir en qué fila se ha producido.
Aquí viene muy bien el atributo sys:commandarguments que hemos estudiado hace
un rato:
<tr sys:command=”Select” sys:commandargument=”{{ { Fila: $index,
Nombre: ProductName} }}” ... >
114  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Lo que hacemos es pasar como argumento al comando de selección un objeto


anónimo (en formato JSON) que tiene dos propiedades:
• Fila: que indica en qué fila estamos actualmente.
• Nombre: el nombre completo del producto, que nos servirá para hacer las
entradas del navegador un poco más amigables.
Este objeto se recibirá en la función que vamos a definir para gestionar el evento
de selección, y que tendrá este aspecto:
function gestionarSelect(sender, args) {
if (args._commandName.toLowerCase() == “select”) {
var infoProd = args._commandArgument;
Sys.Application.addHistoryPoint({ FilaSeleccionada: infoProd.
Fila }, “Detalles del producto - “ + infoProd.Nombre);
}
}

Como podríamos compartir esta función entre varios comandos lo primero es


comprobar si se trata de un comando de selección, que es el que nos interesa. La
propiedad _commandArgument del segundo parámetro sirve para recibir el objeto
JSON definido como argumento en la plantilla que, si recordamos, nos informaba
de la fila seleccionada y del nombre del producto.
El método addHistoryPoint crea la entrada en la historia del navegador. Los
parámetros que le debemos pasar son un objeto arbitrario con información sobre el
estado a restaurar posteriormente, y una descripción para el punto de navegación.
En este caso le pasamos un objeto JSON que sólo tiene una propiedad en la que
se guarda la fila seleccionada por el usuario, pero le podríamos incluir toda la
información adicional que quisiésemos. Menos es más, de todos modos, así que
si puede ser sólo un número o algo así, mucho mejor pues las URLs generadas
quedarán más cortas.
Ahora sólo nos queda responder al evento de navegación que se produce cuando
el usuario usa el botón de adelante o atrás en el navegador, o cuando pegue la URL
generada en la barra de direcciones. Para definir la función que gestionará el evento
usaremos el método add_navigate de la clase Sys.Application:
Sys.Application.add_navigate(
function(sender, e) {
var fila = parseInt(e.get_state()[“FilaSeleccionada”], 10);
if (fila != NaN)
{
$find(“Productos-template”).set_selectedIndex(fila);
}
}
);
Enlazado a datos en el navegador  115

Lo que hacemos es obtener el valor de la fila actual a partir del estado que hemos
enviado como argumento en el paso anterior. Recuerda que sólo mandábamos la
fila actualmente seleccionada. Si todo ha ido correcto lo que hacemos es indicarle
al dataView que gestiona nuestra tabla de productos que esa debe ser la fila que
seleccione. Ya está.
Si ahora probamos nuestra aplicación veremos que se añaden entradas en el
historial de navegación y que podemos movernos por ellas sin problemas a pesar de
que la página no se recarga ni se envía de nuevo al servidor.

Figura 8.- Historial de navegación AJAX

Fíjate además en cómo se ha serializado la información sobre el estado actual


de la página, directamente en la URL, justo a continuación del carácter #. Esto
permitirá a los usuarios pegar directamente esta dirección en su navegador y obte-
ner la página con el elemento apropiado ya seleccionado. Como efecto secundario,
además, obtenemos un título descriptivo para la página, que coincide con el texto
de la entrada de navegación, obteniendo descripciones más informativas sobre lo
que estamos visualizando.

14.- En resumen
En estos dos últimos capítulos hemos aprendido las técnicas más importantes que
nos ofrece ASP.NET AJAX para trabajar en el lado de cliente.
La mayor parte de los programadores cuando descubren ASP.NET AJAX se que-
dan únicamente con los controles de servidor, y en especial el control UpdatePanel,
y dejan de lado el trabajo en el lado cliente y la visión de AJAX “puro” que he
descrito aquí. Se trata de un error. Gracias a todas las técnicas aprendidas estarás en
condiciones de escribir aplicaciones Web mucho más ligeras, escalables y con mayor
velocidad de respuesta para los usuarios. No dejes de lado los controles de servidor,
pero ten siempre en cuenta que JavaScript y todas las extensiones de este lenguaje
que brinda la ASP.NET, te podrán proporcionar un gran poder de desarrollo.
116  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

En el ejemplo de las plantillas de lado cliente que encontrarás en el ZIP de los


ejemplos del libro, he incluido algunas cosas interesantes a mayores de lo que hay
en este capítulo. Todas están comentadas en el código fuente y puedes investigarlas
por tu cuenta. En el archivo “Default.aspx” verás un enlazado a datos algo diferente,
en el que se utiliza una lista desplegable para obtener la lista de proveedores de
“Northwind” y luego, en función de la selección, mostrar qué productos ofrecen
en un formato maestro-detalle como el que hemos estudiado. En el archivo “Bin-
dingPorCodigo.aspx” también se ha modificado el campo de precio de producto
para que lo muestre con el símbolo del Euro y multiplicado por 100 usando una
forma especial de enlazado. Te servirá para aprender la forma de modificar un
valor antes de mostrarlo, usando la sintaxis {binding Campo, convert=Funcion,
convertBack=funcion}, similar al enlazado a datos que se utiliza en WPF (Windows
Presentation Foundation).
capítulo
6
ASP.NET Dynamic Data:
interfaces de datos a la
velocidad de la luz

En las aplicaciones web, como en casi todos los tipos de aplicaciones, una de las
tareas que más tiempo consume y que más aburre a los programadores es la de crear
interfaces de gestión. Se trata de todas esas anodinas pantallas en las que invertimos
tantas horas y que sirven básicamente para dar altas, bajas y modificar registros en
una interminable lista de tablas de la base de datos. Son, para que nos entendamos,
los famosos “mantenimientos”.
Gracias a ASP.NET y todos sus controles enlazados a datos crear estas páginas
es bastante simple, y las podemos hacer casi sin escribir código. Aún así no queda
más remedio que crearlas una a una, lo que puede llevar muchas horas de trabajo
incluso con las facilidades que tenemos. ¿No sería fantástico si pudiésemos conseguir
que se crearan solas?
Esto es precisamente lo que nos proporciona ASP.NET Dynamic Data.
Antes de continuar, unas palabras de advertencia para los escépticos: automá-
tico no quiere decir rígido e inflexible. Uno de los miedos atávicos que tienen los
programadores que llevan unos cuantos años en esto es, sin duda, a la generación
automática de código. Lo que les suele provocar recelo son dos cosas: la pérdida de
control y la merma de rendimiento, que en el fondo son lo mismo. El no saber lo
que pasa por debajo es frustrante y además tienden a pensar que, en un momento
dado, les va a limitar para que la aplicación crezca o cuando necesiten algo muy a
medida de sus necesidades. Nada más lejos de la realidad en el caso de Dynamic
Data. Como veremos se trata de un modelo absolutamente flexible y extensible, sin
que ello haga que pierda sus enormes ventajas en cuanto a productividad.
Incluso si esto no te convence tengo una buena noticia: ni siquiera tendrás que
usar la generación dinámica de la interfaz de usuario si no quieres. Aún así le podrás
sacar partido a esta tecnología desde tus interfaces “normales”, creadas a partir de
controles estándar como las rejillas y otros :-)
Dicho esto, vamos a continuar con el estudio de esta interesante tecnología que
va a multiplicar tu productividad.

117
118  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

1.- ¿Qué es Dynamic Data?


La primera vez que oí hablar de esta tecnología —y tuve la suerte de verla en fun-
cionamiento— fue en Seattle, en las instalaciones de Microsoft a principios de 2007.
Entonces el nombre interno era “Oryx” y me dejó impresionado por la productividad
que prometía dar. Al final tuvo que pasar bastante tiempo para que realmente es-
tuviera disponible y se pudieran hacer las primeras pruebas con ella. La tecnología
definitiva, que es la que nos ocupa, apareció con el Service Pack 1 de .NET 3.5 en
octubre de 2008. En ASP.NET 4.0 se han introducido algunas mejoras para ganar
en flexibilidad.

Nota:
Todo lo explicado en este capítulo es utilizable tanto en ASP.NET 3.5 SP1 como en ASP.NET
4.0. En los sitios que hay alguna diferencia se indica mediante una nota. En el archivo ZIP con
los ejemplos del libro se facilitan los ejemplos en ambas versiones del entorno de desarrollo
para tu comodidad. Por eso verás también que hay muchas notas como esta, más que en cual-
quier otro capítulo del libro.

Dynamic Data permite crear de manera dinámica interfaces de gestión de datos


para nuestras aplicaciones. Se basa en un mecanismo de “andamiaje” que permite a
ASP.NET construir páginas al vuelo para gestión de entidades, a partir de la URL
que le sea solicitada, sin tener que escribir código alguno para ello.

Nota:
El término “andamiaje” se aplica aquí porque procede del vocablo equivalente en inglés
“scaffolding” que se utiliza mucho al referirse a la creación automática de interfaces. La palabra
se popularizó a partir del año 2005 con la aparición del framework llamado Ruby On Rails, que
fue pionero en estos conceptos, atados por regla general al Modelo Vista-Controlador (MVC).
La idea es que la infraestructura proporciona automáticamente un andamiaje inicial para la
interfaz de acceso a datos, y el programador la particulariza para adaptarla a sus necesidades.

Dynamic Data sustenta su funcionamiento básicamente en tres pilares:


• Modelo de datos: se utiliza un modelo basado en objetos para representar
a los datos con los que vamos a interactuar. Este modelo define cómo son
estos datos, cuáles son sus relaciones y sus requerimientos, pero también se
usa para leer y escribir en el almacén de información subyacente. El modelo
define además las validaciones que se harán sobre la información introducida
por los usuarios.
• Plantillas: existen unas plantillas de página, plantillas de entidades y planti-
llas de campos de datos que son las que se utilizan para generar las interfaces.
ASP.NET nos proporciona multitud de ellas por defecto, y podemos crear las
nuestras personales para particularizar comportamientos.
ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  119

• Rutas: dado que las páginas se generan dinámicamente y no existen como


archivos físicos en la realidad, las rutas en la URL de la aplicación adquieren
una importancia esencial, ya que son la forma de comunicar a la aplicación
qué páginas generar y con qué propósito.
El modelo se construye usando un diagrama de entidades ORM (Object-Relational
Mapping). Éste representa mediante un conjunto de objetos a las tablas de una base
de datos relacional. En la actualidad existen dos formas soportadas de construir estos
modelos de datos: Linq2SQL y Entity Framework. La primera es una tecnología de
acceso a datos aparecida con .NET 3.5, y la siguiente, más evolucionada y compleja,
apareció con el Service Pack 1 de .NET 3.5. Ambas tecnologías permiten crear de
manera visual y sencilla un modelo de datos para interactuar con la base de datos
de modo orientado a objetos. Se manejan objetos en lugar de tablas, y se usan
expresiones Linq en lugar de consultas SQL. Ambos ofrecen grandes ventajas de
productividad y mantenimiento sobre los modelos tradicionales.

Nota:
En Krasis Press existen libros dedicados a ambas tecnologías escritos por los principales expertos
en la materia. Es prácticamente la única literatura en castellano existente sobre el tema. No te ol-
vides de visitar la Web www.krasispress.com para informarte si estás interesado en Linq o EF.

Una vez que se crea un modelo de datos visualmente, basta con registrarlo en
Dynamic Data para obtener un sitio web de mantenimiento plenamente funcional y
con múltiples características ya añadidas (como el filtrado de datos, por ejemplo).
Los controles de datos GridView y DetailsView han sido modificados para
soportar campos dinámicos de Dynamic Data. Además otros controles, como el
ListView o el FormView soportan una funcionalidad similar gracias a un nuevo
control denominado DynamicControl. Otro aspecto que ha mejorado en ASP.NET
gracias a esta tecnología es la validación de la entrada de información, ya que es
posible conseguir validación automática de los datos basándose únicamente en el
modelo y sus restricciones.

2.- Nuestro primer proyecto con Dynamic


Data
Podemos emplear esta tecnología en cualquier aplicación Web, pero lo mejor para
sacarle todo el jugo es partir de una infraestructura previa proporcionada por una
plantilla de proyecto especial. Si abres Visual Studio y creas un nuevo sitio Web o
aplicación Web verás que existen dos tipos de proyecto asociados a Dynamic Data,
como se observa al final de la lista de la figura 1.
120  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Figura 1.- Proyectos Web disponibles en Visual Studio 2010

Nota:
Estos tipos de proyectos están disponibles a partir de Visual Studio 2008 con Service Pack 1. Si
no te aparecen en el diálogo de nuevo sitio Web instala como mínimo esta versión. La tecnolo-
gía está soportada también en las versiones gratuitas de Visual Studio (Express).

Cada tipo de proyecto se refiere a una de las dos tecnologías disponibles para
crear el modelo de datos, por lo que debemos escoger uno u otro según queramos
usar Linq2SQL o Entity Framework respectivamente. Para los propósitos de este libro
vamos a utilizar la versión para Linq, pero no hay apenas diferencia y es sólo una
decisión respecto a con qué tecnología estemos más cómodos.
Lo primero que te llamará la atención al crear uno de estos proyectos es que ya
existen de entrada varias carpetas y bastantes elementos (páginas ASPX y controles)
creados por la plantilla. Luego los examinaremos con detalle, pero baste decir que
se trata de las plantillas base para generar la interfaz de usuario, y que podremos
modificarlas para adaptarlas a nuestras necesidades tanto como sea necesario. De
momento haz caso omiso de ellas.
ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  121

3.- Definir el modelo de datos


Lo primero que debemos hacer es añadir un modelo de datos a nuestro proyecto.
Como hemos comentado éste se encargará de indicar a la infraestructura de gene-
ración de páginas cómo son los datos con los que vamos a trabajar: qué tablas hay,
sus relaciones, las restricciones en los campos de datos, etc...
En este ejemplo vamos a usar nuevamente la archiconocida base de datos “Nor-
thwind” de SQL Server.
En el explorador de soluciones pulsa con el botón derecho sobre el nodo del
proyecto y añade un nuevo elemento. En el diálogo escoge la opción “Clase Linq
to SQL”. Llámale Northwind.dbml y acepta. Cuando te indique que debes añadir la
carpeta App_Code al proyecto, di que sí, puesto que es necesaria para albergar este
nuevo archivo.
Desde el Explorador de Servidores de Visual Studio añade una conexión a la
base de datos Northwind. Despliega el nodo correspondiente para mostrar sus tablas
y arrastra desde allí sobre la superficie del diseñador de Linq2SQL las que quieras
utilizar en el proyecto. Yo he empleado las de categorías, productos, proveedores,
clientes, pedidos y detalles de pedido, como se muestra en la figura 2.

Figura 2.- Las tablas a utilizar seleccionadas en el explorador de servidores

Tras haberlas arrastrado el modelo quedará definido en la superficie de diseño


y sólo tendrás que grabar el resultado para que todo esté listo para trabajar. En la
figura 3 se observa el aspecto del diseñador con las tablas ya añadidas.
122  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Figura 3.- El modelo de datos inicial con Linq2SQL.

4.- Añadiendo el modelo a Dynamic Data


Ahora abre el archivo Global.asax de la aplicación. Al hacerlo verás que contiene
un evento llamado RegisterRoutes. Su código comienza con un gran comentario en
el que se nos indica que para poder usar Dynamic Data con un modelo de datos
primero debemos registrarlo con el método RegisterContext de la clase MetaModel.
Esta clase se ocupa de registrar los modelos de datos para que sean utilizados por
Dynamic Data para la generación de interfaces y controles.
Descomenta la última línea de ese bloque de comentarios y utiliza nuestro recién
creado contexto de datos (NortwindDataContext) como parámetro para el método:

DefaultModel.RegisterContext(GetType(NorthwindDataContext), New
ContextConfiguration() With {.ScaffoldAllTables = True})

Fíjate en que el último atributo, ScaffoldAllTables lo hemos cambiado también


para que valga True en lugar de su valor original que era False. Con esto lo que le
estamos indicando a la infraestructura de Dynamic Data es que debe generar páginas
para todas las tablas de nuestro modelo.

Nota:
Para los propósitos de este ejemplo esto es lo que queremos. Más adelante veremos que no es
necesario usar esta parte de la tecnología si preferimos crear nuestras páginas de una manera
manual. En cualquier caso si lo habilitamos deberemos asegurarnos de que hemos protegi-
do adecuadamente el acceso a las rutas de administración autogeneradas, o si no estaríamos
abriendo el acceso a manejar las tablas a cualquiera. Así que ¡atención a esto!.
ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  123

Pulsa F5 para ejecutar la aplicación. Verás que automáticamente dispones de una


página inicial con una lista de tablas en el modelo y que puedes navegar por ellas.

Figura 4.- Vista previa de la interfaz de gestión generada por defecto

Si usas los enlaces verás que se ha creado una interfaz básica para gestionar
todas y cada una de las tablas que teníamos en el modelo. Para cada tabla se mues-
tran todos los campos disponibles (luego veremos cómo decidir cuáles mostrar), y
podemos editar los registros, borrarlos, ordenarlos por cualquier columna e incluso
añadir nuevos registros.
La tecnología es lo suficientemente inteligente como para mostrarnos las enti-
dades relacionadas entre sí usando elementos apropiados para el usuario final. Así,
por ejemplo, al editar un producto en el campo de proveedor obtenemos una lista
con los nombres de los proveedores y no tenemos que introducir el valor numérico
correspondiente a la clave externa, lo cual sería poco operativo en una interfaz de
administración.
También se crean filtros para poder acotar los listados a partir de otras entidades
relacionadas y campos boolenaos. En la figura 4 se puede intuir a qué me refiero, y
vemos que en la parte de arriba aparece la lista de categorías de productos desple-
gada, para que podamos filtrar por ella. Los filtrados se hacen al estilo AJAX, es
decir, sin recarga de la página.
Aunque así todavía no es suficiente, no está mal para sólo haber arrastrado unas
tablas y descomentado una línea ¿verdad?.
124  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

5.- Plantillas de interfaz de usuario


Observemos someramente los elementos presentes en el proyecto para ver cómo
funciona el proceso por debajo. La primera página que podemos ver es “Default.
aspx”. En realidad no tenemos porqué usarla, pero se proporciona para tener un punto
de partida desde el que acceder a las páginas auto-generadas. Lo único que hace esta
página es mostrar la lista de tablas disponibles con un enlace a la página de listado
de cada una de ellas. Si nos sabemos las rutas a las páginas (más sobre esto luego),
no nos hace falta en absoluto. Échale un vistazo no obstante al código del evento
Load de la página para ver cómo enlaza el modelo a una rejilla para sacar el listado
de las tablas: nunca se sabe cuando lo puedes necesitar.

Nota:
En ASP.NET 4.0 se generan los enlaces con un nuevo control especial llamado DynamicHy-
perLink. En la versión 3.5SP1 se usan enlaces normales.

Lo realmente interesante es la carpeta DynamicData existente en la raíz de la


aplicación. En ella vemos que hay varias subcarpetas llenas de páginas y controles.

Figura 5.- La carpeta Dynamic Data


ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  125

La subcarpeta PageTemplates contiene una serie de páginas ASPX que serán las
que se utilicen como plantillas para la interfaz de gestión que hemos visto, generada
automáticamente por Dynamic Data:

• List.aspx: genera la página principal, que lista todos los registros de la tabla
y brinda acceso a las demás páginas: edición, inserción o detalles.

• Details.aspx: muestra los detalles de una entidad.

• Edit.aspx: genera un interfaz que permite editar cualquier entidad/tabla.

• Insert.aspx: la plantilla para insertar un nuevo registro.

• ListDetails.aspx: esta es la plantilla más completa de todas, puesto que aúna


en una sola página las funcionalidades de las otras cuatro. Útil si queremos
tener todo junto.

Si las examinamos veremos que son páginas ASPX normales, y que apenas
contienen código. Podemos modificarlas a nuestro antojo para adaptarlas a lo que
necesitemos. Cualquier cambio o añadido que hagamos en ellas se presentará en
la interfaz autogenerada en tiempo de ejecución. Todas ellas utilizan como Master
Page a Site.master, ubicada en la raíz de la aplicación, por lo que modificando ésta
afectaremos a la disposición de elementos en todas las plantillas.
Las cinco páginas se parecen mucho, así que vamos a examinar una de ellas para
ver su estructura y comprender mejor su funcionamiento.

5.1.- Diseccionando una plantilla


Por ejemplo, vamos a analizar “List.aspx”, la página que permite listar los registros
de cualquier tabla de datos.
Lo primero que examinaremos será su código, contenido en List.aspx.vb o List.
aspx.cs según uses VB o C# como lenguaje de desarrollo.
El código define un variable protegida de ámbito de página llamada table y
de tipo MetaTable. Esta clase System.Web.DynamicData.Metatable encapsula los
metadatos de una tabla/entidad que está siendo manejada por Dynamic Data. A
través de sus miembros tenemos acceso a la información sobre el modelo de datos
subyacente. Así, por ejemplo, podemos averiguar el nombre de la tabla con su pro-
piedad DisplayName, o conocer los detalles de sus campos a través de su colección
Columns. La referencia completa la tienes aquí:

http://msdn.microsoft.com/es-es/library/system.web.dynamicdata.metata-
ble_members.aspx

En el evento Load de la página/plantilla se asigna a este miembro, table, los


metadatos de la tabla actualmente manejada en la plantilla, de forma que se pueda
126  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

hacer uso de ella desde cualquier parte de la página para mostrar información sobre
la entidad gestionada. En la versión 3.5 SP1 el código de asignación es este:
table = GridDataSource.GetTable

GridDataSource es como se llama el control LinqDataSource o EntityDataSource


(según la tecnología de datos que hayamos elegido) que se encarga de hacer el
acceso a los datos en la plantilla. Estos controles han sido extendidos con algunas
propiedades y métodos como este (GetTable) para dar soporte a Dynamic Data.
En ASP.NET 4.0 el código equivalente utilizado es el siguiente:
table = GridQueryExtender.SetTableFromRoute

En ese caso se usa el método SetTableFromRoute de un control QueryExtender


incluido en la página para hacer filtrado de datos (ver capítulo correspondiente en
este libro). Lo que hace es obtener una referencia al modelo de tabla apropiado
basándose en la ruta actual de la página (ver epígrafe 8 de este capítulo), pero es
exactamente lo mismo que el código anterior. De hecho podríamos haber usado el
mismo aquí también en lugar de este.
Los metadatos de la tabla nos ofrecen el método GetActionPath, que nos informa
de la ruta apropiada para realizar las distintas acciones sobre la entidad. Así, por
ejemplo, para obtener la ruta que genera la página de inserción para la entidad actual
podríamos usar:
table.GetActionPath(PageAction.Insert)

Lo mismo con el resto de acciones: edición (PageAction.Edit), detalles (PageAc-


tion.Details) y listado de registros (PageAction.List). En ASP.NET 4.0 esto no es
necesario porque el nuevo control DynamicHyperLink se encarga de esto automáti-
camente como veremos ahora mismo
Analicemos ahora el marcado HTML de página.
Si analizas los controles incluidos verás que, quitando un par de ellos, todos
los demás son controles normales de ASP.NET, como una rejilla y un control Da-
taSource.
El primer control que nos encontramos es un DynamicDataManager. La funcio-
nalidad de Dynamic Data se basa en el uso de unos cuantos controles especiales que
habilitan el enlazado automático con el modelo de datos. Como ya he comentado en
la introducción, algunos controles como las rejillas se han modificado para soportar
enlazado dinámico a datos y de esta manera generar listados y editar registros ba-
sándose en las plantillas. El control DynamicDataManager es un control no visual
cuya función precisamente es habilitar este soporte dinámico en ciertos controles de
la página. En concreto las tareas que realiza son:

• Generar en los controles GridView o DetailsView las columnas necesarias para


que se puedan mostrar o editar los campos de la tabla/entidad de datos.
ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  127

• Ajustar el tipo de contexto de datos y el nombre de la tabla en el control


LinqDataSource o EntityDataSource para que puedan trabajar con la tabla
de datos correcta.
• Establecer los parámetros de filtrado (WhereParameters) en el control origen
de datos para poder hacer filtros.
• Cargar automáticamente las claves externas en el caso de modelos Linq.
Si miras un poco más abajo en el código verás una rejilla, GridView1, que será
la encargada de listar los registros de datos, función principal de esta página. En
este caso, como este es el control que utilizaremos con Dynamic Data, deberá ser
activado para que soporte los controles dinámicos, por eso la plantilla tiene este
marcado:
<asp:DynamicDataManager ID=”DynamicDataManager1” runat=”server”
AutoLoadForeignKeys=”true”>
<DataControls>
<asp:DataControlReference ControlID=”GridView1” />
</DataControls>
</asp:DynamicDataManager>

Lo que estamos indicando de manera declarativa es que el control DynamicData-


Manager debe habilitar el soporte para controles dinámicos en la rejilla GridView1.
Esto es lo que activa realmente toda la funcionalidad de la rejilla, pues a partir de
este momento el control de origen de datos asociado a ésta (un LinqDataSource
en nuestro ejemplo) ya sabrá qué datos debe manejar, y los controles dinámicos
contenidos en la rejilla podrán mostrar la información.
De hecho la rejilla está configurada para generar automáticamente las columnas
a partir de todos los campos que haya en la entidad de datos que maneje. Los dos
elementos definidos en su <itemTemplate> son simples enlaces para editar y ver los
detalles del registro actual. El resto de los campos se genera al vuelo.
Una cuestión importante a tener en cuenta si utilizas ASP.NET 3.5 SP1 es que,
en esta primera versión de Dynamic Data, no estaba soportada la activación decla-
rativa de los controles como acabamos de ver, y debía activarse mediante código al
inicializar la página, así:
Protected Sub Page_Init(ByVal sender As Object, ByVal e As
EventArgs)
DynamicDataManager1.RegisterControl(GridView1, True)
End Sub

Lo que sería equivalente al marcado anterior. Tenlo en cuenta si intentas repro-


ducir estos ejemplos en una versión anterior a Visual Studio 2010.
La paginación de la rejilla se efectúa con un control llamado <asp:GridViewPager>.
No lo busques en el modelo de objetos de ASP.NET pues no es un control estándar a
pesar de que lleva el prefijo asp (deberían haber elegido otro más claro). En realidad
128  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

es un control de usuario que encontrarás en la subcarpeta “DynamicData\Content”.


Por defecto incluye botones para avanzar por las páginas, un cuadro de texto para
saltar a una página directamente, y una lista desplegable para elegir el número de
elementos por página.

Figura 6.- Barra de paginación de los listados automáticos

Si editamos este control (GridViewPager.ascx) podremos personalizar com-


pletamente el aspecto y funcionalidad de la paginación de registros. Los gráficos
utilizados están en esa misma carpeta dentro de “Images”.
Especial mención merece la forma en la que se generan los filtros automáticos
para el listado.
El control utilizado es un QueryableFilterRepeater. Este control no lo encontra-
rás en la barra de herramientas de Visual Studio y es necesario añadirlo a mano a la
página. Su función es la de generar los controles que pueden usarse para filtrar los
registros de la rejilla. Por defecto se generan controles en forma de lista desplegable
para todos los campos que son una clave externa de la tabla y para los campos
booleanos. Este control repetidor se usa para filtrar datos en el DataSource corres-
pondiente usando para ello un control QueryExtender (ver capítulo 7).

Nota:
En la primera versión de Dynamic Data, en ASP.NET 3.5 SP1, no existen estos controles, por lo
que se utiliza en su lugar un control FilterRepeater. Este control proporciona directamente al
origen de datos una lista de parámetros Where que realizan el filtro, ya que no existe tampoco
el control QueryExtender, disponible sólo a partir de ASP.NET 4.0.

En la figura 7 podemos ver los filtros automáticos que se han generado para la
tabla de productos que son tres: uno para el único campo booleano (si el producto
está o no descatalogado) y otros dos para las tablas relacionadas con la actual (ca-
tegorías y proveedores).

Figura 7.- Filtros automáticos para tablas.


ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  129

El control desplegable se puede cambiar por cualquier otro o también es posible


modificar su comportamiento partiendo del original. Para ello podemos modificar la
plantilla apropiada dentro de la subcarpeta “Filters” que se ve en la figura 5 y que,
por defecto, contiene una plantilla para los booleanos, otra para las enumeraciones
y una tercera para las tablas relacionadas, que es la que se ha utilizado aquí.

Nota:
En la versión 3.5 SP1 no existe la carpeta “Filters” y hay, sin embargo, un control en la carpeta
“Content” que se llama “FilterUserControl.ascx” y tiene el mismo propósito

El resto de las páginas/plantillas son tremendamente parecidas, por lo que no te


costará mucho comprender su funcionamiento y poder manipularlas a tu antojo.
Asegúrate de entenderlas bien porque, al hacerlo, tendrás casi todo lo que nece-
sitas para crear tus propias páginas que saquen partido a Dynamic Data, pero sin
tener que usar la generación automática de código. Luego volveremos sobre ello.

6.- Plantillas para entidades


Una de las cosas que probablemente te llamará la atención cuando analices las planti-
llas anteriores es que, en las páginas de detalles, la edición o la inserción de registros,
se emplea un control llamado DynamicEntity. Se trata de una nueva característica
introducida en la versión 4.0 de la plataforma que permite tener mayor control sobre
la forma de generar las interfaces de trabajo con elementos individuales.
En la primera versión de Dynamic Data los registros individuales se mostraban
tal cual estaban definidos en las plantillas “Details.aspx”, “Edit.aspx” e “Insert.
aspx”. Desde la versión 4.0 es posible hilar más fino y definir plantillas de edición
especiales para cada tipo de entidad.
Para ello en la carpeta “EntityTemplates” hay tres plantillas para trabajar con
entidades individuales: visualización (“Default.ascx”), edición (“Default_Edit.ascx”)
e inserción (“Default_Insert.ascx”) respectivamente. Éstas contienen el código de
marcado que se usará para realizar cada una de estas acciones con todos los tipos
de entidades.
Podemos crear nuestras propias plantillas especiales para entidades concretas
con tan solo crear controles análogos a estos con el nombre de la entidad/tabla.
Por ejemplo, para ajustar a nuestra voluntad el trabajo con los registros de la tabla
de productos podemos crear los controles: “Products.ascx”, “Products_Edit.ascx” y
“Products_Insert.ascx”. No es obligatorio crear los tres, sólo los que nos interesen.
En tiempo de ejecución los controles DynamicEntity de las páginas plantilla se
sustituyen por el control apropiado de la carpeta “EntityTemplates”.

7.- Plantillas para campos


Mucho más interesantes que las plantillas de páginas son las plantillas para campos.
Cada campo de las tablas en la base de datos se corresponde con una propiedad en
alguna de las entidades del modelo de datos. Para visualizar y editar los valores
130  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

de los campos, Dynamic Data utiliza para cada una el control que le parece más
apropiado. Así, por ejemplo, para campos de texto usará un TextBox para editar
el campo, y un control Literal para visualizarlo. En el caso de los booleanos se
usarán marcas de verificación (CheckBox), que estarán deshabilitadas en el caso de
visualización.
Dentro de la carpeta “DynamicData/FieldTemplates” están las plantillas utiliza-
das para los tipos de datos soportados por defecto.

Figura 8.- Plantillas de campos de datos dinámicos

Son todos controles de usuario. Disponemos de un par de plantillas para cada


tipo de campo: una para mostrarlo y otra para editarlo, siendo estas últimas las que
llevan el sufijo _Edit. Si abrimos cualquiera de ellas veremos que son controles
de usuario normales, que llevan controles Web comunes dentro. Lo único un
ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  131

poco especial es el código de inicialización que llevan, ya que heredan de la clase


FieldTemplateUserControl, y hacen uso de algunos de sus métodos para configurar
los controles Web que contienen. Examina con un poco de calma el código y verás
que no tiene dificultad alguna.
Por defecto disponemos de controles para campos booleanos, fecha y hora, nú-
meros con decimales, números enteros, texto multi-línea, texto de una sola línea,
direcciones de correo electrónico y URLs (estos dos últimos sólo a partir de ASP.
NET 4.0).
Fíjate en que existen varias plantillas especiales:
• Children.aspx: genera un enlace para ver los registros relacionados con
el registro actual en una relación de tipo uno a muchos (maestro-detalle).
Por ejemplo en una tabla de pedidos enlazaría con el listado de las líneas
de pedido.
• Children_Insert.aspx: para facilitar la selección de una entidad hija al editar
un registro. No está disponible en la primera versión de Dynamic data.
• Enumeration.ascx: si a uno de los campos de tipo entero del modelo le
asignamos como tipo de datos una enumeración que tengamos definida en
el código y que mapee los posibles valores para el campo, Dynamic Data
usará esta plantilla. Así, a la hora de visualizar el campo mostrará el nombre
apropiado de la enumeración en lugar de un simple número. Esta plantilla y
la siguiente no están disponibles en ASP.NET 3.5 SP1, pero funcionarán si
las creas a mano.
• Enumeration_Edit.ascx: idem que la anterior pero para facilitar la edición
del campo eligiendo el valor desde una lista desplegable.
• ForeignKey.aspx: genera un enlace para ver los detalles de un registro
relacionado de tipo uno a uno. Por ejemplo en el caso de un pedido para
visualizar los detalles del cliente asociado con éste.
• ForeignKey_Edit.aspx: facilita la selección de un registro relacionado me-
diante una lista desplegable a la hora de editar el registro principal.
Si cambiamos cualquiera de estas plantillas conseguiremos modificar su com-
portamiento y/o aspecto en todos los campos de la base de datos que hagan uso
de ella.
Por ejemplo, vamos a modificar la plantilla para edición de fechas de modo que
nos ofrezca un calendario desplegable en lugar de tener que escribirlas a mano. Abre
el control “DateTime_Edit.ascx” y ponlo en modo de diseño para que se vea su
aspecto en el editor. Selecciona el cuadro de texto, deshabilítalo poniendo su propie-
dad Enabled a False, y en su lista de tareas añade un control extensor (necesitarás
tener instalado el AJAX Control Toolkit para que funcione). En la lista de extensores
escoge el CalendarExtender. Graba el control y ejecuta de nuevo la aplicación.
132  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Ahora verás como cuando edites un registro que tenga fechas (por ejemplo un
pedido de la tabla Orders) aparecerá un calendario desplegable para facilitar su
selección.
Juega un poco con estas plantillas para aprender bien a tomar control sobre ellas.
Más adelante en este capítulo estudiaremos cómo podemos crear plantillas propias
para ciertos campos de la base de datos o para tipos de datos que no son reconocidos
por Dynamic Data.

8.- Las rutas de las páginas dinámicas


Si te fijas bien en las URL de las páginas autogeneradas por Dynamic Data, verás
que se corresponden con rutas análogas a las siguientes:
http://localhost:xxxx/DynamicDataEjemplo/Products/List.aspx
http://localhost:xxxx/DynamicDataEjemplo/Products/Edit.
aspx?ProductID=1
http://localhost:xxxx/DynamicDataEjemplo/Customers/Insert.aspx

Puedes comprobar que ninguna de estas rutas existe en nuestra aplicación. Es


decir, apuntan a carpetas y archivos imaginarios.
En realidad son rutas ficticias que la infraestructura de ASP.NET se encarga
de redirigir a recursos reales en el servidor para procesarlas. En este caso las rutas
ficticias apuntan a las plantillas ASPX que hemos visto en los apartados anteriores.
Con esta técnica de enrutado conseguimos un doble objetivo:
1. Los usuarios obtienen rutas fáciles de recordar para efectuar operaciones
con las entidades.
2. A través de la propia ruta se le pasa información de contexto a las plantillas
para que sepan con qué entidad deben trabajar y qué acción van a efectuar, ya
que en la propia URL tiene el nombre de la entidad, la acción y, si procede,
la clave primaria del registro.
Las rutas se definen en Global.asax, en el mismo evento RegisterRoutes que
utilizamos en el apartado 4 de este capítulo. Si vuelves sobre el mismo verás que,
por defecto, viene ya una ruta definida con el siguiente código:
routes.Add(New DynamicDataRoute(“{table}/{action}.aspx”) With
{ .Constraints = New RouteValueDictionary(New With {.Action =
“List|Details|Edit|Insert”}), .Model = DefaultModel})

Lo que se hace aquí es añadir una nueva ruta ficticia para que ASP.NET la inter-
prete y se la asigne a Dynamic Data para trabajar. El constructor de la ruta dinámica
toma como parámetro una cadena en la que se marca con comodines la información
que ésta contendrá. En este caso se indica que las rutas, siempre consideradas desde
la raíz de la aplicación, tendrán la siguiente forma:
NombreTabla/Accion.asppx
ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  133

Como podemos comprobar esto se corresponde con las rutas que he puesto de
ejemplo en el párrafo anterior, que servían respectivamente para listar un producto,
editarlo y para insertar un nuevo cliente (fíjate en cómo llevan el nombre de la
entidad y la acción en la URL).
También junto con el constructor se ajustan algunas restricciones de ámbito para
de la ruta a través de la clase RouteValueDictionary:
• Action: define las acciones sobre entidades a las que responderá esta ruta.
En este caso se han incluido todas las posibles, o sea, listar, ver detalles,
editar e insertar.
• Model: el modelo de metadatos que se gestionará con esta ruta. Podemos
tener más de uno. En nuestro ejemplo usamos el que habíamos definido al
principio.
• Tables: no está presente en la línea de ejemplo, pero nos permite establecer
qué tablas en concreto del modelo se van a gestionar con esta ruta. Si no
lo indicamos se usa para todas las tablas contenidas en el modelo. Luego
veremos un ejemplo.
La ruta la podemos construir a nuestra voluntad. Por ejemplo, vamos a cambiar
la ruta por defecto para que sea un poco más estilo REST (http://es.wikipedia.org/
wiki/REST). Cambia la definición de la ruta por esta cadena:
{action}/{table}

Y deja lo demás igual. Ejecuta la aplicación.

Figura 9.- Nuevas rutas estilo REST para las páginas dinámicas

A partir de ahora verás como las rutas han cambiado por completo y que ni
siquiera llevan una extensión .aspx ni apuntan a páginas ficticias concretas. Son más
134  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

fáciles de recordar todavía para los usuarios pues éstos sólo tienen que saber qué
acción quieren efectuar y sobre qué tabla.
El manejo de rutas es una técnica muy potente y podemos hacer muchas otras
cosas.
Recordarás que entre las plantillas existía una llamada “ListDetails.aspx” que en
realidad no se estaba usando en ningún lado. Cuando la mencioné dije que era una
página “todo en uno” ya que en ella teníamos todo lo necesario para gestionar una
entidad: listado, creación, edición y borrado. Pues vamos a usarla en lugar de las
páginas individuales que hemos venido usando hasta ahora.
Convierte en un comentario la definición de la ruta que acabamos de hacer y des-
comenta la siguiente ruta que encontrarás ya preparada en el evento RegisterRoutes
en el ejemplo descargable:
routes.Add(New DynamicDataRoute(“{table}/Mantenimiento”) With { _
.Action = PageAction.List, _
.ViewName = “ListDetails”, _
.Model = DefaultModel })

Ejecuta la aplicación. Verás que la página generada para cada entidad tiene todo
lo necesario para gestionarla por completo, ya que desde la misma página -y estilo
AJAX- podemos editar, crear, borrar y ver el detalle de cualquier registro de cual-
quier tabla de nuestro modelo. Y lo mejor de todo es que para que un usuario acceda
al mantenimiento de cualquier tabla lo único que tiene que hacer es poner el nombre
de la misma seguido de la palabra “Mantenimiento”, por ejemplo:
http://localhost/DynamicDataEjemplo/Suppliers/Mantenimiento

¡No está nada mal! ¿verdad?


En este caso hemos indicado sólo el comando pageAction.List ya que con la
plantilla que hemos seleccionado todos los comandos se gestionan desde esa página.
Por eso tampoco tenemos la acción especificada en la ruta.
Las rutas se pueden utilizar para definir nuestras propias plantillas de trabajo
para las entidades. Por ejemplo, imagina que creamos una plantilla propia que hace
la edición de las entidades de algún modo especial y queremos usarla en lugar de
las estándar. Si a la plantilla le hemos llamado “MisDetalles.aspx” y la tenemos
en la carpeta “DynamicData”, junto con la demás, podemos definir la ruta de esta
manera:
routes.Add(New DynamicDataRoute(“{table}/Mantenimiento”) With { _
.Action = PageAction.List, _
.ViewName = “MisDetalles”, _
.Model = model})

Es decir, sólo cambiamos el modelo de la vista y ya funcionaría. En el proyecto


demo descargable se ha incluido este ejemplo también.
ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  135

Es posible crear plantillas específicas para tablas concretas. Para eso está la
carpeta “DynamicData/CustomPages”. Para crear plantillas especiales para una en-
tidad debemos crear una subcarpeta dentro de “CustomPages” con el nombre de la
tabla en la base de datos e incluir dentro las plantillas específicas que necesitemos.
Serían plantillas normales, como las que hay en la raíz, pero se usarían sólo para esa
entidad. Si falta alguna acción se seguiría usando la plantilla por defecto.
En el proyecto de ejemplo descargable de la Web he personalizado las plantillas
que se usarán con la tabla de productos, que están en la carpeta “DynamicData/
CustomPages/Products”. De esta forma al entrar en cualquier acción de los productos
se usarán estas plantillas en lugar de las predeterminadas.

8.1.- Parámetros individuales en las rutas


En las rutas personalizadas que se refieren a un único registro, por ejemplo las rutas
de edición, éste se le pasa como un parámetro a la página de forma análoga a esta:
http://localhost:xxxx/DynamicDataEjemplo/Products/Edit.
aspx?ProductID=1

Podemos conseguir una URL con cualquier otra forma usando el nombre de
la clave primaria como comodín en la ruta personalizada. En este caso hay que
especificar la tabla concreta que queremos utilizar, ya que cada entidad maneja
claves primarias diferentes.
Por ejemplo, podemos conseguir que los productos se editen con una ruta estilo
REST, especificando el ID al final de la misma, usando esta ruta:
routes.Add(New DynamicDataRoute(“Products/Mantenimiento/{ProductID}”)
With { _
.Action = PageAction.Edit, _
.ViewName = “Edit”, _
.Model = DefaultModel, _
.Table = “Products”})

que nos daría rutas como la siguiente, mucho más claras:


http://localhost:xxxx/DynamicDataEjemplo/Products/Mantenimiento/1

Esta ruta abriría directamente el producto con ID = 1 en modo de edición.


Fíjate en que en esta ruta hemos empleado una propiedad adicional llamada
Table, que ata la ruta a una entidad concreta.
Como comentario final sobre las rutas he de añadir que es muy importante el
orden en el que las definamos dentro de Global.asax. Las rutas se evaluarán se-
cuencialmente en el orden en que fueron definidas. En cuanto se encuentre una que
coincida se parará el procesamiento del resto de rutas. Por ello hay que ser muy
cuidadosos cuando las agreguemos para evitar que unas “tapen” a las otras y no
consigamos el efecto buscado.
136  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

9.- Ampliando los metadatos del modelo


Sin que tengamos que hacer nada especial, Dynamic Data ya se ocupa de efectuar
multitud de validaciones por nosotros: que se introduzcan valores del tipo apropiado,
que los campos que no admiten nulos sean obligatorios, etc... Toda esta información
la obtiene directamente del modelo de datos, ya que tanto Linq2SQL como Entity
Framework incluyen metadatos para indicar este tipo de restricciones.
Sin embargo raramente estas restricciones por defecto van a ser suficientes. Hay
una serie de restricciones que no se pueden extraer de la base de datos y por lo
tanto del modelo. Se trata de reglas de negocio que se deducen de los requerimientos
específicos de nuestra aplicación. Los hay generales (por ejemplo, un producto no
puede tener stock negativo) y particulares de nuestro desarrollo (nuestros requisitos
del proyecto nos indican que no podemos descatalogar un producto que todavía
tenga stock).
Estas reglas de negocio las podemos incluir en el modelo de datos para forzar su
cumplimiento a la hora de introducir o editar registros.
Existe un espacio de nombres llamado System.ComponentModel.Annotations,
que ofrece una serie de atributos especiales destinados a proporcionar información
extra a DynamicData acerca del modelo de datos.

Figura 10.- El espacio de nombre Annotations y sus atributos.

Gracias a estos atributos podremos indicar a Dynamic Data mucha información


extra útil para generar la interfaz. Ahora mismo veremos la manera de hacerlo.
Si abres el archivo Northwind.designer.vb de nuestro modelo de datos Linq2SQL
verás el código autogenerado para describir las tablas y efectuar todas las operacio-
nes con la base de datos. Busca la definición de alguna de las entidades, por ejemplo
Product, y comprobarás que se trata de una clase parcial.
ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  137

Nota:
Las clases parciales son una interesante característica de los lenguajes .NET que permite de-
finir una clase separando el código de la misma en dos o más archivos diferentes. Estas clases
están pensadas para que varios programadores puedan implementar partes de la clase de forma
independiente unos de otros y, sobre todo, para poder complementar el código generado por
herramientas automáticas. Este es el caso que nos ocupa ahora. Así, podemos ampliar las capa-
cidades de nuestras clases que representan entidades usando un archivo aparte. De este modo
si debemos regenerar el modelo porque ha habido algún cambio, las modificaciones y añadidos
hechos en el archivo independiente no se verán afectados, cosa que sí ocurriría si tocamos
directamente el código original.

Vamos a ampliar esa clase parcial para darle información adicional a la que trae el
modelo. Crea una nueva clase con el nombre Product dentro de la carpeta App_Code.
Asegúrate de ponerle la palabra clave Partial en su definición. Esta representará a
la misma entidad Product que hay en nuestro modelo. Añade los siguientes espacios
de nombres en la parte de arriba del archivo:
Imports System.Data.Linq
Imports System.Web.DynamicData
Imports System.ComponentModel.DataAnnotations

(En C# sería con la palabra clave using).


Por desgracia no podemos utilizar los atributos directamente sobre esta clase par-
cial que hemos creado, ya que las propiedades que representan a los campos de la base
de datos no pueden redefinirse (ya están definidas en el código autogenerado y no
son “propiedades parciales”). Por ello tendremos que hacerlo indirectamente. El modo
de conseguirlo es crear otra clase “de paja” que realmente sólo servirá para aplicar
atributos a campos y ofrecer información al modelo. Enseguida lo entenderemos.
Vamos a decorar la clase con un atributo especial: MetaDataType. Éste sirve
para indicar el nombre de esa clase auxiliar sobre la que aplicaremos los atributos
informativos. La definición de nuestra clase parcial para la entidad quedaría así:
<MetadataType(GetType(MetaDataDeProducto))> _
Partial Public Class Product

End Class

La clase MetaDataDeProducto todavía no existe, pero la vamos a crear justo a


continuación. El nombre que le demos es indiferente, sólo tiene que coincidir con
el que hayamos indicado en el atributo MetaDataType. En ella crearemos campos
con el mismo nombre que los campos de la tabla del modelo. Éstos nos servirán
simplemente como medio para aplicar atributos diversos y proporcionar información
a Dynamic Data. Pensemos en esta nueva clase como una clase “tonta” que sólo
sirve para mapear campos del modelo con características de éste. Por ejemplo,
138  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

vamos a definir dos campos de la tabla de productos para que sean excluidos de la
generación automática:
Class MetaDataDeProducto

<ScaffoldColumn(False)> _
Public ReorderLevel As Object

<ScaffoldColumn(False)> _
Public UnitsOnOrder As Object

End Class

Fíjate en que simplemente hemos creado dos miembros con el mismo nombre
que tienen sendos campos de la tabla productos. El tipo que se indique para éstos
va a ser indiferente, puesto que Dynamic Data sólo se fijará en su nombre y en los
atributos que los decoren, por eso hemos puesto Object.
A cada uno de estos miembros le podemos asignar tantos atributos del espacio de
nombres Annotations (figura 10) como sean necesarios. En el listado anterior lo que
estamos indicando con el atributo ScaffoldColumn es que Dynamic Data no debe
usar estos dos campos de datos cuando genere las interfaces, ni para visualizar ni
para editarlos. Ejecuta la aplicación y vete a la tabla de productos Verás que ambos
campos han desparecido.

9.1.- Mejorando la validación de campos


Existen tres atributos de validación que podemos utilizar y que nos ayudarán a
incluir condiciones adicionales a las que tiene el propio modelo de datos:
• RangeAttribute: especifica un rango de valores posibles para un campo. Es
posible indicar también el mensaje a mostrar si el valor indicado excede el
intervalo.
• RegularExpressionAttribute: indicaremos una expresión regular que deberá
cumplir el texto que se asigne al campo. Muy útil para indicar expresiones
comunes, como la sintaxis de un email o el formato de un teléfono.
• RequiredAttribute: para forzar a que un campo sea obligatorio. Normalmen-
te no se usa mucho porque los campos obligatorios suelen estar marcados
como no nulos en la base de datos, pero puede venir bien si no lo hemos
marcado por algún motivo (por ejemplo porque tratamos los valores nulos de
forma especial en el programa pero no dejamos que los usuarios los puedan
introducir).
Otros atributos, sin ser de validación, que podemos usar son:
• DataTypeAttribute: para indicar el tipo específico del dato de entre los dis-
ponibles en la enumeración DataType. Podemos ver sus valores a la derecha
ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  139

de la figura 10. Así podemos indicar que un campo no es simplemente de


texto (que es lo que se sacaría del modelo de datos) sino que es además
específicamente una clave de acceso o va a guardar URLs, con lo que la
interfaz se podría especializar para responder a estos requisitos. Por ejemplo,
si indicamos que es un email se usará la plantilla correspondiente que hemos
visto en el apartado 7 de este capítulo.
• StringLengthAttribute: para indicar la longitud máxima de una cadena.
• DisplayColumnAttribute: sirve para especificar qué campo debemos usar
para mostrar los detalles de una entidad asociada con la nuestra mediante una
clave externa. Por ejemplo el nombre del proveedor en lugar de su ID cuando
visualicemos un campo IDProveedor en una tabla relacionada.
• DisplayFormatAttribute: formato para usar al visualizar el dato.
• UIHintAttribute: nos permite especificar la plantilla a utilizar para visualizar
y editar el campo actual. Nos sirve para editar algunos campos de modo
especial o, directamente, para definir cómo se deben editar cuando son de
tipo desconocido. Más sobre esto luego.
Así, por ejemplo, si queremos indicar que el precio de un producto no puede ser
nunca menor que cero, podemos añadir este campo a la clase auxiliar:
<Range(0, Integer.MaxValue, ErrorMessage:=”El precio no puede ser
menor que cero.”)> _
Public UnitPrice As Object

Con lo que se validará automáticamente esta condición desde todos los lugares
de la interfaz de usuario que usen Dynamic Data.
La clase auxiliar para pedidos la podemos definir del siguiente modo:
Public Class MetaDataDePedidos

<DataType(DataType.Date)> _
Public OrderDate As Object

<DataType(DataType.Date)> _
Public RequiredDate As Object

<DataType(DataType.Date)> _
Public ShippedDate As Object

<DisplayFormat(DataFormatString:=”{0:F} Kg”,
NullDisplayText:=””)> _
Public Freight As Object

End Class
140  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Con lo que conseguiremos que las fechas de los pedidos se muestren exclusi-
vamente como fechas (sin la hora, que también está en la base de datos), y que el
peso del pedido se visualice en kilogramos, usando una cadena vacía en caso de
que alguno contenga un nulo.

9.2.- Validaciones personalizadas


Adicionalmente todas las excepciones que generemos desde el modelo de datos serán
capturadas y gestionadas por el validador dinámico en la interfaz de usuario. Esto
nos permite añadirle validaciones mucho más complejas a las entidades escribiendo
un poco de código.
El lugar apropiado para realizar la validación es en la clase parcial que hemos
creado antes.
Si abrimos el código del modelo de datos (la clase parcial xxxx.designer.vb de la
que hablamos antes), veremos que todas las entidades tienen una sección (“Defini-
ción de métodos de extensibilidad”) que contiene multitud de métodos parciales con
nombres típicos de eventos (figura 11).

Figura 11.- Métodos de extensibilidad de las entidades

Todos ellos son métodos parciales. Esto quiere decir que si no los implementamos
en otra parte de la clase, el compilador los elimina ya que no se van a usar jamás.
Pero si los implementamos se podrán utilizar durante la ejecución del programa.
Si nos fijamos bien veremos que hay dos métodos especiales (OnValidate y
OnCreate para la validación global de la clase y la creación de la misma) y que
el resto de métodos se corresponden con el nombre de los campos de la entidad y
hay dos por cada campo. Los que se llaman OnXXXXChanging son métodos que
ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  141

se llamarán automáticamente justo antes de cambiar el valor de la propiedad, y los


OnXXXXChanged se llamarán tras haber cambiado el valor.
Sabiendo esto añadir código de validación especial para un campo es una tarea
sencilla, pues basta con implementar el método parcial correspondiente al cambio
de valor de éste. Por ejemplo, para impedir mediante código que los stocks puedan
ser negativos escribiríamos esto:
<MetadataType(GetType(MetaDataDeProducto))> _
Partial Public Class Product
Private Sub OnUnitsInStockChanging(ByVal value As
System.Nullable(Of Short))
If value < 0 Then
Throw New ArgumentOutOfRangeException(“Los stocks no
pueden ser negativos”)
End If
End Sub
End Class

A partir de ahora se mostrará el mensaje indicado cuando un usuario intente


introducir un valor de inventario negativo en algún producto.
Estos eventos no nos permiten validar unos campos en relación con otros de la
misma entidad porque puede que no estén todavía establecidos cuando se ejecuta el
evento (se van estableciendo por orden). Si queremos conseguir una validación de
este tipo -en la que entren en juego varios campos- deberemos usar el método parcial
para la validación general de la clase, OnValidate:
Private Sub OnValidate(ByVal action As System.Data.Linq.ChangeAction)
If action = ChangeAction.Insert Or action = ChangeAction.Update Then
If Me.Discontinued And Me.UnitsInStock > 0 Then
Throw New ArgumentOutOfRangeException(“No se puede
asignar stock distinto de cero a productos descatalogados.”)
End If
End If
End Sub

Con este código comprobamos si estamos en una inserción o actualización, y en


ese caso nos aseguramos de que si un producto está descatalogado no pueda tener
productos en el almacén.
Todas estas excepciones se convertirán en validaciones en la interfaz del usuario,
por lo que disponemos de un mecanismo muy potente para crear reglas de negocio
directamente en el modelo de datos. Éstas se cumplirán en todo momento independien-
temente de donde se usen posteriormente las entidades, sea con Dynamic Data o no.

10.- Plantillas de campos propias


Como hemos visto, dentro de la carpeta “DynamicData/FieldTemplates” están defini-
dos los controles para visualizar y editar los tipos de datos más comunes. No obstante
en muchos casos estas plantillas de controles serán insuficientes y necesitaremos
142  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

editar los datos de alguna forma especial o bien habrá algún tipo de dato que no sea
reconocido. En estos casos podemos crear nuestras propias plantillas de campos.
Lo primero que tenemos que decidir es el nombre de la plantilla, ya que será
la forma que tenga Dynamic Data de reconocerla y usarla. Como ya hemos visto,
pueden existir dos variantes de una plantilla de campo: para visualización o para
edición. Ambas llevan el mismo nombre sólo que la de edición va seguida del sufijo
_Edit.
Supongamos que queremos crear una plantilla que nos permita editar algunos
campos que pueden contener HTML. En lugar de usar el control de texto normal
podemos usar un control editor de HTML.
En el código de ejemplo descargable he creado un campo personalizado llamado
HTMLText que permite visualizar y editar campos HTML. Para ello tengo dos archi-
vos: “HTMLtext.ascx”, que sirve para visualizar, y “HTMLText_Edit.ascx” que sirve
para editar el contenido. Ambos están en la carpeta “FieldTemplates” del proyecto.
El control de visualización es un control Literal que mostrará cualquier HTML
que le indiquemos en su propiedad Text. La única cuestión a tener en cuenta es que
usaremos la propiedad FieldValue en lugar de la más habitual FieldValueString
del control FieldTemplateUserControl del que hereda. El motivo es que la segunda
devuelve la cadena formateada para HTML, por lo que visualizaríamos el código
pero no se renderizaría en la página como HTML.
El control de edición utiliza un control Editor de los que vienen disponibles
gratuitamente en las versiones del AJAX Control Toolkit a partir de la 3.0.30515
(descargable desde http://ajaxcontroltoolkit.codeplex.com/). El código es muy
sencillo y casi idéntico al de cualquier otra plantilla: simplemente se ajustan los
validadores y se define el contenido del control (propiedad Content de éste) como
valor a extraer de la edición.
Una vez creada la plantilla sólo nos resta indicar en los metadatos qué campos
queremos que sean editables con ella. La forma de hacerlo es mediante el atributo
UIHint, estudiado en el epígrafe 9.1. Por ejemplo, vamos a hacer que la descripción
de las categorías de productos sean editables en formato HTML. El código necesario
es el de la figura 12.

Figura 12.- Código para asignar editor de campos personalizado


ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  143

El atributo indica el nombre del control y Dynamic Data se encarga de llamar a la


versión apropiada según se esté visualizando o editando el campo. Si sólo tuviésemos
uno de las dos variantes se usaría el control más apropiado para el tipo de datos
subyacente. Así, en este ejemplo si no tuviésemos el control de visualización (o sea,
“HTMLText.ascx”) se usaría el correspondiente a campos de texto: “Text.ascx”, y
lo mismo en caso contrario.
En la figura 13 vemos el editor de campos en funcionamiento, tanto en edición
como en visualización.

Figura 13.- Nuestros controles para editar y visualizar HTML

11.- Dynamic Data en páginas propias


Desde el principio del capítulo he comentado que es posible sacarle partido a esta
tecnología sin usar necesariamente la parte de generación automática de interfaces
ni las rutas. De hecho, salvo en los casos en los que hayamos protegido debidamente
y de manera consciente el acceso a las rutas autogeneradas, es incluso mejor usar
Dynamic Data sin la generación automática de interfaces.
144  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

En realidad a estas alturas ya sabes lo necesario para sacarle partido, pues todo
lo que hemos visto es válido.
Si tenemos el modelo de datos correctamente registrado (del modo que se explica
en el epígrafe 4 de este capítulo), lo único que tenemos que hacer en nuestras páginas
para sacar partido a Dynamic Data es arrastrar un control DynamicDataManager a
la superficie de diseño de la página.

Figura 14.- Los controles de datos dinámicos

Como ya hemos visto en el punto 5.1, este control registra a otros controles para
poder sacarle partido a la generación automática de campos de Dynamic Data. Si
en nuestra página tenemos un GridView y un control LinqDataSource configurados
para trabajar conjuntamente, sólo tenemos que registrar la rejilla con nuestro Dyna-
micDataManager para que ésta sea capaz de generar la visualización y edición de
las columnas de datos, bien declarativamente:
<asp:DynamicDataManager ID=”DynamicDataManager1” runat=”server”
AutoLoadForeignKeys=”true”>
<DataControls>
<asp:DataControlReference ControlID=”GridView1” />
</DataControls>
</asp:DynamicDataManager>
ASP.NET Dynamic Data: interfaces de datos a la velocidad de la luz  145

Bien mediante código


Protected Sub Page_Init(ByVal sender As Object, ByVal e As System.
EventArgs) Handles Me.Init
DynamicDataManager1.RegisterControl(GridView1)
End Sub

Ahora, en lugar de usar los campos enlazados normales de una rejilla podemos
utilizar los campos dinámicos simplemente indicando el nombre del campo que van
a representar:
<asp:GridView ID=”GridView1” runat=”server” AllowPaging=”True”
AllowSorting=”True” AutoGenerateColumns=”False” CellPadding=”4”
DataKeyNames=”CategoryID” DataSourceID=”LinqDataSource1”
GridLines=”None” PageSize=”3”>

<Columns>
<asp:DynamicField DataField=”CategoryName” runat=”server” />
<asp:DynamicField DataField=”Description” runat=”server” />
</Columns>

</asp:GridView>

Al usar este tipo especial de campos enlazados, soportados nativamente por los
controles GridView y DetailsView, podremos sacar partido a todo lo que hemos visto
sobre Dynamic Data. Se usarán las plantillas de controles apropiadas para cada uno,
se hará la validación automática de los campos, etc... Ello nos libera de una gran
cantidad de trabajo pues no tendremos que preocuparnos por editar cada una de las
columnas de la rejilla para hacer validaciones, dar formatos, permitir la selección
de elementos relacionados, etc...
Los controles ListView y FormView permiten también todo el trabajo con campos
dinámicos, pero en este caso no soportan los campos enlazados de tipo Dynamic-
Field. Para poder exprimir la potencia de los datos dinámicos en este caso, debemos
incluir controles de tipo DynamicControl dentro de su marcado. Básicamente estos
controles dinámicos se comportan igual que los campos dinámicos de la rejilla, pero
los podemos usar en el HTML de estos otros controles que no tienen soporte nativo
para datos dinámicos. Por ejemplo, un ListView podría tener la siguiente definición
para sus Items:
<ItemTemplate>
<tr>
<td>
<asp:DynamicControl runat=”server”
DataField=”ProductID” />
</td>
<td>
<asp:DynamicControl runat=”server”
146  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

DataField=”ProductName” />
</td>
<td>
<asp:DynamicControl runat=”server”
DataField=”Supplier” />
</td>
<td>
<asp:DynamicControl runat=”server”
DataField=”UnitPrice” />
</td>
<td>
<asp:DynamicControl runat=”server”
DataField=”Discontinued” />
</td>
</tr>
</ItemTemplate>

Como vemos, tan fáciles de utilizar como los campos dinámicos enlazados de
la rejilla.
Del mismo modo podría emplear un control DinamycEntity como hemos visto
al analizar las plantillas.
Todo lo que hemos visto de modificar los metadatos del modelo, asignar controles
específicos para edición y visualización, etc... sigue siendo válido para estos contro-
les y los utilizaremos igual. La mejor forma de aprender a usarlos en la práctica es
analizar bien, como ya dije anteriormente, las plantillas por defecto que trae Visual
Studio para la interfaz autogenerada, pues en realidad lo único que hacen éstas es
deducir de la ruta qué tablas van a utilizar. El resto es lo mismo que podríamos
hacer nosotros en cualquier página.

12.- En resumen
Dynamic Data es una tecnología espectacular aparecida con el Service pack 1 de
.NET 3.5 y disponible en ediciones superiores, que nos permite generar interfaces
completas de gestión de datos de manera automática. Podemos personalizar casi sin
límite el comportamiento de las interfaces generadas.
Cualquier programador Web podrá ver la ganancia de productividad que obtiene
con esta tecnología sin perder por ello potencia ni capacidad de adaptación a nece-
sidades concretas.
capítulo
7
Filtrado de datos automático
con QueryExtender

Una de las tareas más comunes que debemos realizar como programadores es la de
filtrar datos. Imagínate el típico listado en una aplicación, por ejemplo para mostrar
todos los productos de la base de datos “Northwind”. Como son muchos, debes facili-
tar a los usuarios alguna manera de hacer filtros sobre los datos visualizados. De esta
manera localizarán fácilmente aquellos en los que estén realmente interesados.
Lo habitual en estos casos es que tengas que interceptar el evento de algún botón
de búsqueda, y entonces modificar dinámicamente la cláusula WHERE de la consul-
ta. Otra opción bastante utilizada es gestionar el evento de tipo Selecting del control
origen de datos, en el cual podrás cambiar también la consulta dinámicamente.
Si bien estas técnicas son relativamente sencillas, nos obligan a escribir código
y son difíciles de mantener. Cada vez que queramos modificar la forma de hacer
la búsqueda o si necesitamos añadir más filtros a la misma tendremos que tocar el
código y recompilar.
En ASP.NET 4.0 se incluye un nuevo control -y clases relacionadas- que nos
permiten crear filtros de manera declarativa, sin necesidad de escribir código alguno.
Es otra gran idea para mejorar la productividad en tareas comunes.

1.- El control QueryExtender


El nuevo control QueryExtender se utiliza para definir de forma declarativa filtros
sobre orígenes de datos, pudiendo incluir tantas condiciones como sea necesario,
y sin necesidad de escribir código. Como resultado se obtiene un conjunto de re-
sultados que cumpla las condiciones especificadas, ordenado según el criterio que
hayamos decidido.
Los QueryExtender funcionan exclusivamente con controles de origen de datos
para Linq2SQL (LinqDataSource) y para Entity Framework (EntityDataSource), ya
que el lenguaje de consulta que manejan por debajo es Linq.
Otra cosa interesante a tener en cuenta al usarlos es que modifican directamente
la expresión Linq manejada por el control origen de datos, por lo que en realidad
de la base de datos se obtienen ya los datos filtrados. Es decir, no es un control

147
148  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

que actúe a posteriori sobre los datos recibidos, procesándolos en memoria, sino
que trae únicamente los datos necesarios para mostrar en cada caso, por lo que el
rendimiento es muy bueno.

2.- Tipos de filtros


Como veremos enseguida, el control QueryExtender es sólo un contenedor de expresio-
nes de filtrado. Éstas son las que definen el filtro y las que asociamos a otros controles
de la interfaz para que tomen de ellos sus valores (parametrización del filtro).
Los filtros se definen, por tanto, como una jerarquía de controles cuyo marcado
debemos incluir en la página. En la raíz de la misma está el control QueryExten-
der. Como hijos de éste se incluyen uno o varios controles que representan tipos
de expresiones de filtrado. A su vez estas expresiones contienen parámetros para
particularizar los valores de filtrado:
QueryExtender
Filtro1
Parámetro1
Parámetro2
Filtro2
Parámetro1
.....

Disponemos de las siguientes expresiones de filtrado que podemos declarar den-


tro del control:

• SearchExpression: busca en la base de datos una cadena de texto. Puede


buscar en uno o varios campos a la vez, y podemos especificar en dónde que-
remos que esté la cadena objetivo de la búsqueda: al principio del campo, al
final o en cualquier punto del campo (sería equivalente a una búsqueda SQL
con LIKE usando ‘%cadena’, ‘cadena%’ y ‘%cadena%’ respectivamente).

• RangeExpression: hace el filtro de un campo comparando sus valores con


unos valores mínimo y máximo especificados en un par de controles.

• PropertyExpression: compara el valor de la propiedad de un control con los


valores de un campo en el origen de datos. Por ejemplo, puedes usar el valor
seleccionado en una lista desplegable para mostrar los registros que tengan
un campo que coincida con ese valor. Luego lo veremos con un ejemplo.

• CustomExpression: permite crear criterios de búsqueda personalizados. Se


le indica el nombre de un método propio que modifique mediante código la
consulta Linq a lanzar contra el origen de datos. Es tal vez el menos útil,
Filtrado de datos automático con QueryExtender  149

en mi opinión, puesto que es casi equivalente a los métodos tradicionales


comentados al principio del capítulo y hace que pierda la gracia el sistema,
ya que hay que escribir código. No obstante, en ocasiones puede ser útil.

• MethodExpression: permite llamar a un método estático/compartido (in-


dicado con el atributo MethodName) de una determinada clase (atributo
TypeName) para realizar el filtrado dinámicamente. El método puede carecer
de parámetros o, en caso de tenerlos, el primero deberá ser una referencia
al control origen de datos, y los siguientes se corresponderán con cada uno
de los nodos hijo de tipo XXXParameter que particularizan la expresión de
filtrado.

• DynamicFilterExpression: permite hacer filtros a partir de controles Dyna-


micFilter de Dynamic Data. Las has visto en el código de las plantillas del
capítulo anterior.

• ControlFilterExpression: se utiliza en páginas con Dynamic Data para filtrar


los valores de un control de datos en función del valor seleccionado en una
rejilla u otro control enlazado.

• OrderByExpression: no es una expresión de filtrado, sino de ordenación.


Nos permite devolver los resultados ordenados a partir de un campo (atributo
DataField) y según una dirección (atributo Direction, con valores Ascending
o Descending). Es posible incluir varias de estas expresiones para ordenar
según diversos criterios.

Las más utilizadas, y con las que trabajaremos habitualmente, son las tres prime-
ras y la última de ordenación. Todas estas expresiones podemos utilizarlas de manera
individual o combinarlas para obtener filtros complejos. Cada uno de ellos se asocia
a uno o varios controles para obtener los valores con los que trabajar.
Ya es suficiente de teoría. Para aprender a manejar bien todo esto, vamos a
crear primero una página sencilla de la que partir para añadirle, a continuación,
funcionalidad de filtrado de datos con un control QueryExtender.

3.- Creación de la página base para ejemplo


Vamos a crear una página que nos servirá de base para construir diversos filtros con
el control QueryExtender y así aprender a manejarlo. En el ZIP con las demos del
libro se ha incluido una página por cada ejemplo, pero todas ellas usan la página
que vamos a crear ahora como base.
En un nuevo Sitio Web agrega una nueva clase Linq2SQL llamada NorthWind.
dbml. Di que sí cuando Visual Studio te pregunte si quieres crear la carpeta App_
Code, pues es necesario que vaya dentro de ésta para poder utilizarlo.
150  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Figura 1.- Agregar una nueva clase Linq2SQL

Desde el explorador de servidores abre la base de datos “Northwind”. Sobre la


superficie de diseño del .dbml arrastra las tablas de proveedores (Suppliers) y de pro-
ductos (Products). Te debería quedar algo con el aspecto mostrado en la figura 2.

Figura 2.- Nuestra entidad Linq2SQL para trabajar.


Filtrado de datos automático con QueryExtender  151

Graba esta entidad y a continuación, en una nueva página ASPX, añade un


ScriptManager y un UpdatePanel. Dentro del panel arrastra un control LinqDataSource.
Desde la lista de tareas de éste escoge la opción de configurarlo. Eso abrirá el
asistente de configuración del origen de datos. En el primer paso pulsa simplemente
siguiente ya que el contexto que está seleccionado es el que queremos usar (y el
único disponible). En el segundo paso elige la tabla de Productos y marca los
campos indicados en la figura 3.

Figura 3.- Campos seleccionados en el contexto de datos

Tras aceptar el diálogo arrastra ahora un control GridView y asígnale como


origen de datos el anterior control, usando para ello las tareas asociadas a la rejilla.
Marca las opciones de paginación (3 por página) y ordenación, y ajusta las cabeceras
de las columnas como creas conveniente. En los archivos descargables desde la Web
yo he añadido además una columna de tipo plantilla para mostrar el nombre del
proveedor de cada producto.
Último paso muy importante: para que te funcionen los distintos tipos de expre-
siones de filtro (y que Visual Studio te ofrezca soporte Intellisense para utilizarlos),
deberás agregar una línea especial al archivo de configuración de tu sitio web,
ya que de manera predeterminada no están registrados para su uso.
Localiza el nodo <pages> dentro de web.config y desplázate hacia abajo hasta que
encuentres el sub-nodo <controls>. Al final de la lista añade la siguiente línea:
<add tagPrefix=”asp” namespace=”System.Web.UI.WebControls.Expressions”
assembly=”System.Web.Extensions, Version=4.0.0.0, Culture=neutral,
PublicKeyToken=31BF3856AD364E35”/>
152  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

En cuanto grabes el web.config, las expresiones de filtro descritas en el epígrafe


2 de este capítulo estarán disponibles para su uso en tu aplicación.
No te olvides de hacer esto último o te encontrarás con errores a la hora de
compilar.
Ahora ejecuta la página para ver que todo funciona como es debido y que se
visualiza un listado con todos los productos de la base de datos.
Posteriormente podrás copiar y pegar todo lo que hay entre las etiquetas
<html></html> de esta página ASPX, para replicar su funcionalidad en otras
nuevas que crees para hacer pruebas.

4.- Primer filtro: búsqueda por nombre


Nuestro primer filtro será la típica casilla de búsqueda en la que introducimos un
texto y se nos devuelven todos los productos que lo contengan en su nombre. En
este caso además incluiremos también en la búsqueda el nombre de los proveedores
(Suppliers), haciéndola sobre dos campos de la base de datos a la vez.
Arrastra un control de texto en la parte de arriba del panel y coloca justo delante
un botón. Este botón no tendrá código, y lo usaremos únicamente para provocar un
Postback al servidor (asíncrono, porque está dentro de un UpdatePanel).
Pasa a la vista de código HTML de la página y justo debajo de las etiquetas que
definen el control LinqDataSource escribe la definición de un control QueryEx-
tender, así:
<asp:QueryExtender ID=”QueryExtender1” runat=”server”
TargetControlID=”LinqDataSource1”>
</asp:QueryExtender>

El propio editor de Visual Studio te ayudará a escribir la etiqueta.


En este punto lo único que hemos hecho es crear este control y usar su propie-
dad TargetControlID para asignarlo al origen de datos con el que trabaja la rejilla
(LinqDataSource1). De esta forma todas las expresiones de filtrado que definamos
dentro de él actuarán sobre este DataSource.
Entre las dos etiquetas del control QueryExtender puedes añadir tantas expresio-
nes de filtrado como sean precisas. Visual Studio te ayudará con la sintaxis, como
se observa en la figura 4.

Figura 4.- Intellisense para las expresiones de filtrado


Filtrado de datos automático con QueryExtender  153

Para nuestro ejemplo definiremos una expresión de tipo SearchExpression. El


código declarativo quedaría de la siguiente manera (míralo en la página “Search.
aspx” del ZIP con los ejemplos del libro):
<asp:QueryExtender ID=”QueryExtender1” runat=”server”
TargetControlID=”LinqDataSource1”>
<asp:SearchExpression DataFields=”ProductName, Supplier.CompanyName”
SearchType=”Contains” >
<asp:ControlParameter ControlID=”TextBox1” />
</asp:SearchExpression>
</asp:QueryExtender>

En la expresión de búsqueda indicamos sobre qué campos del origen de datos


queremos buscar usando el atributo DataFields. Además debemos decir si queremos
que el texto buscado esté al principio (StartsWith), al final (EndsWith) o en cualquier
posición del campo (Contains), que es lo que hemos escogido nosotros. Como nodo
hijo de la expresión se define un parámetro que indica de dónde queremos obtener
el texto para realizar la búsqueda. En este caso es un parámetro que adquiere la
cadena desde un control de la página, el cuadro de texto TextBox1, pero existen otras
muchas posibilidades como veremos enseguida.
Como ves se trata de una jerarquía de etiquetas, tal y como se describe en el
apartado 2.
Ejecuta la página. Escribe parte del nombre de un producto o de un proveedor en
el cuadro de texto y pulsa ENTER o presiona el botón. Verás como automáticamente
el contenido de la rejilla cambia y muestra sólo los productos cuyo nombre contiene
ese texto.
¡Funciona! y todo sin escribir código de ningún tipo.

Figura 5.- Nuestro filtro en plena acción: ¡Mira mamá, sin código! ;-)

Incluir condiciones adicionales en el filtro es tan fácil como agregar nuevas


expresiones dentro del control QueryExtender.
154  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

5.- Filtrado por rangos de valores


Añade otra página al sitio (“Range.aspx”) y replica el código de la página patrón,
copiando y pegando, para tener ya todo lo básico configurado. Ahora arrastra dos
controles de texto a la parte de arriba del panel. Nos servirán para filtrar la lista de
productos indicando un valor mínimo y máximo para el precio de éstos.
En el código HTML incluye estas etiquetas:
<asp:QueryExtender ID=”QueryExtender1” runat=”server”
TargetControlID=”LinqDataSource1”>
<asp:RangeExpression DataField=”UnitPrice” MinType=”Inclusive”
MaxType=”Inclusive”>
<asp:ControlParameter ControlID=”TextBox1” DefaultValue=”0” />
<asp:ControlParameter ControlID=”TextBox2” />
</asp:RangeExpression>
</asp:QueryExtender>

En el caso de las expresiones de filtrado por rangos debemos indicar por qué
campo vamos a filtrar (atributo DataField) y si queremos que los valores mínimo y
máximo se incluyan en el rango de valores permitidos (o sea, si es un intervalo abier-
to o cerrado en cada extremo). Dentro de la expresión incluimos dos parámetros que
obtienen sus valores desde un control, para indicar que el valor mínimo lo obtenga
del primer cuadro de texto, y el máximo del segundo. Además el valor mínimo, si
el campo está en blanco, se considerará que es cero (no hay precios negativos).

6.- Filtrado por valores de propiedades


En una nueva página de ejemplo que vamos a llamar “PropertyQE.aspx”, copiaremos
el código HTML de la página de búsqueda del apartado 4. La usaremos de base
para añadirle una opción adicional de filtrado y decidir si queremos ver sólo los
productos descatalogados o no. Pon un control de tipo CheckBox al lado del cuadro
de búsqueda. Le he quitado también la ordenación al LinqDataSource, y ahora la
haremos con una expresión de ordenación para probarlas.
En el marcado del QueryExtender añade este filtro:
<asp:PropertyExpression>
<asp:ControlParameter ControlID=”CheckBox1” PropertyName=”Checked”
Name=”Discontinued” />
</asp:PropertyExpression>
<asp:OrderByExpression DataField=”ProductName”
Direction=”Ascending”></asp:OrderByExpression>

Ahora, al ejecutar la página verás que puedes filtrar simultáneamente por el texto
de búsqueda en el nombre de los productos y de los proveedores, y también decidir
si quieres ver sólo los productos descatalogados o no.
Filtrado de datos automático con QueryExtender  155

Como ejemplo adicional un poco distinto, he creado un filtro que permite selec-
cionar de una lista desplegable el nombre de un proveedor y que se muestren sólo
los productos de éste. A la página de ejemplo le he llamado “PropertyQE2.aspx”.
Es muy sencillo de hacer. Puedes estudiar el código HTML utilizado mirándolo en
el ZIP con los ejemplos del libro.

Figura 6.- Ejemplo de filtro a partir de una lista desplegable

7.- Parámetros de filtrado


En los ejemplos que acabamos de ver nuestras expresiones tomaban los valores a
utilizar para el filtrado desde uno o varios controles dentro de la página. Al igual
que en el caso de los controles DataSource, que permiten tomar sus parámetros de
diversos lugares, las expresiones de filtrado también disponen de la misma funcio-
nalidad. En concreto ofrecen los siguientes orígenes para valores de parámetros:
• ControlParameter: el que hemos utilizado hasta ahora. Obtiene el valor del
parámetro a partir de una propiedad de un control de la página.
• CookieParameter: usa una cookie para sacar el valor para el filtro.
• FormParameter: un parámetro enviado a la página a través de un POST de
un formulario.
• ProfileParameter: utiliza un valor definido para el perfil del usuario con la
API de Profile de ASP.NET.
• QueryStringParameter: obtiene el valor desde un parámetro pasado en la
URL.
• RouteParameter: permite extraer el valor para el parámetro a partir de un
fragmento de la ruta de la página actual, si estamos usando el sistema de
enrutado aparecido en ASP.NET 3.5 (ver capítulo 1). Usado normalmente en
combinación con Dynamic Data.
• SessionParameter: lee el valor de una variable de sesión.
156  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Como vemos las posibilidades son muchas y podemos usar varios de estos orí-
genes al mismo tiempo para generar valores para un filtro. En la figura 7 puedes
verlos en el editor de Visual Studio.

Figura 7.- Diferentes tipos de parámetros de filtrado

Para hacer un ejemplo con uno de estos orígenes de parámetros adicionales,


vamos a modificar la página de búsqueda del apartado 4 (está ya modificada en el
ZIP de los ejemplos).
Añade una columna a la rejilla que genere, para cada registro, un enlace para
mostrar los detalles de un producto en otra página. Para ello, desde el editor de
columnas de la rejilla, añade una nueva de tipo HyperLinkField, asígnale el texto
“Ver detalles >>”, el nombre del campo ProductID en la propiedad DataNaviga-
teUrlFields, y la cadena “ ~/DetallesProducto.aspx?ID={0}” en la propiedad Data-
NavigateUrlFormat.
Con ello lo que obtenemos es un enlace para cada producto que apuntará a la pá-
gina “DetallesProducto.aspx”, pasándole el identificador del producto en la URL.
Ahora crea una nueva página “DetallesProducto.aspx” y arrastra sobre ella un
control LinqDataSource configurado para traerse todos los campos de la tabla de
productos, y un control DetailsView enlazado a este origen de datos.

Nota:
Soy consciente de que sería mucho más sencillo y directo usar las propiedades de filtrado pro-
pias del LinqDataSource para obtener los datos del producto a partir del valor indicado en la
URL, usando un método tradicional. Este ejemplo se hace con un QueryExtender como prueba
de concepto de uso de otro tipo de orígenes de filtrado, pero no porque sea la mejor forma de
hacerlo en este caso particular.

En el código de la página incluye las siguientes etiquetas justo debajo del control
LinqDataSource:
<asp:QueryExtender ID=”QE1” runat=”server” TargetControlID=”LinqDataS
ource1”>
Filtrado de datos automático con QueryExtender  157

<asp:PropertyExpression>
<asp:QueryStringParameter DefaultValue=”0”
QueryStringField=”ID” Name=”ProductID” />
</asp:PropertyExpression>
</asp:QueryExtender>

En este caso hemos usado una expresión de tipo propiedad, pero en lugar de
asignarle el valor de un control, hemos usado un parámetro QueryStringParameter
para sacar el identificador del producto de la URL actual. Le indicamos el nombre
del parámetro de la URL del que sacamos el valor. En este caso también le decimos
que use como valor por defecto (si no existe el parámetro ID) un 0, de modo que no
se devolverá dato alguno de la base de datos, mostrándose un mensaje por defecto
que hemos configurado previamente en el DetailsView.

8.- En resumen
La nueva funcionalidad de QueryExtender nos brinda la posibilidad de crear listados
con múltiples filtros complejos de una manera directa y sin necesidad de escribir
código. Esta es una de las características, junto con Dynamic Data, que convierten
a ASP.NET en la plataforma de desarrollo Web más productiva del mercado para
crear interfaces de datos.
Descarga los ejemplos de este capítulo, juega con ellos y haz tus propias pruebas
para aprender a sacarle todo el partido.
Índice analítico

{{ }}, doble llave 103 ContentTemplateContainer 33


<CompositeScript> 87 CSS condicionadas 99
<ContentTemplate> 32
<ProgressTemplate> 39 D
<ScriptReferences> 83 data (propiedad) 95
<scripts> 72 DataContext 108
dataProvider (propiedad) 95
A DataView 95-98
ADO.NET Data Services 6 dataview (atributo) 96
AdoNetDataContext 109 Desarrollo Paralelo 6
AJAX Control Toolkit 50-58 Disparadores (triggers) 34-37
AllowCustomErrorRedirect 48 DisplayAfter 40
ASP.NET MVC 7 doble llave {{}} 103
AssociatedUpdatePanelID 39 Dynamic Data 117-146
AsyncPostBackError 44 DynamicControl 145
AsyncPostBackErrorEventArgs 45 DynamicDataManager 126, 144
AsyncPostBackErrorMessage 45 DynamicEntity 129, 146
AsyncPostBackTrigger 37 DynamicField 145
AuthenticationService 76 DynamicLayout 39

B E
binding 103 EnableHistory 113
EnablePageMethods 64
C EnablePartialRendering 32, 85
ChildrenAsTriggers 34 EndRequestEventArgs 47
class (atributos) 99 Enlazado de datos
ClientScriptManager 82 en tiempo real 103-105
code (atributos) 101 Enrutamiento 6
Combinación de scripts 87 Entity Framework 5
ConfirmButtonExtender 58 Eric lawrence 62

159
160  Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)

Errores, gestión en AJAX 43-48 M


evaluador de expresiones {{}} 103 Maestro-detalle, vistas 105
Master Pages 42
F Membership, AJAX 75-81
Facebook 10 MetaDataType 137
fetchMethod (propiedad) 95 Metadatos (Dynamic Data) 136
Fiddler 62 MetaModel 122
FieldTemplateUserControl 131 MetaTable 125
FilterRepeater 128 Métodos de página 62
MicrosoftAjaxMode 85
G Modelo de datos
(Dynamic Data) 118, 121-123
GET 19
MVC 7
GetCurrent 43
getElementByID 15
getElementsByTagName 15 O
Gmail 10, 11 Observer, patrón 103
GridViewPager 127
P
H PageRequestManager 47
Historial del navegador 112-115 PageTemplates (carpeta) 125
Hotmail 10 Paralelo, Desarrollo 6
Plantillas
(Dynamic Data) 118, 123-132
I Plantillas
Interval 41 de campos propias 141-143
IsAsyncPostback 31 Plantillas
isLoggedIn 78 de lado cliente (concepto) 90-91
isUserInRole 81 Plantillas para campos 129
Plantillas para entidades 129
J POST 19
JSON 11, 21-23 PostBackTrigger 37
Pseudo-columnas de datos 98

L
Lenguajes dinámicos 7 Q
LinqDataSource 5 QueryableFilterRepeater 128
Live binding 103-105 QueryExtender 147
Índice analítico  161

R Timer (control) 40
RegisterRoutes 122 Triggers (diparadores) 34-37
Remote Scripting 11
repintado parcial 30, 33-34 U
Retrollamdas de red 60-67 UpdateMode 33
Roles, AJAX 75-81 UpdatePanel 28, 32-33
RoleService 76 UpdateProgress 38
Routing 6
Rutas (Dynamic Data) 119, 132-135 V
ValidatorCalloutExtender 58
S
saveChanges 109 W
ScaffoldAllTables 122 WCF 3
ScaffoldColumn 138 WebServiceProxy 71
ScriptHandlerFactory 70 Windows Cardspace 4
ScriptManager 28, 31-32, 42-43 Windows
ScriptManagerProxy 43 Communication Foundation 3
ScriptService 69 Windows
selectedData 106 Presentation Foundation 4
Services Windows Workflow Foundation 3
(propiedad ScriptManager) 72, 83 WPF 4
Servicios de Aplicación AJAX 75-81
Servicios Web, desde AJAX 67-75 X
sys (atributos) 96, 100-101 XMLHttpRequest 11
sys:activate 96
sys:attach 97
sys:command 105
sys:commandargument 105
sys:commandtarget 105
sys:key 106
System.Wed.Extensions.dll 5
sys-template 94

T
Tick (evento) 41