Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
miércoles, 19 de diciembre de 2007
Hace unos días me topé con los Diez Mandamientos del Abogado, el célebre decálogo formulado por el jurista uruguayo Eduardo J. Couture, donde se recogen una serie de normas éticas y de conducta que deberían guiar las acciones de estos profesionales.

Me llamó la atención porque muchas de estas líneas son totalmente aplicables a otros ámbitos y colectivos, y por supuesto al mundo del desarrollo de software, dado que contiene perlas como: "Estudia, puesto que el Derecho evoluciona constantemente", o "Piensa, puesto que el Derecho se aprende estudiando pero se ejerce pensando".

Sin embargo, el que me hizo reflexionar fue el décimo:

10. AMA TU PROFESION. Trata de considerar la abogacía de tal manera, que el día en que tu hijo te pida consejo sobre su destino, consideres un honor para ti proponerle que se haga abogado.

Y es que la pregunta es: ¿estamos tan satisfechos de nuestra profesión como para aconsejar a un hijo, al que se supone que debemos desearle lo mejor del mundo, dedicarse a ella?

No son pocos los profesionales del desarrollo de software que, bien por circunstancias laborales, personales, falta de vocación u otros motivos, reniegan continuamente, y a veces no sin razón, de este mundillo. Y es que con mucha frecuencia se trata de un trabajo mal pagado, muy duro, con horarios imposibles, estrés continuo, necesidad de actualización de conocimientos frecuente, y escaso reconocimiento social.

Así está el patio como está. Cada vez más gente que huye dando el salto a otras profesiones, las matrículas en informática por los suelos, los ingenieros en continua protesta (;-)) ... en fin, un panorama nada alentador para el futuro.

A pesar de esto, yo soy de los que estarían encantados de que alguno de mis hijos (hijas, en este caso) se dedicara al mundo del desarrollo de software, siempre, eso sí, que consiguieran apasionarse por ello. Nunca se la recomendaría como una salida profesional más, pues creo que al igual que es una dedicación fascinante para el que le gusta, estoy seguro de que debe ser una auténtica pesadilla para el que no disfrute creando software.

Probablemente dedicándose a esto no conseguirán nunca hacerse ricas, ni famosas, ni llegar todos los días pronto a casa... pero bueno, tampoco siendo médicos, ni abogados, ni taxistas. Y, en cambio, tendrían por delante una profesión (y muchas veces hobby) donde volcar toda su creatividad y talento, la satisfacción de la investigación y aprendizaje continuos, de crear software que ayuden a otros a trabajar, estudiar o pasar buenos ratos de ocio... en fin, un universo de posibilidades.

¿Y tú, recomendarías a tu hijo que se dedicara al mundo del desarrollo?

Publicado en: www.variablenotfound.com
martes, 18 de diciembre de 2007
Hace unos días Rosario C. realizaba, a través de un comentario en el post "Llamar a métodos estáticos con ASP.Net Ajax", una consulta sobre un problema con el se había topado al intentar retornar DataSets desde un método de página (PageMethod) de ASP.Net Ajax, un tema tan interesante que vale la pena escribir un post en exclusiva.

Recordemos que los métodos estáticos de página son una interesante capacidad que nos ofrece este framework para poder invocar desde cliente (javascript) funciones de servidor (codebehind) de una forma realmente sencilla. Además, gracias a los mecanismos de seriación incluidos, y como ya vimos en el post "Usando ASP.NET AJAX para el intercambio de entidades de datos", es perfectamente posible devolver desde servidor entidades o estructuras de datos complejas, y obtenerlas y procesarlas directamente desde cliente utilizando javascript, y viceversa.

Es es ahí donde reside el problema con los DataSets: precisamente este tipo de datos no está soportado directamente por el seriador JSON incorporado, que es el utilizado por defecto, de ahí que se genere una excepción como la que sigue:

