RSS 2.0 Atom 1.0 CDF  
JASoft.org - December, 2005
El blog de José Manuel Alarcón Aguín. Programación .NET y mucho más...
 

Siguiendo con el post anterior sobre AJAX y una vez estudiado el objeto XMLHttpRequest vamos a definir algunas funciones que nos serán útiles para realizar cargas de datos en cualquier navegador.

Lo primero que necesitaremos siempre es obteneruna referencia a un objeto XMLHttpRequest. No se obtiene de la misma manera en Internet Explorer que en los demás navegadores, por lo que debemos tener en cuenta esas diferencias. En IE se obtiene a partir de un objeto ActiveX. En Firefox, Opera, Safari y compañía es una clase integrada del modelo de objetos del navegador. Sabiendo esto es fácil hacer una función genérica para obtener una referencia a un objeto XMLHttpRequest que funcione en todos los navegadores:

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");
 }
}

Ahora que tenemos el objeto tenemos que usarlo para obtener una URL. Debemos usar sus métodos y propiedades (vistos en el anterior post) para conseguirlo:

http = getHttpRequest()
http.onreadystatechange = finCarga;
http.open("GET", "http://www.miserv.com/misdatos.aspx", true)
http.send(null);

Es decir, obtenemos una referencia al objeto como hemos visto, establecemos la función a llamar cuando cambie el estado de la petición que vamos a hacer (más sobre esto luego). Indicamos que vamos a llamar (por GET) a una URL (segundo parámetro) de modo asíncrono (true en el tercer parámetro). Esto hará que se devuelva el control inmediatamente tras hacer la llamada.
Por fin debemos hacer la llamada (open no la hace sólo la prepara), usando para ello el método send. Se le pasa un nulo porque no enviamos ninguna información extra (es una petición GET).

Bien, una vez hecha la llamada, sólo nos queda definir la función que actuará cuando tengamos los datos (finCarga en nuestro ejemplo). Veámosla:

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);
  }
 }
}

Como vemos se comprueba que el estado es el definitivo (terminado), que se ha obtenido correctamente la página (código de estado 200), y se guarda en una variable (global) el XML obtenido (podría haber sido el texto obtenido con responseText). La función ProcesaRespuesta() se encargará de, a partir del XML realizar el trabajo que corresponda.

Para trabajar con el XML usaremos el DOM. No voy a entrar en detalle en este tema ya que cualquiera que haya trabajado con HTML dinámico sabe usarlo más o menos. De todos modos puedes consultar la referencia del DOM para JavaScript que tiene el W3C o si lo prefieres la de Firefox/Mozilla. Generalmente nos va a ser suficiente con usar dos o tres métodos del DOM. Los más importantes sin duda son getElementsByTagName y getElementByID que sirven para localizar elementos en el XML del mismo modo que se hace para hacerlo en HTML común.

EJEMPLO PRÁCTICO:

Para rematar este tema (me hubiera gustado pararme más pero estos días no he tenido tiempo) os dejo un archivo ZIP con un ejemplo sencillo pero completo.

Se trata de un par de listas desplegables. La primera de ellas contiene una serie de elementos (empresas, blogs, revistas, libros...) que el usuario puede elegir. La segunda lista se rellena dinámicamente en segundo plano a partir de la selección hecha en la primera. Lo he colocado en mi servidor para que puedas probarlo directamente on-line pulsando sobre la siguiente figura:


Pulsa sobre la imagen para probarlo

Nota: Sí, antes de que me lo preguntes: el navegador de la imagen es Internet Explorer 7 en castellano) ;-)

Para rellenar la segunda lista se llama por detrás, usando AJAX, a una página ASP que en función del parámetro facilitado devuelve la lista apropiada. El ASP lo tienes en el archivo ZIP de descarga. No se conecta a una base de datos ni nada similar para simplificar (en la realidad seguramente sería así) pero te servirá para hacerte una idea.

CONCLUSIÓN:

Una vez sentadas las bases de AJAX "a pelo", y estudiado un ejemplo práctico completo  (que te puedes descargar) me tomaré unos días de vacaciones del Blog :-)

Dentro de unos días y para rematar con esta serie comentaré algunas cuestiones peliagudas referentes al uso de AJAX, y veremos problemas típicos y sus soluciones en este tipo de aplicaciones. A partir de ahora es cosa tuya cómo lo apliques.

Si alguien quiere que me deje un comentario aquí y podemos tratar también el tema de los Script Callbacks de ASP .NET 2.0, que están íntimamente relacionados con técnicas AJAX pero hacen que sea más fácil que todo esto.

