Beruflich Dokumente
Kultur Dokumente
0
(saltando desde la versión 2.0)
ISBN: 978-84-936696-1-4
Depósito Legal: VG 961-2009
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!
vii
Contenido
CONTENIDO..........................................................................................................................ix
Presentación......................................................................................................................xiii
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
ix
x Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
Í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.
xiii
xiv Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
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.
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
¡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
¿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
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.
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.
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)
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...
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);
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);
}
}
}
Este ejemplo tan sencillo constituye en realidad todo lo que es necesario saber
sobre los fundamentos de funcionamiento de AJAX.
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.
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);
}
}
}
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’);
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);
tenido en cuenta por la aplicación. Esta segunda técnica es la más fiable, aunque un
poco más tediosa de implementar.
<cliente>
<nombre>José Manuel</nombre>
<apellidos>Alarcón Aguín</apellidos>
<empresa>Krasis</empresa>
<telefono>902 876 475</telefono>
<edad>37</edad>
</persona>
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
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
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.
25
26 Tecnologías ASP.NET 4.0 (saltando desde la versión 2.0)
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.
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.
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í.
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.
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.
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:
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
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.
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.
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)
Debes tenerlo en cuenta porque es una sutil diferencia pero puede ser importante
en algunas aplicaciones.
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
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.
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.
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
End Sub
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)
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 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)
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)
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.
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.
Ahora repite la operación anterior y verás como el problema desaparece y los controles se incor-
poran a tu barra de herramientas.
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.
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)
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)
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.
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:
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.
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
Es evidente que para ciertos casos esta técnica ofrece grandes ventajas.
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.
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.
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)
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:
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.
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.
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 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)
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>
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.
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)
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.
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);
}
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”)
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>
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.
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.
</Scripts>
</CompositeScript>
</asp:ScriptManager>
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
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.
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:
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
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.
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)
.sys-template { display:none; }
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í:
{
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
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.
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=”*”>
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.
• sys:disabled: idem que el anterior pero para el atributo disabled que permite
desactivar controles.
• 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>
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)
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.
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.
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 }
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”.
<span>{binding value}</span>
<input type=”text” id=”txtCopia” value=”{binding value}”
/>
</span>
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.
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)
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.
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”
});
});
¡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”
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}}]}
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)
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.
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 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.
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 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)
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.
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.
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.
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
DefaultModel.RegisterContext(GetType(NorthwindDataContext), New
ContextConfiguration() With {.ScaffoldAllTables = True})
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
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)
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.
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.
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.
http://msdn.microsoft.com/es-es/library/system.web.dynamicdata.metata-
ble_members.aspx
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
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).
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
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.
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.
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}
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
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.
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”})
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
End Class
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.
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.
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
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.
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.
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
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.
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.
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.
Figura 5.- Nuestro filtro en plena acción: ¡Mira mamá, sin código! ;-)
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).
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.
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.
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
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)
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