System.InvalidOperationException
A circular reference was detected while serializing an object of type 'System.Globalization.CultureInfo'.
en System.Web.Script.Serialization.JavaScriptSerializer.SerializeValueInternal(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat serializationFormat)\r\n en System.Web.Script.Serialization.JavaScriptSerializer.SerializeValue(Object o, StringBuilder sb, Int32 depth, Hashtable objectsInUse, SerializationFormat [...]

Pero antes de nada, un inciso. Dado que la excepción se produce en el momento de la seriación, justo antes de enviar los datos de vuelta al cliente, debe ser capturada en cliente vía la función callback especificada como último parámetro en la llamada al método del servidor:

// Llamada a nuestro PageMethod
PageMethods.GetData(param, OnOK, OnError);
[...]


function OnError(msg)
{
// msg es de la clase WebServiceError.
alert("Error: " + msg.get_message());
}
 
Aclarado esto, continuamos con el tema. La buena noticia es que el soporte para DataSets estará incluido en futuras versiones de la plataforma. De hecho, es perfectamente posible utilizar las librerías disponibles en previews disponibles en la actualidad. Existen multitud de ejemplos en la red sobre cómo es posible realizar esto, aunque será necesario instalar alguna CTP y referenciar librerías en el Web.config. Podéis echar un vistazo a lo descrito en este foro.

Esta solución sin embargo no es muy recomendable en estos momentos, puesto que estaríamos introduciendo en un proyecto librerías y componentes que, en primer lugar, están ideados sólo para probar y tomar contacto con las nuevas tecnologías, y, en segundo lugar, las características presentadas en ellos pueden modificarse o incluso eliminarse de las futuras versiones.

Existen otras formas de hacerlo, como la descrita en siderite, que consiste en crear un conversor justo a la medida de nuestras necesidades extendiendo la clase JavaScriptConverter, aunque aún me parecía una salida demasiado compleja al problema.

Sin embargo, la solución que he visto más simple es utilizar XML para la seriación de estas entidades, lo que se puede lograr de una forma muy sencilla añadiendo al método a utilizar un atributo modificando el formato de respuesta utilizado por defecto, de JSON a XML.

He creado un proyecto de demostración para VS2005, esta vez en VB.NET aunque la traducción a C# es directa, con una página llamada DataSetPageMethods.aspx en el que se establece un PageMethod en el lado del servidor, con la siguiente signatura:

<WebMethod()> _
<Script.Services.ScriptMethod(ResponseFormat:=ResponseFormat.Xml)> _
Public Shared Function ObtenerClientes(ByVal ciudad As String) As DataSet
 
Este método estático obtendrá de la tradicional base de datos NorthWind (en italiano, eso sí, no tenía otra a mano O:-)) un DataSet con los clientes asociados a la ciudad cuyo nombre se recibe como parámetro.

Como podréis observar, el método está precedido por la declaración de dos atributos. El primero de ellos, WebMethod(), lo define como un PageMethod y lo hará visible desde cliente de forma directa. El segundo de ellos redefine su mecanismo de seriación por defecto, pasándolo a XML. Si queréis ver el error al que hacía referencia al principio, podéis modificar el valor del ResponseFormat a JSON, su valor por defecto.

Desde cliente, al cargar la página rellenamos un desplegable con las distintas ciudades. Cuando el usuario selecciona un elemento y pulsa el botón "¡Pulsa!", se realizará una llamada asíncrona al PageMethod, según el código:

function llamar()
{
PageMethods.ObtenerClientes($get("<%= DropDownList1.ClientID %>").value , OnOK, OnError);
}
 
La función callback de notificación de finalización de la operación, OnOk, recibirá el DataSet del servidor y mostrará en la página dos resúmenes de los datos obtenidos que describiré a continuación. He querido hacerlo así para demostrar dos posibles alternativas a la hora de procesar desde cliente los datos recibidos, el DataSet, desde servidor.

En el primer ejemplo, se crea en cliente una lista sin orden con los nombres de las personas de contacto de los clientes (valga la redundancia ;-)), donde muestro cómo es posible utilizar el DOM para realizar recorridos sobre el conjunto de datos:

var nds = data.documentElement.getElementsByTagName("NewDataSet");
var rows = nds[0].getElementsByTagName("Table");
var st = rows.length + " filas. Personas:  br />";
st += "<ul>";
for(var i = 0; i < rows.length; i++)
{
st += "<li>" + getText(rows[i].getElementsByTagName("Contatto")[0])+ "</li>";
}
st += "</ul>";
etiqueta.innerHTML = st;
 
En el segundo ejemplo se muestra otra posibilidad, la utilización de DOM para crear una función recursiva que recorra en profundidad la estructura XML mostrando la información contenida. La función javascript que realiza esta tarea se llama procesaXml.