Saturday, December 24, 2005 7:07:00 PM (Hora estándar romance, UTC+01:00)  #    Comments [0]   AJAX | ASP.NET | Programación  |  Trackback

Lo prometido es deuda y hoy seguimos (o casi empezamos) con el estudio de las aplicaciones AJAX.

Aparte de saber HTML y JavaScript, el primer objeto que debemos conocer para hacer aplicaciones AJAX es XMLHttpRequest.

Si has utilizado alguna vez las bibliotecas MSXML con Visual Basic 6.0 o ASP 3.0 es probable que ya lo conozcas. Aunque en el caso de Internet Explorer se sigue usando este objeto ActiveX en el resto de los navegadores (Firefox, Opera, Safari...)  este objeto forma parte ya de las clases nativas del navegador.

Su utilidad básica es la de enviar peticiones (tanto GET como POST) a Urls mediante el protocolo HTTP.

Los métodos y propiedades básicos que debemos conocer son los siguientes(los corchetes indican parámetros opcionales):

  • open(metodo, URL, [asincrono], [usuario], [clave]): sirve para abrir una conexión al servidor. No envía ni obtiene información, sólo se conecta. El tercer parámetro es booleano y sirve para indicar si la conexión se realizará asíncronamente o no (normalmente será que sí). Se puede especificar un nombre de usuario y una contraseña de accesopero normalmente esto será absurdo (estará en claro en el JavaScript, ya me dirás de qué sirve entonces).
  • send(contenido): envía una petición. Si es un POST se pueden incluir los datos a enviar en el parámetro, si no se usa un nulo.
  • abort(): cancela un envío/petición.
  • onreadystatechange: se le asigna un método que será llamado automáticamente cuando se descargue de todo la URL remota (cuando se llama asíncronamente).
  • readyState: informa del estado de la petición: 0=no iniciada, 1=cargando, 2=terminada pero sin procesar, 4=completada.
  • status: código de estado HTTP: por ejemplo 200 (éxito), 404 (no encontrado), etc...
  • statusText: mensaje de información del estado anterior.
  • responseXML: documento DOM que representa el XML devuelto por la petición (se espera XML, claro). Más sobre este asunto en la próxima entrega.
  • responseText: el contenido puramente textual del recurso remoto. Útil si no nos devuelve XML.

Hay algunas cosas más pero con estas es más que suficiente para el 99% de los casos.

Vale por el momento. Quédate con esta idea. En la próxima entrega vamos a ver el uso en la práctica de este objeto. Haremos una función genérica que nos sirva para obtener cualquier URL remota y que funcione tanto en Internet Explorer como en los demás navegadores del mercado.

¡Permanece atento!

Wednesday, December 21, 2005 9:46:00 PM (Hora estándar romance, UTC+01:00)  #    Comments [0]   AJAX | ASP.NET | Programación  |  Trackback

Si hay una palabra de moda últimamente en el mundillo (o submundo, que también vale) de la programación Web esta es sin duda AJAX. Se trata de un acrónimo que significa Asynchronous JavaScript And XML y sienta las bases de las nuevas aplicaciones dinámicas que no recargan la página, como por ejemplo GMail o MSN Earth (ahora Local Live) entre otras muchas.

Se basa en el uso de un objeto llamado XMLHttpRequest que como puedes imaginar sirve para hacer peticiones de documentos XML a través del protocolo HTTP y que apareció por primera vez en las bibliotecas XML de Microsoft (MSXML). Con este objeto se piden documentos XML que luego es posible manipular mediante código JavaScript desde dentro de una página y mostrar resultados dentro de capas (div) de la misma. Esto es lo básico pero da mucho de sí .

Aunque ahora parece que los chicos de Google han inventado la pólvora lo cierto es que el concepto original de esta tecnología fué creado por Microsoft (se llamaba Remote Scripting y he de confesar que a mi no me gustaba mucho en su día), el primer navegador en soportarlo fue, claro está, Internet Explorer y la primera aplicación de este tipo fué Outlook Web Access, para acceder a buzones Exchange.

Bien, sentados los antecedentes de la tecnología vamos a ver en unos cuantos "post" próximos cómo funciona AJAX y desarrollaremos un ejemplo práctico con ella que funcione no sólo en IE, sino en todos los navegadores.

Aunque ahora existen ya algunas bibliotecas de funciones especiales para AJAX en diversas plataformas de desarrollo nosotros, que somos unos machotes, vamos a hacerlo a pelo, es decir sólo con lo que trae el navegador y así aprenderemos bien el funcionamiento.

Por cierto en .NET la biblioteca AJAX más conocida es AJAX .NET. La verdad es que está muy bien pero se quedará en nada cuando aparezca el año que viene (se supone) la biblioteca AJAX de Microsoft llamada con el nombre clave Atlas. Yo ya he trabajado un poco con ella y he visto ejemplos más grandes en funcionamiento y os puedo asegurar que es una verdadera virguería: con soporte para espacios de nombres, herencia, servicios Web, controles ASP.NET, y muchas cosas que facilitan el trabajo. En la parte de cliente parece que estás trabajando en C# en lugar de en JavaScript. Lo malo que tiene es que las betas actuales apestan: muy mala documentación y además desfasada respecto a las biblitoecas, los ejemplos no funcionan, etc.. A ver si sacan una beta de verdad (esto son más bien alfas). Por cierto si has probado la beta de Microsoft Live (te lo recomiendo) puedes comprobar que los módulos que se "enganchan" al sistema usan por debajo Atlas. Si alguien está interesado que me lo diga y podemos hacer una mini-serie de "post" explicando cómo se crean estos módulos de "Live".

En fin, lo dicho, tras haber presentado AJAX mañana y los próximos días vamos a currarnos un ejemplo completo desde cero para ver cómo trabaja el asunto. ¡Mantente sintonizado a este blog!

Tuesday, December 20, 2005 9:35:00 PM (Hora estándar romance, UTC+01:00)  #    Comments [0]   AJAX | ASP.NET | Programación  |  Trackback

El otro día me topé de casualidad con un post de Daniel Fisher que hablaba de una pequeña utilidad que había escrito para lanzar el servidor Web que viene con Visual Studio 2005 para cualquier carpeta. Su código lo había adaptado de otro hecho por Chris Fraizer.

El caso es que la he retocado un poco, la he "castellanizado" y he hecho que funcione bien el lanzamiento final de Internet Explorer, además de ofrecer un archivo de registro válido. El resultado lo puedes descargar desde aquí (11,8 KB), y el código fuente desde aquí (27,1 KB).

Descarga el primer archivo con el programa y descomprímelo en C:\Windows o análogo (en el raíz de tu sistema, vamos, aunque puede ser en cualquier carpeta que esté en el "path" del sistema). Haz doble clic sobre el archivo CassiniAqui.reg. A partir de este momento tienes una nueva opción en el menú contextual de las carpetas en el explorador de Windows: "Lanzar la web de esta carpeta"

Si utilizas esta opción se abrirá el servidor integrado que viene con Visual Studio 2005 o Visual Web Developer 2005 Express Edition. Se pondrá a escuchar en un puerto aleatorio (igual que pasa con Visual Studio) y se abrirá el navegador predeterminado yendo directamente a la nueva web que hemos creado al vuelo.

El servidor de VS2005 es una versión modificada del servidor Cassini. Por eso he bautizado la utilidad como "CassiniAqui".

A mi me parece muy útil ya que no necesitarás abrir Visual Studio para probar ciertos cambios o para ver qué demonios hacía una página determinada de una de tus aplicaciones.

Si no usas Visual Studio 2005 (sigues con Visual Studio 2003) y aún así quieres hacer algo similar lo tienes fácil: descárgate el servidor Cassini con el enlace anterior y retoca el código de CassiniAqui para que en lugar de usar la versión modificada de VS2005 use directamente Cassini. Lo recompilas con VS2003 y ya está.

¡Que te aproveche!

Friday, December 16, 2005 9:07:00 PM (Hora estándar romance, UTC+01:00)  #    Comments [0]   Freeware | Programación  |  Trackback

En mi anterior 'post' titulado Usar un ObjectDataSource con un DataSet tipado en ASP.NET 2.0 se me olvidó comentar un detalle. Si aún a pesar de lo que te digo quieres usar el ObjectDataSource para realizar actualizaciones a través de un DataSet tipado puedes conseguir que funcione. Para ello desmarca la opción de "Actualizar campos de la base de datos" en el diálogo de configuración del TableAdapter que vayas a utilizar enlazado a este objeto.

Esto elimina el último parámetro del método Update, teniendo por tanto el número de parámetros correcto y funcionará sin problemas. O casi...

El inconveniente ahora es que los valores auto-generados por la base de datos no se recuperarán tras la actualización o añadido de datos. Por ejemplo los autonuméricos. Esto tiene una implicación que puede engañar a mucha gente, incluso a programadores experimentados. Lo relato a continuación.

ATENCIÓN: Puede que si lo usas y añades un registro, de repente, veas que sí tienes el autonumérico correcto en tu control enlazado. Pero ¿en qué quedamos? ¿no habíamos marcado la opción para que no se actualizara? Entonces, ¿cómo es que obtengo el autonumérico igual? No entiendo nada...