Como se puede comprobar analizando el código, utilizar la información del DataSet en cliente es necesario, en cualquier caso, un conocimiento de la estructura interna de est tipo de datos, lo cual no es sencillo, por lo que recomendaría enviar y gestionar los DataSets en cliente sólo cuando no haya más remedio.

Una alternativa que además de resultar menos pesada en términos de ancho de banda y proceso requerido para su tratamiento es bastante más sencilla de implementar sería enviar a cliente la información incluida un un array, donde cada elemento sería del tipo de datos apropiado (por ejemplo, un array de Personas, Facturas, o lo que sea). La seriación será JSON, mucho más optimizada, además de resultar simplísimo su tratamiento en javascript.

El proyecto de demostración también incluye una página (ArrayPageMethods.aspx) donde está implementado un método que devuelve un array del tipo Datos, cuya representación se lleva también a cliente y hace muy sencillo su uso, como se puede observar en esta versión de la función de retorno OnOk:

function OnOK(datos)
{
var etiqueta = $get("lblMensajes");
var s = "<ul>";
for (var i = 0; i < datos.length; i++)
{
s += "<li>" + datos[i].Contacto;
s += ". Tel: " + datos[i].Telefono;
s += "</li>";
}
s += "</ul>";
etiqueta.innerHTML = s;
}
 

Por último, comentar que el proyecto de ejemplo funciona también con VS2008 (Web Developer Express), aunque hay que abrirlo como una web existente.

Publicado en: www.variablenotfound.com.


Enlace: Descargar proyecto VS2005.
martes, 11 de diciembre de 2007
He encontrado un post muy interesante sobre hábitos comunes en personas altamente innovadoras y creativas, extraídos del libro de Scott Berkun, "The myths of innovation", que me permito interpretar y recoger a continuación.

1. Trabajar duro

La innovación implica algo más que tener grandes ideas. Hay que tener fe, trabajar duro y luchar en contra de la marea para conseguir nuestros objetivos. Como dijo Thomas Alba Edison:
"La invención es un 1% de inspiración y un 99% de transpiración"

2. Evitar inhibiciones

Estas inhibiciones nos hacen sentirnos limitados y bloqueados, necesitamos abrir la mente eliminando asunciones y restricciones, abrirnos a nuevas ideas y soluciones sin limitaciones mentales de ningún tipo. La innovación tiene más que ver con la psicología que con el intelecto.

3. Asumir riesgos, cometer errores

El miedo al fracaso es uno de los causantes de que fabriquemos nuestras propias limitaciones. Está claro que algunas ideas fallarán durante el aprendizaje, por lo que es conveniente construir prototipos frecuentemente, probarlos, someterlos a discusión, usar el feedback y realizar cambios de forma incremental.

En lugar de tratar los errores como fracasos, es conveniente pensar en ellos como experimentos. En palabras de Scott Berkun,
"Los experimentos son los errores previstos para aprender algo deliberadamente"
Así, no debemos flagelarnos con los fallos, aceptémoslos y utilicemos la lección aprendida para encontrar nuevas y mejores soluciones. Los errores forman parte del camino hacia el éxito, positivémoslos; como decía Edison,
"No me he equivocado. Simplemente he encontrado 10.000 soluciones que no funcionaban"

4. Escapar

Cuando más relajados estamos internamente, más receptivos somos respecto a nuestra creatividad. Este es el motivo por el que frecuentemente tenemos grandes ideas en el cuarto de baño, en la cama o estando solos.

Cada uno de nosotros contacta con "su lado más creativo" de una forma diferente, sólo es cuestión de averiguar cómo. Los hay que caminan, que hacen footing, otros escuchan música... ¿cuál es tu método?

5. Anotar las ideas

Muchos creativos y personas innovadoras tienen siempre a mano algo donde apuntar ideas y pensamientos: libretas, post-it, papel, o algo más tecnológico. Da igual el soporte, lo importante es disponer de una forma de capturar sus pensamientos, pensar sobre el papel, romper con las inhibiciones y comenzar el proceso creativo.

El famoso manuscrito de Leonardo Da Vinci fue comprado por Bill Gates por más de 30 millones de dólares.

6. Encontrar patrones y combinarlos

Las ideas aparecen partiendo de otras ideas. Por ejemplo, Edison no fue el inventor de las bombillas, pero hizo que pudieran durar más tiempo creando un filamento de carbono e introduciéndolo en el interior de un cristal.

Ábrete a nuevas ideas, estúdialas y piensa en combinarlas para mejorar soluciones existentes.