Vale. Lo que pasa es una ilusión "óptica". Lo que obtenemos no es el autonumérico real sino uno autogenerado por el DataSet. El DataSet tipado tiene los campos autonuméricos marcados como tales y cuando se introduce un nuevo registro en memoria incrementan su valor del mismo modo que pasaría en la base de datos. Esto se hace para poder relacionar registros entre tablas en memoria pero no tienen un valor real ya que, en general, no van a coincidir con los autonuméricos que se generarán en la base de datos por muchos motivos: puede que haya huecos por inserciones no confirmadas y, sobre todo, seguramente habrá varios usuarios introduciendo datos simultáneamente.

Por este motivo si pruebas la aplicación tú solo, sin más usuarios introduciendo datos, tendrás la ilusión de que obtienes los valores autonuméricos de la base de datos cuando en realidad no es así. Si te fias de esto luego en la realidad (cuando la aplicación esté en producción) verás que los autonuméricos que usas no coinciden en muchos casos con los reales y te puedes volver loco para encontrar en dónde está el error. Así que mucho ojo.

Para evitar este tipo de problemas yo lo que recomiendo es establecer el valor inicial de los campos autonuméricos del DataSet tipado en -1 y el incremento también en esta cantidad. De este modo los autonuméricos de memoria tendrán todos valores negativos y los de la base de datos serán positivos, siendo muy fácil distinguir unos de otros.

Espero que esta disertación, aunque algo "espesa", llegue a calar ya que según mi experiencia este problema lo va a tener muuuucha gente. Eso seguro. Así que ya sabes: "yo la primera vez lo leí en el Blog de Jose Alarcón" ;-)

Saturday, December 10, 2005 7:54:00 PM (Hora estándar romance, UTC+01:00)  #    Comments [1]   Programación  |  Trackback

El objeto ObjectDataSource que viene con ASP.NET 2.0 es estupendo ya que permite usarlo como origen de datos para enlazar rejillas y otros controles (como DetailsView por ejemplo) con objetos de negocio.

Esto va muy bien, por ejemplo, para enlazarlo con servicios Web ubicados en otras máquinas o con componentes de la capa de negocio que encapsulan las validaciones y verificaciones de seguridad aislándonos de la capa de datos (al interesado en estos temas le recomiendo el curso "Desarrollo de aplicaciones de datos en N-Capas con .NET 1.x y 2.0" de campusMVP).

Por otro lado tenemos la potencia del nuevo modelo de TableAdapters que va unido a los nuevos DataSet tipados que se crean visualmente en Visual Studio 2005. Estos enlazan los DataSet tipados con el propio acceso a datos y, en la práctica, constituyen por si mismos una verdadera capa de datos, sin necesidad de escribirla nosotros como pasaba en versiones anteriores del entorno. La verdad es que son muy potentes y encima independientes del origen de datos relacional que usemos.

Muchos programadores conocen ambas cosas y lógicamente piensan en utilizarlas conjuntamente. Es decir, crear un DataSet tipado para encapsular el acceso a datos y luego usar un ObjectDataSource para, utilizando directamente los TableAdapters, enlazar controle de la interfaz a esta capa de acceso a datos.

El caso es que esto funciona muy bien para hacer consultas y mostrar sus resultados, para eliminar registros e incluso para crear nuevos registros en la base de datos. Sin embargo en la mayor parte de los casos falla estrepitósamente al editar registros.

El motivo de esto lo encontramos en la forma (desde mi punto de vista bastante chapucera y desilusionante) en que han programado las actualizaciones en el ObjectDataSource los chicos de Microsoft. En lugar de llamar a la sobrecarga del método Update del TableAdapter que toma como parámetro un DataSet y pasarle el DataSet original obtenido en el enlace con sólo las filas modificadas (que sería en principio lo lógico y lo que se suele hacer en casi todas las aplicaciones), lo que hace este objeto es llamar a otra sobrecarga de dicho método Update que toma tantos parámetros como campos se hayan enlazado. El ObjectdataSource envía entonces los parámetros que tenga especificados en su sección <UPDATEPARAMETERS>enviándolos además en el mismo orden exactamente. Cualquier variación en el orden o el tipo hará que falle la llamada. Y lo que es peor: si en el asistente de creación del TableAdapter se marca la opción de actualziar los campos desde la base de datos (para que se traiga los autonuméricos normalmente), se añade un parámetro extra a esa sobrecarga del método Update el TableAdapter de manera que entonces el ObjectdataSource se encuentra con un parámetro de más y no sabe que hacer, generándose una excepción. Lamentable.

En las betas de .NET 2.0 pasaba esto, pero todos confiábamos en que la versión final lo tendría solucionado, cosa que no ha ocurrido. Una pena. En el curso de "Desarrollo de aplicaciones Web con ASP.NET 2.0" que estoy dando estos meses en campusMVP, y en el anteriormente mencionado sobre N-capas, esta es una de las preguntas más recurrentes que tengo por parte de los alumnos. Ahora ya sabes porqué es.

Si la parrafada anterior te ha parecido un tanto difícil de entender, simplemente haz la prueba de enlazar un GridView a un DataSet tipado con un ObjectDataSource. Edita un registro y verás exactamente a qué me refiero.

Mi consejo: el ObjectdataSource úsalo para enlazar consultas a rejillas con el ánimo de mostrar datos y navegar por ellos. Las actualizaciones a la base de datos hazlas con un componente de capa intermedia propio que utilice DataSets o bien, si no necesitas capas, usa directamente un SqlDataSource para el enlace de los datos en ambos sentidos. No te dará problemas.

Friday, December 09, 2005 6:30:00 PM (Hora estándar romance, UTC+01:00)  #    Comments [4]   Programación  |  Trackback

Este tema me lo ha recordado hoy por la mañana un alumno de mi curso de "Técnicas de escritura de código seguro" de campusMVP. Me parece muy interesante comentarlo aquí y de paso contestarle a él el porqué de que ocurra... (Gracias por recordármelo, Iván).

Resulta que en ADO.NET 1.x (ya "clásico"), estamos acostumbrados a hacer algo similar a lo siguiente:

SqlCommand cmd = new SqlCommand("SELECT * FROM Tabla WHERE campo = @Parametro");
cmd.Parameters.Add("@Parametro", 1);

suponiendo que, por ejemplo, el parámetro "@Parametro" es un valor numérico.

Hasta aquí todo normal. El caso es que si en un programa escrito con ADO.NET 2.0 escribimos exactamente lo  mismo, aunque funcionará por compatibilidad, el compilador y el propio entorno de Visual Studio nos darán un aviso diciendo que el método 'Add' está obsoleto (deprecated en inglés) y que es mejor que usemos el método AddWithValue. ¿Por qué este cambio tan aparentemente arbitrario?

De hecho en Microsoft dicen que en versiones posteriores de.NET (la 3 o la que sea la siguiente) es posible que el método Add no esté soportado en absoluto :-(((

El motivo es el siguiente: cuando el parámetro es de otro tipo no numérico (una cadena o una fecha) no hay problemas. Pero si el parámetro es numérico como en el ejemplo anterior: ¿qué creeis que pasará cuando llamemos al código de ejemplo?

De entrada cabría pensar lo obvio: que se asigna el parámetro "@Parametro" con el valor '1'. Sin embargo no es así. Lo que ocurre en realidad es que el compilador mira cuál de los métodos Add sobrecargados se ajusta mejor y resulta que el que mejor lo hace es, precisamente el siguiente:

public SqlParameter Add(string parameterName, SqlDbType sqlDbType)

es decir, el segundo parámetro en lugar de interpretarse como un valor para ser asignado se interpreta como un valor de tipo SqlDbType que se ajusta mucho mejor (en concreto en nuestro ejemplo como SqlDbType.Binary que se corresponde con el 1). Es decir, en lugar de asignar el parámetro estamos indicando que el parámetro es de tipo binario, que no es precisamente lo que queríamos. Por supuesto la aplicación romperá. Un verdadero peligro.

De ahí que los chicos de Microsoft hayan tomado la dolorosa decisión de cambiar el método de asignación e, incluso, se planteen eliminar el anterior de la API. Razón no les falta pero es un poco fastidioso.

Así que ya sabes: vete acostumbrándote a usar AddWithValue.

NOTA: Esta es la definición del método Add sacada directamente del código fuente de System.Data.SqlClient en .NET 2.0:

[Obsolete("Add(String parameterName, Object value) has been deprecated.  Use AddWithValue(String parameterName, Object value).  http://go.microsoft.com/fwlink/?linkid=14202", false), EditorBrowsable(EditorBrowsableState.Never)]
public SqlParameter Add(string parameterName, object value);

Fíjate que el enlace del comentario nos lleva a la lista de APIs obsoletas en .NET 2.0. Vale la pena echarle un vistazo.

Wednesday, December 07, 2005 4:35:00 PM (Hora estándar romance, UTC+01:00)  #    Comments [0]   Programación  |  Trackback
Copyright © 2008 José Manuel Alarcón Aguín. All rights reserved.