7. Ser curioso

Muchos innovadores son simplemente personas curiosas a los que les gusta resolver problemas. Es una buena costumbre observar soluciones existentes y preguntarse si hay otras vías para hacerlo, cuestionarse las normas y métodos existentes.

Publicado en: Variable Not Found.
lunes, 10 de diciembre de 2007
Hasta la versión 3.0 de C#, la declaración de una variable se debía realizar indicando su tipo de datos antes del identificador elegido para la misma. También era muy frecuente definir en ese mismo momento su valor inicial, siguiendo un patrón similar al siguiente:
  string s = "cadena";
Sin embargo, la declaración anterior es redundante. Si la constante "cadena" es un string, ¿por qué hace falta indicar que la variable s también lo es?

Las variables locales implícitamente tipadas permiten obviar en su declaración el tipo que tendrán, dejando al compilador la tarea de averiguar cuál será en función de las variables o constantes que se usen al inicializarlo. Por tanto, será posible escribir código como:
  var x = XmlDateTimeSerializationMode.Local;
Y será equivalente a:

XmlDateTimeSerializationMode x = XmlDateTimeSerializationMode.Local;
 
Creo que no hace falta decir cuál de ellas es más cómoda a la hora de programar, ¿no? Simplemente hemos indicado con la palabra var que preferimos que sea el compilador el que haga el trabajo sucio.

Otro contexto donde este tipo de variables pueden facilitarnos la vida de forma frecuente es en los bucles for, foreach y bloques using:
  // Un ejemplo de bucle...
var chars = "Saludicos".ToCharArray();
foreach (var ch in chars)
{
Console.Write(ch); // ch es char
}

// Y ahora un bloque using...
using (var ctx = new AppContext())
{
// usamos ctx, que es de tipo AppContext
}
 
También es importante la existencia de esta nueva forma de declaración para posibilitar la creación de objetos de tipo anónimo, es decir, aquellos cuya especificación de clase se crea en el mismo momento de su definición. Por tanto, una declaración como la siguiente será válida:
  var x = new { Nombre = "Juan", Edad = 23 };
Para no desviarme del objetivo de este post, otro día hablaremos más detenidamente de las clases anónimas, aunque adelantaré que una vez compilado el código anterior el resultado será algo parecido al siguiente:

class __Anonymous1
{
private string nombre ;
private int edad ;
public string Nombre { get { return nombre ; } }
public int Edad { get { return edad ; } }
public __Anonymous1(string nombre, int edad)
{
this.nombre = nombre;
this.edad = edad;
}
}
...
__Anonymous1 x = new __Anonymous1("Juan", 23);

 
Existen ciertas reglas de obligado cumplimiento para usar variables locales implícitamente tipadas:
  • La declaración debe incluir un valor de inicialización. En otras palabras, no será posible usar en una línea var i;, puesto que el compilador no podría inferir el tipo en este momento.
  • El valor de inicialización debe ser una expresión evaluable como clase en tiempo de compilación. Expresamente prohibido el valor null, es decir, nada de var n = null;, puesto que el compilador no sabría de qué tipo se trata; eso sí, si fuera necesario, podría forzarse un casting var str = null as string;.
  • La declaración no puede incluir más de una variable. Una línea como var i = 1, s = "hola"; generará un error en compilación.
  • El inicializador no puede referirse a la propia variable declarada, obviamente.
  • Sólo pueden utilizarse como variables locales en bloques de código, en bucles for y foreach y como recurso de un bloque using.

Por último, me gustaría añadir un par de detalles que considero interesantes.

Seguimos teniendo Intellisense...Primero, el hecho de utilizar la palabra "var" y no indicar de forma explícita el tipo puede hacernos pensar que estamos utilizando tipado dinámico en tiempo de ejecución, como lo hacemos con Javascript, sin embargo esto no es así. Como he comentado anteriormente, el compilador toma el tipo del lado derecho de la asignación de inicialización, por lo que si la variable no es inicializada se produce un error de compilación:
Implicitly-typed local variables must be initialized
(Las variables locales implícitamente tipadas deben ser inicializadas)
Por ello, un entorno como Visual Studio sabe en todo momento el tipo exacto de que se trata, y nos puede ofrecer las ayudas en la codificación, detección de errores sintáticos e intellisense, como se puede observar en la imagen.

Segundo, y muy interesante. Como ya comenté en su momento, la nueva versión de Visual Studio permite la generación de ensamblados para versiones anteriores de la plataforma, es decir, que es posible escribir código C# 3.0 y generar un ensamblado para el framework 2.0. Esto, a efectos prácticos, implica que una vez demos el salto definitivo a VS2008 podremos usar las variables locales de tipo implícito, así como otras novedades del lenguaje, incluso en aplicaciones diseñadas para .NET 2.0.



Publicado en: Variable Not Found.
domingo, 9 de diciembre de 2007
Browsershots.org es un servicio dedicado a realizar capturas de pantalla de una página web utilizando distintos navegadores y sistemas operativos, y gratis... siempre que no tengas mucha prisa.

El invento utiliza un sistema en el que las solicitudes de captura de pantalla se distribuyen entre "factorías", máquinas de los propios usuarios de la comunidad, que prestan parte del ancho banda y capacidad de sus equipos para realizar estas tareas. De hecho, hay unas sencillas instrucciones para crear una nueva factoría de capturas de pantalla y apoyar así el proyecto.

Para enviar la solicitud de captura es necesario indicar la URL a comprobar, así como los navegadores y características de la captura, combinando entre los siguientes elementos:
  • Navegadores:
    • para Linux: Epiphany 2.14, Firefox 1.5, Firefox 2.0, Galeon 2.0, Iceweasel 2.0, Konqueror 3.5, NetFront 3.2, Opera 9.24, Opera 9.5
    • para Windows: Firefox 1.5, Firefox 2.0, MSIE 5.5, MSIE 6.0, MSIE 7.0, Opera 9.24, Safari 3.0.
    • para Mac OS: Firefox 2.0, Safari 1.3, Safari 2.0, Safari 3.0
  • Anchos de pantalla: 640, 800, 1024, 1280, 1400 y 1600 pixels.
  • Profundidad de color: 8, 16, 24 y 32 bits por pixel.
  • Javascript: deshabilitado, habilitado, v1.3, v1.5, v1.6, v1.7, v2.0
  • Java: deshabilitado o habilitado.
  • Flash: deshabilitado o habilitado.



Una vez enviado el formulario con las características deseadas, el trabajo pasará a la cola de cada una de las factorías desde se generarán las capturas de pantalla. El tiempo de espera dependerá del número de solicitudes pendientes, pero podremos conocer una estimación global y detallada desde la misma página:



Conforme las factorías van terminando su trabajo, enviarán una copia de la captura al servidor central, que publicará en la misma página web las imágenes obtenidas. Simplemente recargando la página obtendremos información actualizada del proceso y podremos ampliar las imágenes obtenidas hasta el momento pulsando sobre ellas:



Es importante destacar que la solicitud que enviamos tiene una validez de 30 minutos. Si en ese tiempo no hemos obtenido los resultados deseados, debemos extender (hay un botón para ello en la página) este tiempo, pues de lo contrario expirará. De esta forma, los señores de ScreenShots.org pueden asegurar que estamos todavía en la página y que seguimos interesados en las capturas de pantalla que hemos solicitado previamente.

Las capturas generadas son de un tamaño y calidad excelente, suficientes para detectar problemas en el diseño o las habituales incompatibilidades entre los motores de renderizado de páginas web de los navegadores.

El único problema es el tiempo que puede llegar a tardar la realización de un conjunto completo de capturas. En distintas pruebas me he encontrado con colas de más de una hora, aunque, también he de decirlo, la mayoría de las capturas se han generado en los primeros diez minutos. Además, por una módica cantidad mensual (10€/15$) puedes adquirir un mes de prioridad en el servicio, por lo que, según aseguran, obtendrás todas las pantallas en menos de 3 minutos.

Publicado en: Variable Not Found.
jueves, 6 de diciembre de 2007
Por cortesía de Mergia, ya es posible ver en vivo el sitio web de demostración de NiftyDotNet, el componente que ha supuesto mi primera aportación al mundo del software libre.

NiftyDotNet, para el que no lo conozca, es un componente ASP.Net para las plataformas Mono y Microsoft, que encapsula la librería javascript Nifty Corners Cube para conseguir redondear las esquinas de elementos de una página web de una forma realmente sencilla. Basta con arrastrar los controles sobre un Webform, indicarles los elementos que se verán afectados y listo.

Ejemplo de uso de NiftyDotNet

Publicado en: Variable Not Found.