Y después de ASP.NET MVC 2… ¿qué?

ASP.NET MVC Estando todavía calentito el horno del que acaba de salir ASP.NET MVC 2, es curioso conocer algo sobre el próximo plato que están preparando Haack y su equipo: ¡ASP.NET MVC 3!

Ya se han publicado algunas de las líneas y objetivos que guiarán los futuros desarrollos y determinados detalles que probablemente serán incluidos, aunque obviamente todavía pueden variar sustancialmente. Las principales áreas de atención son en estos momentos:

  • aumentar la productividad de los desarrolladores,
  • facilitar aún más el uso de Ajax,
  • incluir mejoras arquitecturales,
  • y aumentar el rendimiento.
El aumento de productividad irá ligado a la introducción de un nuevo conjunto de helpers destinados a realizar tareas muy comunes en el desarrollo de aplicaciones web, como la implementación de rejillas de datos (grids) paginados.

Asimismo, se dará soporte a nuevos atributos de validación además de los ya incluidos en las anotaciones de datos de ASP.NET 4.

Otra línea en estudio es la introducción de herramientas basadas en línea de comandos, alternativas a las incluidas en Visual Studio.

Para facilitar el uso de Ajax se introducirán nuevos helpers que simplifiquen la implementación de escenarios habituales, como los controles de selección de fecha o cuadros de edición con auto-completado.
También se considera interesante ampliar los helpers existentes para permitir la actualización parcial de varias zonas de la página, e incluir soporte para plantillas en cliente, que permitan añadir el marcado muy rápidamente a datos retornados en formato JSON desde el controlador.

Desde el punto de vista arquitectural, se planea seguir flexibilizando el framework, permitiendo nuevos puntos de inyección de dependencias, como la instanciación de clases del Modelo desde el binder, o en filtros de acción.Piezas de un sistema

Se prevé también la creación de una factoría de controladores para MEF (Managed Extensibility Framework) que permitan ampliar las funcionalidades de un sistema mediante sus sencillos mecanismos de extensibilidad, sin necesidad de recompilar.

Se incrementarán también las herramientas de generación de andamiaje de aplicaciones para acelerar la implementación de escenarios comunes, como los habituales CRUD.

Y respecto al rendimiento, el equipo está estudiando nuevas técnicas para mejorar el cacheo de respuestas, y soluciones como aumentar el control sobre el estado de sesión, permitiendo activarlo o desactivarlo para mejorar la eficiencia.

En cualquier caso, lo que sí ha dejado claro Phil es que ASP.NET MVC 2 será la última versión del framework con soporte para ASP.NET 3.5 SP1.

Fuente: Roadmap de ASP.NET MVC.
Publicado en: Variable not found.
Hey, ¡estoy en twitter!
Publicado por José M. Aguilar | con no comments

ASP.NET MVC 2 final, ya disponible

¡ASP.NET MVC 2! Hace unos minutos en Twitter se propagaba la noticia de la disponibilidad de la versión final de ASP.NET MVC 2, que puedes descargar ya desde el sitio web de Microsoft.

Según se indica en el documento de notas de la revisión, y como era previsible, no se puede instalar en equipos con Visual Studio 2010 RC. Por lo demás, no se ha introducido ningún cambio destacable desde la anterior revisión, la segunda Release Candidate.

También se ha publicado el código fuente en CodePlex.

Publicado en: Variable not found.

Publicado por José M. Aguilar | con no comments

30 Leyes epónimas relacionadas con el desarrollo de software (I)

Un epónimo es el nombre de una persona o lugar que cede su nombre a una época, pueblo, unidad, ley, etc. Son epónimos por ejemplo "Diesel", cedido por Rudolf Diesel, inventor de este tipo de motores, o "Hamburguesa", infame trozo de carne picada cuyo nombre procede de su lugar de origen.

Hace unos años, el gran Phil Haack posteó sobre leyes epónimas relacionadas con el desarrollo de software en "19 Eponymous Laws Of Software Development", y seleccioné las que me resultaron más interesantes en un par de posts.

Ahora los he vuelto a maquetar y les he añadido un nuevo conjunto de leyes muy interesantes para todos los que nos dedicamos al mundo del desarrollo de software, y muchas de ellas incluso aplicables a otros ámbitos.

John Postel

1. Ley de Postel

Sé conservador en lo que hagas y liberal en lo que aceptes de los demás

Esta frase, de Jonathan Bruce Postel, también llamada Principio de Robustez, es la piedra filosofal del protocolo TCP, y está recogida en la RFC 793, sección 2.10, de septiembre de 1981.

 


C. Northcote Parkinson

2. Ley de Parkinson

El trabajo se extiende siempre hasta rellenar la totalidad del tiempo disponible para completarlo

Esta ley fue postulada inicialmente en 1955 por C. Northcote Parkinson en The Economist y más tarde entró a formar parte de su libro, basado principalmente en las experiencias de la administración británica.

 

 


Vilfredo Pareto

3. Principio de Pareto

Para muchos fenómenos, el 80% de las consecuencias derivan del 20% de las causas

Vilfredo Pareto fue un estudioso de la economía y sociología del siglo XIX, y se fijó que el 80% de las propiedades y riqueza estaban repartidas entre el 20% de la población, enunciando su famoso principio. A partir de ahí, se piensa que esta proporción es cierta en múltiples ocasiones, hasta en el número de bugs en el código fuente de un software, o el tiempo de desarrollo de funcionalidades.

 


 

 


Ted Sturgeon

4. Revelación de Sturgeon

El noventa por ciento de cualquier cosa es basura

Theodore Sturgeon era un autor de ciencia ficción americano que escribió esta frase defendiendo a este tipo de literatura de críticos que opinaban que el 90% era una porquería.

Hay un corolario que dice "La revelación de Sturgeon es cierta salvo para la basura, donde el 100% es basura".

 



Lawrence J. Peter

5. El principio de Peter

En una jerarquía, todo individuo tiende a subir hasta alcanzar su nivel de incompetencia

Seguro que todos conocéis ejemplos de ello: un fabuloso desarrollador es ascendido a directivo en una empresa, la cual gana un gestor pésimo y pierde un programador excelente. Doble penalización. Lawrence J. Peter, pedagogo de profesión, ya lo enunció en 1968 en el libro El principio de Peter.



Douglas Hofstadter

6. Ley de Hofstadter

La realización de un trabajo siempre dura más de lo esperado, incluso habiéndose tenido en cuenta la Ley de Hofstadter

Esta genial y recursiva Ley creada por el científico, filósofo y académico estadounidense Douglas Hofstadter es absolutamente cierta. Y si no, pensad un poco, ¿cuántas veces habéis estimado plazos en un desarrollo, lo habéis incrementado de forma considerable por los imprevistos y aún así os habéis quedado cortos?

 

 


Edward A. Murphy

7. Ley de Murphy

Si algo puede ir mal, lo hará

La famosa ley, también enunciada en forma de tostada que recurrentemente cae con la mantequilla hacia abajo, fue dictada por Edward A. Murphy, Jr., mientras trabajaba para la fuerza aérea americana como ingeniero, diseñando un sistema de cohetes experimental. Sería lógico pensar que el experimento acabó en tragedia, pero parece ser que la creación y consideración de esta ley les ayudó a evitar graves desastres en sus pruebas.

 



Frederick Brooks

8. Ley de Brooks

Incluir trabajadores en un proyecto retrasado hará que éste avance aún más lentamente

Fred Brooks postuló esta ley en su famoso libro The Mythical Man-Month: Essays on Software Engineering como resultado de su experiencia en IBM. Existen variantes y corolarios como "Una señora es capaz de tener un hijo en nueve meses, pero este plazo no puede disminuir por muchas mujeres embarazadas que pongamos a ello". Simplemente genial.

 

 


No se dejó hacer la foto ;-) Este es uno de los diagramas usados en el paper 'How Do Committees Invent?' donde presentaba sus ideas

9. Ley de Conway

Cualquier software refleja la estructura organizacional de quien lo produjo

A pesar de que suena a guasa, la ley de Melvin Conway no puede ser más cierta. Una empresa con tres grupos de desarrollo tenderá a generar software distribuido en tres subsistemas, reflejo fiel de las relaciones entre los grupos participantes. Y por cierto, extrapolando un poco... ¿habéis pensado alguna vez que el software que se hace en vuestra empresa es un desastre? ¿creéis que con esta ley podríais obtener alguna conclusión? ;-D

 

 


Auguste Kerckhoffs

10. Principio de Kerckhoffs

En términos de criptografía, un sistema debería ser seguro incluso si todo sobre el mismo se conoce públicamente, salvo una pequeña porción de información

Es increíble que Auguste Kerckhoffs lingüista y criptógrafo alemán, enunciara en el siglo XIX este principio, base de todos los sistemas de criptografía de clave pública actuales.

 

 

 

 

Sigue leyendo en 30 Leyes epónimas relacionadas con el desarrollo de software (II)

Hey, ¡estoy en twitter!

Eliminar una entrada incorrecta del historial en IE8

Mi gran descubrimiento del día probablemente será una chorrada para la inmensa mayoría, pero también seguro que hay alguien que, como un servidor, no se había percatado hasta el momento de este detalle de IE8.

Como sabéis, la barra de direcciones de este navegador nos permite escribir la URL de la página que queremos visitar, pero podemos ahorrar mucho tiempo si tecleamos únicamente algunos caracteres contenidos en la misma: Internet Explorer buscará en el historial de sitios visitados y en los favoritos, mostrando aquellos sitios que contengan la cadena buscada.

Por ejemplo, escribiendo “vari”, mi Explorer 8 muestra las siguientes sugerencias:

IE8 sugiriendo sitios que contienen el texto "vari"
Sin embargo, si alguna vez introducís una dirección incorrecta, por ejemplo tecleando un carácter de más, u omitiendo alguno, esta URL será añadida al historial, y se mostrará como sugerencia también, efecto bastante incómodo.

En la captura anterior, se puede ver que Internet Explorer ofrece “varialablenotfound.com”, una dirección que tecleé de forma incorrecta tiempo atrás.

Pues mi gran descubrimiento es este: si paseamos el ratón sobre esta sugerencia, o la seleccionamos usando la tecla flecha abajo, aparece a la derecha un icono que nos permite eliminarla, para que no nos moleste más:

Icono para eliminar la entrada de las sugerencias
La explicación de que no lo haya visto hasta ahora es muy sencilla: al aparecer a la derecha del desplegable queda fuera del foco de atención en ese momento, que está justo en el lado contrario, y especialmente en pantallas con resolución muy ancha.

Por curiosidad he probado a ver si existía algo parecido en Chrome, Firefox y Opera, y ninguno incluye este detalle, que nos viene de perlas a los que somos algo muñones ;-)

Crossposteado desde Eliminar una entrada incorrecta del historial en IE8.

Publicado por José M. Aguilar | con no comments
Archivado en: ,

Cambios en el retorno de datos JSON con MVC 2

aspnetmvc Con objeto de mejorar la seguridad de nuestras aplicaciones, la Release Candidate de ASP.NET MVC 2 introdujo un cambio importante en la forma de procesar peticiones que retornan información serializada como JSON: por defecto, ahora sólo se responde a peticiones de tipo POST.

Dado que en MVC 1.0 era justo al contrario, esta pequeña reorientación hace que aplicaciones que antes funcionaban correctamente dejen de hacerlo al migrarlas a la última versión del framework. Un ejemplo lo tenemos con las aplicaciones que aprovechan la potencia de jqGrid para mostrar y editar rejillas de datos. Recordemos que el intercambio de información entre cliente y servidor se realiza mediante llamadas Ajax que retornan siempre datos en notación JSON.

Los síntomas que notaremos en estos casos son muy simples: ¡las acciones que deberían devolvernos información dejan de hacerlo! En algunas ocasiones, dependiendo del uso que se dé en la vista a la la información retornada, es posible que aparezcan errores de script en el navegador, pero otras ni siquiera eso.

En cualquier caso, no es difícil dar con la solución. Con Firebug, Fiddler, o cualquier herramienta que nos permita monitorizar las peticiones, podremos observar que en la petición Ajax se está produciendo un error HTTP 500 (error interno de servidor):

image

Y si seguimos profundizando en dicha petición, podemos ahora conocer el mensaje descriptivo que envía el servidor, un auténtico detalle por parte del equipo de desarrollo de ASP.NET MVC 2, en el que incluso nos indican la solución al problema:

<html>
<head>
<title>This request has been blocked because sensitive 
information could be disclosed to third party web sites when 
this is used in a GET request. 
To allow GET requests, set JsonRequestBehavior to AllowGet.</title>
<style>

Ante esta situación, podemos optar por dos soluciones. La primera sería indicar explícitamente en la instancia del resultado, de tipo JsonResult, que queremos permitir el método GET, así:

return Json(data, JsonRequestBehavior.AllowGet);

Y otra posibilidad sería realizar las peticiones Ajax utilizando el método POST, algo que podemos conseguir muy fácilmente la mayoría de las veces. En el caso de los ejemplos con jqGrid, sería sustituyendo el ’GET’ por ‘POST’ en código:

[...]
jQuery("#list").jqGrid({
    url: '<%= Url.Action("ObtenerDatosGrid") %>',
    datatype: 'json',
    mtype: 'POST',    // <- Aquí
    colNames: ['Apellidos', 'Nombre', 'Fecha Nac.', 'Email'],
[...]

¡Gracias, Maxi, por los comentarios que inspiraron este post!

Crossposteado desde Cambios en el retorno de datos JSON con MVC 2 @ Variable not found

Error al conectarse al administrador de deshacer del archivo de código fuente

“Error al conectarse al administrador de deshacer del archivo de código fuente c:\blahblah\archivo.designer.cs” es un mensaje con el que Visual Studio (tanto la versión 2005 como 2008) me ha abofeteado en numerosas ocasiones cuando estoy desarrollando sitios web ASP.NET.

Y la verdad, es uno de esos casos en los que no sabes qué hacer: recompilas, limpias la solución, abres y cierras el IDE… pero nada, cuando le da por ahí, no hay forma de hacerlo entrar en razón:

Error al conectarse al administrador de deshacer del archivo de código fuente

Como ya habría corregido el problema en el momento de escribir este post y no pude reproducirlo para capturar la ventana,
os muestro cómo luce en su versión en inglés ;-)


imageBuscando un poco de información, parece ser que se trata de un problema que aparece al editar el archivo .aspx mientras se está depurando la página, siguiendo una secuencia determinada.

Una de las formas de solucionarlo es un poco bestia, pero funciona (al menos en VS 2008): basta con eliminar el archivo xx.designer.cs indicado por el error (previo backup, por si acaso, aunque hasta ahora no me ha hecho falta nunca ;-)) y forzar al entorno a que lo genere de nuevo, pulsando el botón derecho del ratón sobre el archivo .aspx y seleccionando la opción “Convertir en aplicación web” que habrá aparecido en el mismo.

De momento no le he visto contraindicaciones, y estoy de lo más contento :-)

 

 

 

 

Hábilmente crossposteado desde: Error al conectarse al administrador de deshacer del archivo de código fuente  @ Variable not found
Hey, ¡estoy en twitter!

Publicado por José M. Aguilar | con no comments

Cambiar el location.href conservando el Referer

¿Se pierde el Referer al cambiar el location.href? El HTTP Referer es una variable que nos envían los navegadores en cada petición, permitiéndonos conocer de dónde viene el usuario, es decir, la página donde se encontraba el enlace, botón o formulario cuya activación ha provocado el salto a nuestro sitio.

Por ejemplo, si un servidor recibe esta solicitud:

GET / HTTP/1.1
Accept: image/gif, image/jpeg, image/pjpeg, image/pjpeg, (...omitido...), */*
Referer: http://www.google.es/search?hl=es&rls=ig&q=variable&btnG=Buscar&meta=&aq=f&oq=
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; (...omitido...) )
Accept-Language: es
Accept-Encoding: gzip, deflate
Host: www.variablenotfound.com
Connection: Keep-Alive

analizando el campo Referer se puede determinar de forma muy sencilla que se trata de un usuario que proviene de Google, y que estaba buscando el término “variable”. Esta característica aporta una información, que aunque no es demasiado fiable (se puede alterar y desactivar muy fácilmente),  se utiliza con mucha frecuencia con fines estadísticos, o para adecuar el contenido de la página a las intenciones del usuario.

Sin embargo, como se trata de una característica manejada directamente por el navegador, podemos encontrarnos con que no todos actúan de la misma manera... o en otras palabras, es Internet Explorer es el que actúa de forma diferente al resto ;-)

Si la visita a nuestra página se produce como consecuencia de la ejecución de un script que ha modificado dinámicamente el location.href, o ha invocado a location.replace, Internet Explorer saltará a la nueva dirección, pero no enviará el Referer en los encabezados de la petición, por lo que en el punto de destino no se podrá conocer el origen de la misma.

Por tanto, un código como el siguiente hará que la página editar.asp no pueda conocer desde dónde se le ha referenciado:

<input type="button" value="editar" onclick="location.href='editar.asp';">

Dado que Microsoft todavía está considerando modificar este comportamiento, hay que darle un poco las vueltas para conseguirlo provocando el salto hacia la página de destino sin utilizar los mecanismos citados anteriormente.

Una forma muy ingeniosa que he encontrado se basa en crear dinámicamente un tag <a> con su correspondiente href establecido, y simular la pulsación del mismo invocando al evento click:

function navigateWithReferrer(url) {
   var fakeLink = document.createElement("a");
   if (typeof (fakeLink.click) == 'undefined')
      location.href = url; 
      else {
         fakeLink.href = url;
         document.body.appendChild(fakeLink);
         fakeLink.click();   
      }
}

Visto en: Velocity Reviews
Crossposteando desde el location.href: Cambiar el location.href conservando el Referer @ Variable not found.

Por sorpresa… ASP.NET MVC 2 RC 2

Hace unas horas Phil Haack ha anunciado la publicación de la penúltima revisión del framework MVC 2 antes de la versión final.

Según se recoge en el documento de notas de la revisión, la única característica añadida ha sido el cambio en la forma de validar las entidades del Modelo. En versiones anteriores, durante el binding se evaluaban las restricciones establecidas en las propiedades cuyos valores eran recibidos por el servidor, mientras que ahora se valida la entidad completa.

También, aunque de menor calado, han sido introducidos otras mejoras interesantes, como el nuevo método Peek() del diccionario TempData, el incremento del rendimiento en varios puntos, o una nueva forma de indicar parámetros opcionales en las rutas, que son eliminados para no interferir otros datos recibidos en la petición.

Desafortunadamente, aún no podemos probarla con las versiones preliminares de VS2010, y sólo funcionará con la versión 2008 del IDE. :-(

Enlaces:

Publicado en: Variable not found.

Publicado por José M. Aguilar | con no comments

Actualizando el equipo con Disk2Vhd

Por cosas de la procrastinación, tenía una máquina pendiente de formatear desde hace unos años ;-P, y he aprovechado el fin de semana para hacerlo. Como sabréis, esto no es tarea fácil, se requiere mucho pragmatismo, gran concentración y, principalmente, vencer al Diógenes digital que todos llevamos dentro ;-D.

Y claro, una vez que nos ponemos, el problema es cómo conseguir minimizar los daños colaterales. Esta máquina, aunque algo antigua, estaba todavía en uso y tenía gran cantidad de archivos y software instalado en su disco de sistema. También contaba con un disco exclusivamente para datos, pero éste no me suponía ningún problema.

En estos casos normalmente no basta con hacer un salvado del disco duro, a lo bruto, sobre otro disco, formatear y listo; así sólo conseguiremos tener acceso desde la nueva máquina a los ficheros físicos del sistema anterior, pero no podremos realizar tareas de nivel superior, como copiar configuraciones, exportar o importar datos desde aplicaciones, etc. Y lo que es imposible, al menos en mi caso, es planificar este movimiento con tanta exactitud que no se quede ni un byte por detrás.

La conclusión a la que llegué es que la única forma de hacerlo con cierta tranquilidad era virtualizando el sistema anterior. Esto me permitiría acceder en vivo a la configuración anterior y traspasar archivos con la seguridad que necesitaba.

Dis2VhdY aquí es donde ha entrado en juego Disk2Vhd, la magnífica herramienta de Sysinternals (¿he dicho Microsoft? ;-)), que es capaz de generar un disco duro virtual (archivo con extensión .vhd) a partir de un disco duro físico. Y lo mejor de todo, que puede hacerlo sobre el propio equipo que está generando el volcado, es decir, en caliente.

El único requisito es disponer de espacio libre (por ejemplo, como yo lo hice, en un disco duro externo), estar corriendo Windows XP SP2, Windows Server 2003 SP1 o superiores, incluyendo sistemas x64, y suficiente espacio en un disco duro como para almacenar el archivo resultante del volcado.

La aplicación es muy sencilla de utilizar. Se descarga desde su sitio web y se ejecuta, no requiere instalación (también puedes usarla directamente); tras ello, simplemente debemos elegir los discos a virtualizar, seleccionar una ubicación de salida para el archivo .vhd, esperar unas horitas y ya lo tenemos. Normalmente bastará con virtualizar el disco de sistema.

Consejo #1: para que la conversión se realice más rápidamente, lo mejor es hacer que el .vhd a generar resida en un disco duro distinto del que estamos virtualizando, aunque se puede realizar sobre el mismo.

Una vez con el archivo .vhd a buen recaudo, ya podemos formatear tranquilamente el disco del sistema, montar el nuevo sistema operativo y comenzar a instalar las aplicaciones que vayamos a necesitar.

Para acceder al sistema anterior tal y como estaba antes de la masacre, basta con instalar Virtual PC, crear una máquina virtual, “engancharle” el disco .vhd que hemos generado, y arrancar normalmente, pero ojo:

Consejo #2: haz una copia de seguridad del archivo .vhd antes de realizar cambios sobre el disco duro virtual. Me he encontrado algunos callejones sin salida en los que me ha venido de perlas (p.e., petes del Virtual PC al instalar las Virtual Machine Additions que me dejaban la máquina virtual inutilizada).

La primera vez que enciendes la máquina virtual se llevará un buen rato arrancando Windows; es lógico, pues todos los controladores que tiene instalados corresponden a la máquina física, y el nuevo entorno debe ser configurado, prácticamente igual que si hubiéramos instalado el disco duro físicamente en otro equipo y arrancáramos desde él. Cuando esta reconfiguración finaliza, podremos utilizar con normalidad la máquina virtual e ir pasando las configuraciones y archivos al nuevo sistema tranquilamente.

Consejo #3: revisa la configuración básica de tu máquina virtual para evitar conflictos y funcionamiento anómalo en algunas aplicaciones; nombre de máquina, dirección IP, variables de entorno del sistema operativo, etc. En mi caso, la variable TMP/TEMP apuntaba a una unidad inexistente en el entorno virtual, y provocó algún que otro problemilla.

Y por si lo que queremos acceder a los datos del equipo anterior en bruto, existe la posibilidad de montar un archivo .vhd como si fuera un disco duro más y acceder a su contenido directamente, por lo que podemos evitar la incomodidad de tener que arrancar Virtual PC para todo. Si usas Windows 7 (o 2008), esta capacidad viene “de serie”, sólo tienes que activarla desde el administrador de discos:

Montando un VHD en Windows 7

Virtualmente crossposteado desde Actualizando el equipo con Disk2Vhd @  Variable not found

Publicado por José M. Aguilar | con no comments

Forzar validación desde cliente con ASP.NET Webforms

 

Validación de formularios ASP.NET desde javascript Cada vez que tengo que forzar la validación de los datos de un formulario Webforms mediante javascript me veo obligado a preguntarle a Google, ese que todo lo sabe, cómo era el nombre de la función. Cosas de la edad, supongo ;-)

Así que, a modo de auto-recordatorio y con la intención de que pueda ser útil a alguien más, ahí va: la función se llama Page_ClientValidate(). Retorna “true” si, una vez evaluados todos los validadores de la página, el valor de los campos es correcto (al menos en cliente; otra cosa son las comprobaciones en servidor, p.e., las definidas en un CustomValidator).

Y como ejemplo de uso, puede valer el siguiente. Se trata de un botón de envío en un formulario donde se compone un correo electrónico:

   1: ...
   2: <asp:Button ID="btnEnviar" runat="server" Text="Enviar mail" 
   3:      OnClick="btnEnviar_Click"
   4:      OnClientClick="return confirmar();"
   5: />
   6: ...
   7:  
   8: <script type="text/javascript">
   9:     function confirmar() {
  10:         if (!Page_ClientValidate())   // Fuerza la validación en cliente
  11:             return false;             
  12:             
  13:         return confirm('¿Seguro que desea realizar el envío?');
  14:     }
  15: </script>

 

Como se puede observar, en el atributo OnClientClick del botón incluye un script en el que se retorna el valor devuelto por la función confirmar. Si el retorno es false, se cancela el Postback, evitando así que se invoque al evento btnEnviar_Click que es el que realiza el envío del mail propiamente dicho.

En el cuerpo de la función confirmar(), en primer lugar, validamos la página; si ésta no supera el proceso, los validadores habrán mostrado sus mensajes de error y retornamos falso, haciendo que se anule el postback. Si la validación es correcta, solicitamos al usuario que confirme la acción y retornamos su decisión.

Publicado en: Variable not found.

ASP.NET 4, más orientado al SEO

La nueva versión de ASP.NET viene cargadita de novedades orientadas a la optimización en motores de búsqueda. Si sabemos aprovecharlas, nuestros sitios webs serán mejor indexados en los buscadores, seremos más fácilmente localizables desde estas herramientas y, consecuentemente, podremos aspirar a tener mayor número de visitantes.

En este post resumiré las mejoras introducidas en  ASP.NET 4 que pueden ayudarnos en nuestra relación con los motores de búsqueda. No son novedades revolucionarias, puesto que todo lo que nos ofrecen podíamos conseguirlo con las versiones anteriores de ASP.NET de alguna u otra manera, aunque sin duda que nos facilitarán algo las cosas. :-)

Page.MetaDescription y Page.MetaKeywords

Descripción y tags en ASP.NET ASP.NET 4 incorpora a la clase Page las propiedades MetaDescription y MetaKeywords para facilitarnos el acceso a la metainformación de la página.

Con la primera de ellas, MetaDescription , podemos especificar en cada página una descripción individualizada, que será utilizada por los buscadores como un resumen de su contenido. Por ejemplo, Google, la incluye como parte de los resultados de las búsquedas.

La segunda, MetaKeywords, permite añadir tags o palabras clave que puedan ser útiles para clasificar su contenido. Aunque la mayoría de los buscadores no utilizan esta información a efectos de indexado, al parecer algunos sí lo hacen, por lo que sigue siendo costumbre incorporar esta información.

En ambos casos podemos establecer su contenido de forma declarativa, en las directivas de la página, así:

<%@ Page Title="Variable not found" Language="C#" 
         MasterPageFile="~/Blog.master" AutoEventWireup="true"
         CodeBehind="Default.aspx.cs" Inherits="Variable.Blog" 
         MetaDescription = "Variable not found. Artículos, noticias, curiosidades, 
                            reflexiones... sobre el mundo del desarrollo de software, 
                            internet, u otros temas relacionados con la tecnología"
         MetaKeywords = "desarrollo,programación,.net,c#,asp.net,asp.net mvc,software"   
%>


Pero también, y esta posibilidad es bastante más interesante, podemos establecerlas mediante código, por ejemplo en el Page_Load(), lo que puede ser útil si por ejemplo tenemos que obtener valores desde una base de datos o similar:

protected void Page_Load(object sender, EventArgs e)
{
   Product prod = ObtenerProducto(); // Carga el producto 
 
   this.Title = prod.Denominacion;
   this.MetaDescription = prod.DescripcionBreve;
   this.MetaKeywords = prod.Tags;
}


En cualquiera de los casos, el resultado es el mismo. ASP.NET, a la hora de generar la página, añadirá en la sección <head> de la página una etiqueta <meta> con la descripción y etiquetas proporcionadas:

<title>NETBOOK ACER ASPIRE ONE D250-0BK</title>
<meta name="description" content="Netbook con procesador Intel® Atom N270 1.6 GHz, 
                                  1GB memoria, 160GB disco duro, t. gráfica Intel® GMA 950,
                                  pantalla 10.1' LED, webcam y Windows XP." />
<meta name="keywords" content="Netbook, ultraportátil, acer, aspire" />


Ni que decir tiene que la etiqueta <head> de la página (o la master que utilice) debe tener el correspondiente runat="server" para que pueda ser modificada en tiempo de servidor.

Response.RedirectPermanent()

Redirecciones 301 en ASP.NET 4 Los más viejos del lugar recordarán que Response.Redirect() está con nosotros desde los tiempos del ASP tradicional, y desde entonces ha mantenido su función: enviar al agente de usuario un resultado HTTP 302, apuntándole una nueva dirección URL en la que localizar el recurso solicitado. Vamos, lo que toda la vida hemos llamado redirección. :-)

Sin embargo, el código de estado 302 definido por el protocolo indica, literalmente, que el recurso solicitado ha sido localizado pero que se encuentra en otra dirección (retornada como información adicional a la respuesta) aunque sólo temporalmente. Por ello se dice que estas redirecciones son transitorias; el navegador (o los robots de búsqueda, en el caso que nos ocupa) no pueden asegurar que la URL indicada sea la ubicación correcta y permanente del recurso.

El código HTTP 301, sin embargo, indica que el recurso solicitado ha sido movido de forma permanente a la dirección que se retorna al agente de usuario. Esto podría ser útil, por ejemplo, si modificamos los dominios sobre los que corre un sitio web, o cambiamos el esquema de URLs que estamos utilizando y, entre otras cosas, quisiéramos aprovechar la información la información que ya tienen los buscadores sobre nuestro sitio.

Pues con este propósito ha sido incluido el método Response.RedirectPermanent()cuya forma de utilización es idéntica a las redirecciones habituales:

 
public partial class About: System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
   {
      // Redirigimos About.aspx a AcercaDe.aspx
      Response.RedirectPermanent("AcercaDe.aspx"); // 301 al canto!
}
}

¡Direcciones amigables!

Routing en ASP.NET 4 ¡Ya era hora de que ASP.NET ofreciera un mecanismo integrado para generar URLs amigables! El motor de routing introducido con ASP.NET 3.5  SP1 tan magníficamente aprovechado en el framework MVC, toma ahora su protagonismo también en el mundo de Webforms. Y la forma de usarlo, igual de sencilla. :-)

Para que nuestra aplicación soporte friendly urls, tenemos que registrar durante la inicialización del sistema las rutas, o mejor dicho, el patrón del URLs, que vamos a entender. Por ejemplo, imaginemos que nuestra aplicación, un catálogo on-line, debe atender peticiones a direcciones como las siguientes para mostrar la página de detalle de un producto:

Esto podríamos conseguirlo durante la inicialización del sistema, registrando un patrón de ruta de la siguiente forma, en el global.asax:

void Application_Start(object sender, EventArgs e)
{
   // Code that runs on application startup
   RegistrarRutas();
}
 
private void RegistrarRutas()
{
   RouteTable.Routes.MapPageRoute(
      "DetalleProducto",       // Nombre de la ruta
      "product/{productCode}"// patrón de URL
      "~/product.aspx"         // Página que procesará la petición
);
}


Lo que indica la ruta es que las direcciones de tipo product/{productCode} serán enviadas a la página física ~/product.aspx. La porción variable de la URL, el parámetro {productCode} podremos obtenerlo desde el código de la página para cargar los datos desde el almacén correspondiente, por ejemplo así (en product.aspx.cs):

public partial class Product : System.Web.UI.Page
{
   protected void Page_Load(object sender, EventArgs e)
   {
      string productCode = RouteData.Values["productCode"] as string;
      if (!cargaProducto(productCode))
      {
         Response.Redirect("ProductNotFound.aspx");
      }
   }
}


Como puede intuirse, a través de la colección RouteData.Values podremos acceder a los parámetros suministrados en el interior de la ruta, es decir, los que forman parte de la URL de la página que introduce el usuario.

Al igual que ocurre en ASP.NET MVC, el sistema de rutas es bidireccional, por lo que podremos también generar links ‘amistosos’ hacia recursos del sitio web, como en el siguiente ejemplo:

HyperLinkToAcerAspire.NavigateUrl = 
      GetRouteUrl("DetalleProducto", 
                   new { productCode = "ACER-ASPIRE-ONE-D250-0BK" }
);


El método GetRouteUrl() genera la URL del producto utilizando el propio motor de routing, para lo que le pasamos el identificador de la ruta y, en un objeto anónimo, el valor para el parámetro requerido (productCode). El resultado para el caso anterior será la dirección: /product/ACER-ASPIRE-ONE-D250-0BK.

Mejoras en el marcado

Aunque no vamos a detallarlas, o al menos en este post ;-), aparecen también en ASP.NET 4 un buen conjunto de novedades dirigidas a mejorar la calidad del marcado que enviamos a cliente. Y dado que siempre se ha dicho que un marcado respetuoso con los estándares es bueno para el SEO, estas nuevas capacidades del framework nos ayudarán también a mejorar nuestro posicionamiento.

Convenientemente crossposteado desde: ASP.NET 4, más orientado al SEO  @ Variable not found.

Controladores diminutos

 

Controladores en ASP.NET MVCCuando desarrollamos sobre el framework MVC, estamos acostumbrados a crear nuestros controladores partiendo de la clase base Controller, que nos proporciona métodos, propiedades y mecanismos que nos ahorran mucho trabajo en su implementación. Por ejemplo, toda la lógica de localización e invocación de las acciones está definida en esta clase, así como métodos de creación de los tipos más utilizados de ActionResult, las llamadas al model binder, o propiedades de acceso rápido a datos del contexto.

Sin embargo, y es una prueba más de la flexibilidad de diseño del framework, esto no es ni mucho menos obligatorio. La factoría de controladores por defecto no es tan exigente a la hora de localizar estas clases como primer paso durante el proceso de una petición.

Si observamos el código del marco de trabajo, concretamente la clase ControllerTypeCache, nos encontramos con este método estático, responsable de determinar si un tipo dado cumple las condiciones necesarias para ser considerado un controlador válido:

internal static bool IsControllerType(Type t)
{
    return ((((t != null)
             && t.IsPublic) 
             && (t.Name.EndsWith("Controller", StringComparison.OrdinalIgnoreCase) 
             && !t.IsAbstract)) 
             && typeof(IController).IsAssignableFrom(t));
}

 

Como podemos observar, en primer lugar se comprueba que la clase sea pública; es lógico, pues el framework debe instanciarla. A continuación, comprueba que su nombre atienda a la convención de acabar en “Controller” (sí, la convención que aprendimos a desmontar hace algún tiempo ;-)). La siguiente es que no se trate de una clase abstracta, algo básico si pretendemos instanciarla. Por último, se exige que implemente el interfaz IController cuya definición es la siguiente:

public interface IController
{
    void Execute(RequestContext requestContext);
}

Es decir, en ningún caso aparece la clase Controller, ni siquiera la clase ControllerBase, antecesora de la primera. Basta con implementar el método Execute() definido en el interfaz.

Por tanto, cumpliendo estos requisitos podríamos escribir un controlador tan reducido como el siguiente:

using System.Web.Routing;
using System.Web.Mvc;
 
namespace MasterViewModel.Controllers
{
    public class PersonaController : IController
    {
        public void Execute(RequestContext contexto)
        {
            contexto.HttpContext.Response.Write(
                    "Ejecutando la acción " + contexto.RouteData.Values["action"] +
                    " con parámetro " + contexto.RouteData.Values["id"]
            );
        }
    }
}

 

 

Incluyendo dicho controlador en un proyecto en el que mantengamos las rutas por defecto, si realizamos una petición del tipo GET /Persona/Beber/Cerveza, obtendremos en el navegador un mensaje como el siguiente:

Resultado de ejecución

Sin embargo, aunque el desarrollo de controladores así de ligeros pueda resultar a priori una idea atractiva  pensando sobre todo en optimizar aún más la velocidad de respuesta, en la práctica no tiene demasiado sentido hacerlo. Sería prácticamente igual de eficiente sobrescribir el método ExecuteCore de la clase Controller, y nos beneficiaríamos de los métodos y propiedades implementados en la misma.

Diminutamente crossposteado desde: Controladores diminutos @ Variable not found.

Publicado por José M. Aguilar | con no comments

Server.Transfer en ASP.NET MVC

ASP.NET MVC ofrece “de serie” mecanismos para transferir el control desde una acción a otra utilizando para ello redirecciones HTTP. Esto significa que cuando un cliente realiza una petición y el método de acción desea que ésta sea procesada desde otra acción, el navegador será informado de la URL a la que debe dirigirse mediante una respuesta de tipo 302 para que, tras recibirla, realice una nueva solicitud que finalmente ejecutará la lógica apropiada.

En el diagrama se muestra el proceso completo de transferencia desde una petición a /home/mail hacia /mail/index utilizando la ruta por defecto, y muestro una posible implementación en ASP.NET MVC a continuación:

Redirección basada en retornar un HTTP 302

Y el código:

public class HomeController : Controller
{
    [...]
    // GET /Home/Mail
    public ActionResult Mail()
    {
        return RedirectToAction("index", "mail");
    }
 
}
 
public class MailController : Controller
{
    [...]
    // GET /Mail
    public ActionResult Index()
    {
        return View();
    }
 
}

Aunque es la forma habitual de realizar redirecciones en ASP.NET MVC, hace unos días estaba pensando que hay ocasiones en las tanta petición puede resultar pesada y vendría bien disponer de algo parecido al Server.Transfer, presente desde que peleábamos con ASP clásico, que era capaz de transferir la ejecución a otro punto sin necesidad de que el cliente replanteara la petición.

Y no es que me parezca una práctica especialmente recomendable, puesto que hay muchas cuestiones de ese tipo que pueden resolverse en MVC a nivel configuración de rutas o simplemente replanteando la estructura de direcciones del sitio, pero la verdad es que cuando te pica el gusanillo…

Intento erróneo #1: invocar directamente una acción desde otra

Bueno, en realidad este intento no llegué a hacerlo, pero se trata de un error bastante frecuente cuando se empieza con ASP.NET MVC framework, y me ha parecido interesante reflejarlo aquí como aviso a navegantes:

public class HomeController : Controller
{
    // GET /Home/Mail
    public ActionResult Mail()
    {
        return new MailController().Index(); // <- Mal
    }
 
}

 

Aunque a primera vista parece tener sentido, y de hecho funcionará muchas veces, se trata de una mala práctica y puede causarnos muchos problemas, y sobre todo, como en el ejemplo anterior, si se trata de llamadas de acciones de distintos controladores. Hay que tener en cuenta que en el contexto de petición estará la información de la petición inicial (/home/mail), no el que podría esperarse desde el método de acción new MailController().Index().

También es peligroso porque al invocarlo directamente estaríamos saltándonos todos los filtros que hubiéramos podido establecer mediante atributos en la acción final, MailController.Index(). Imaginad, por ejemplo, que la acción está protegida por un [Authorize]… :-O

Intento erróneo #2: invocar a Server.Transfer en el controlador

El segundo intento fue, pues eso, invocar directamente a Server.Transfer() desde el cuerpo de una acción. Pensaba que en cualquier caso no era una buena idea puesto que el método de acción que lo invocase sería difícilmente comprobable usando pruebas unitarias, pero bueno, estaba dispuesto a sacrificar esta ventaja:

public class HomeController : Controller
{
    // GET /Home/Mail
    public ActionResult Mail()
    {
        Server.Transfer(Url.Action("Index", "Mail")); // Mal
        return new EmptyResult();                     // En realidad podría devolver
    }                                                 // un nulo, o cualquier otra cosa
}

Al ejecutar el proyecto, el resultado obtenido es una bonita excepción “Error al ejecutar la solicitud secundaria” al intentar procesar la transferencia hacia la dirección “/Mail” (generada por el helper Url.Action()).

Primer acercamiento a la solución correcta: default.aspx

Aparentemente, la solución para transferir el control hacia otra acción pasa por recorrer el ciclo de vida completo de la petición, pero sin necesidad de que el navegador vuelva a hacerla. Ahora la cuestión es cómo conseguir esto armando el menor estropicio posible.

Si os fijáis, al crear el proyecto ASP.NET MVC, en la plantilla se habrá incluido un archivo en la carpeta raíz del mismo, llamado default.aspx. Su objetivo es transferir la ejecución al framework MVC si un usuario realiza una petición al raíz del sitio, siendo esta página el documento por defecto.

Observemos el código por defecto del método Page_Load que encontramos en su code-behind:

public void Page_Load(object sender, System.EventArgs e)
{
    // Change the current path so that the Routing handler can correctly interpret
    // the request, then restore the original path so that the OutputCache module
    // can correctly process the response (if caching is enabled).
 
    string originalPath = Request.Path;
    HttpContext.Current.RewritePath(Request.ApplicationPath, false);
    IHttpHandler httpHandler = new MvcHttpHandler();
    httpHandler.ProcessRequest(HttpContext.Current);
    HttpContext.Current.RewritePath(originalPath, false);
}

Como indican los comentarios, lo que se hace es cambiar la ruta original de la petición, instanciar un nuevo manejador MVC (MvcHttpHandler) y hacer que éste se encargue de procesarla… o en otras palabras, estamos transfiriendo la ejecución, justo lo que queríamos conseguir. Después, para evitar daños colaterales, se vuelve a dejar la ruta de la petición como estaba.

Por tanto, podríamos utilizar este mismo código y crear un método de extensión sobre la clase Controller:

public static class ControllerExtensions
{
    public static void Transfer(this Controller controller, string url)
    {
        string originalPath = controller.Request.Path;
        HttpContext.Current.RewritePath(url, false);
        IHttpHandler httpHandler = new MvcHttpHandler();
        httpHandler.ProcessRequest(HttpContext.Current);
        HttpContext.Current.RewritePath(originalPath, false);
    }
}

Así, podríamos utilizarlo desde nuestras acciones de la siguiente forma:

public class HomeController : Controller
{
    [...]
 
    // GET /Home/Mail
    public ActionResult Mail()
    {
        this.Transfer(Url.Action("index", "mail"));
        return null;
    }
}

Con esto ya tenemos una primera solución a nuestro problema. Sin embargo, aunque puede parecer muy limpio, y de hecho yo estaba bastante conforme con esta solución, este código tiene varios problemas.

En primer lugar, realizar pruebas unitarias sobre la acción Mail sería realmente complicado. Además, la invocación a Transfer() ejecutaría la acción asociada a la ruta de la URL indicada, pero al finalizar continuaría ejecutándose el método Mail(), desde donde la hemos invocado. Esto puede provocar efectos curiosos, si por ejemplo retornásemos un ViewResult o cualquier otro tipo de contenido desde la misma (serían enviados de forma consecutiva el cliente).

Solución final: crear un ActionResult personalizado

Aunque el enfoque anterior es correcto y podría ser válido a pesar de sus inconvenientes, encuentro una solución más fina en una pregunta de StackOverflow, How to simulate Server.Transfer in ASP.NET MVC: crear un resultado de acción (ActionResult) personalizado.

/// <summary>
/// Transfers execution to the supplied url.
/// </summary>
public class TransferResult : RedirectResult
{    
    public TransferResult(string url): base(url)    {    }
   
    public override void ExecuteResult(ControllerContext context)    
    {        
        var httpContext = HttpContext.Current;        
        httpContext.RewritePath(Url, false);        
        IHttpHandler httpHandler = new MvcHttpHandler();        
        httpHandler.ProcessRequest(HttpContext.Current);    
    }
}

De esta forma, evitamos los dos problemas de la anterior aproximación. El método de acción seguiría siendo fácilmente testeable, y evitaríamos resultados indeseados al tratarse de un tipo de respuesta, que evita la posibilidad de introducir código adicional.

Alegremente crossposteado desde: Server.Transfer en ASP.NET MVC@Variable not found.

Top posts 2009 en Variable Not Found

Ya iba siendo hora de estrenarme este 2010 y,image como ordena la tradición, el primer post del nuevo año lo reservo para comentar las entradas de Variable not found que han sido más populares en a lo largo del año que acabamos de cerrar.

Esta vez voy a introducir una novedad en el análisis, separando los posts que han sido escritos durante este año de otros, creados años anteriores y que todavía gozan de gran popularidad en la red.

Cosecha de 2009

Encabezando el top ten aparece “101 formas de saber que tu proyecto está condenado al fracaso”, una traducción autorizada del original 101 Ways To Know Your Software Project Is Doomed de Max Pool, que enumeraba, en clave de humor, una serie de pistas para saber si un proyecto se estaba precipitando hacia un estrepitoso fracaso.

Seguido de cerca tenemos “ASP.NET MVC: trece preguntas básicas”, un pequeño resumen de cuestiones realizadas muy frecuentemente tras el primer encuentro con el framework, como qué es MVC, qué ventajas tiene sobre Webforms, o si vale la pena pasarse a esta nueva forma de desarrollar aplicaciones web.

En tercer lugar, “Y todavía otras 101 citas célebres del mundo de la informática”, la tercera entrega de un clásico de este blog, con otras ciento una frases recogidas sobre este mundillo. En total, 303 frases, no está mal.

A continuación, el post “jqGrid: Grids espectaculares para ASP.NET MVC, paso a paso” ha demostrado el interés que despierta el uso de este plugin para mostrar y editar datos tabulados en el framework ASP.NET MVC.

En quinta posición tenemos la serie de tres posts “C#: Desmitificando las expresiones lambda” con la que pretendí quitar un poco de misterio a las expresiones lambda, esa fascinante y temida característica de la última hornada de nuestro lenguaje favorito.

En el post “Aspectos a tener en cuenta al crear sitios web públicos” recopilé las opiniones de un hilo de StackOverflow en el que se debatía sobre aquellos aspectos que teníamos que considerar, desde el punto de vista técnico, para crear webs destinadas al público en general, clasificadas por categorías como experiencia de usuario, seguridad, rendimiento, SEO, etc.

En séptima posición, “Plantillas de proyectos ASP.NET MVC 1.0 para NUnit”, donde se describían los pasos para instalar NUnit como framework de pruebas unitarias en proyecto ASP.NET MVC.

La octava posición es para “Indicios de que tu interfaz de usuario fue creado por un programador”, que comentaba y ampliaba el post de Ian Voyce “The 7 signs your UI was created by a programmer” sobre los rastros que dejamos los desarrolladores cuando queremos hacer de diseñador.

En penúltima posición, “Generación de PDF desde .NET usando formularios”, donde describía un truco muy rápido para generar documentos PDF al vuelo, utilizando formularios en el documento y la librería iTextSharp para rellenar su contenido.

En décima posición, “Programadores con producción neta negativa (NNPP)”, post donde comentaba el curioso fenómeno de la existencia de desarrolladores cuyo saldo en las aportaciones a un proyecto resultara negativo, o sea, que el valor de su producción fuera superado por el coste de los errores y defectos que introducían en las aplicaciones.

Cosechas anteriores

En este apartado encontramos los posts que podríamos considerar clásicos en este blog, pero dado que muchos de los lectores acaban de llegar, no está de más comentar su existencia.
Las dos primeras posiciones, un año más como líderes indiscutibles, citados, copiados y pegados hasta la saciedad, “Otras 101 citas célebres del mundo de la informática” y “101 citas célebres del mundo de la informática”.

En tercera posición, “Bordes redondeados en webs (sin esfuerzo) con Nifty Corners Cube”, un post de 2007 donde comentaba el uso de esta librería de scrips para redondear las esquinas de elementos de bloque en páginas web.

Le sigue otra creación de 2007, “Evitar el postback al pulsar un botón en ASP.Net”, donde comentaba distintas formas de hacer que no se lanzara un postback al pulsar un botón incluido en una página ASP.NET, utilizando, por ejemplo, para evitar envíos múltiples de un formulario.

Otro de los grandes clásicos, en “13 Consejos para comentar tu código” comentaba una serie de prácticas destinadas a facilitar la legibilidad en el código fuente.

La sexta posición la ocupan las “32 técnicas de producción de ideas”, un resumen de un post de Neuronilla sobre técnicas destinadas a favorecer la creatividad.

El post “Llamar a servicios web con ASP.NET AJAX paso a paso”, donde se describe detalladamente cómo utilizar ASP.NET AJAX para obtener datos desde un servicio web.

En octava posición, curiosamente, un post sobre el “Escaneo de puertos con idle scan”, una ingeniosa técnica para detectar puertos abiertos en máquinas remotas utilizando un equipo zombie que oculta la identidad del atacante.

“20 desastres famosos relacionados con el software” traducción autorizada por Timm Martin de su serie “20 Famous Software Disasters”, que recogía un buen número de casos en los que fallos relacionados con el software habían tenido consecuencias trágicas.

Por último, “Enviar mensajes con imágenes incrustadas desde .NET”, de la cosecha de 2008, donde se mostraba cómo incrustar archivos de imagen en un mensaje de correo electrónico generado desde una aplicación .NET.

¡¡Feliz 2010!!

Publicado en: Variable not found.

PremoniSense, la gran novedad de Visual Studio 2010

Programa para la mejora de la experiencia del usuario Seguro que muchas veces te has preguntado qué es el “programa para la mejora de experiencia de usuario”, esa pantalla que nos aparece desde hace muchos años tras instalar Visual Studio (y otros productos de Microsoft), sugiriéndonos sutilmente que ayudemos a recopilar información del uso que damos a sus servicios y software.

En el encuentro de desarrolladores DevConn4 del pasado diciembre ya se oían rumores sobre el uso que estaban dando a esta información los chicos del laboratorio, y concretamente los investigadores del grupo Information retrieval and management, pero nada que pudiera ser tomado en serio en aquél momento.

El pasado uno de Abril, Dough Seven, Senior Product Manager de Visual Studio Team System en Microsoft, en el marco de las DSL’s Developers Conference, ya dejó caer que en la próxima versión de Visual Studio (la 2010) se introducirían “tecnologías que revolucionarían la forma en la que desarrollamos software e incrementarían la productividad de forma nunca vista”. Ahora, con el tiempo, se entiende que no se refería a nuevas plantillas, componentes o mecanismos de refactorización, se trataba de una pequeña pista de lo que se estaba cociendo en Redmon.

Visual Studio 2010, with Premonisense!Y por fin, cuando ya va quedando menos para lanzamiento oficial de Visual Studio 2010 (estaba previsto para el próximo mes de marzo, aunque ha sido retrasado), es el mismísimo S. Somasegar Vicepresidente Senior de la División de Desarrollo, el que ha desvelado el gran secreto: la tecnología PremoniSense™.

Tras este enigmático nombre se encuentra el resultado de recopilar pautas de comportamiento de decenas de miles de desarrolladores de todo tipo durante más de diez años y procesarlas mediante complejos mecanismos estadísticos e inteligencia artificial. Esto ha permitido desarrollar un motor de inferencia, alojado en la nube, capaz de analizar en tiempo real el uso que hacemos del entorno de desarrollo y adelantarse a nuestras acciones, automatizando gran parte de las tareas habituales de los programadores.

Cómo funciona

El modus operandi es el siguiente: cuando PremoniSense™ detecta un comportamiento conocido, aparece un cuadro describiéndonos cuál es el siguiente paso que vamos a dar en función de lo que hemos hecho hasta el momento; la base de conocimiento de la que obtiene esta información es tan amplia que estadísticamente se estima que el índice de error de sus predicciones el 0,1%, es decir, un fallo de cada 1000 acciones. Pero lo más interesante sin duda es que, dado que conoce perfectamente nuestras intenciones, nos ofrece la posibilidad de hacer el trabajo por nosotros automáticamente.

Áreas de interacción

De momento, esto que veremos en Visual Studio 2010 será únicamente un adelanto, por lo que sólo será posible disfrutar de él en tres áreas de actividad en el proceso de desarrollo de software: la arquitectura de aplicaciones, el desarrollo o implementación, y la calidad del software.

Se prevé que Visual Studio 2012 ya incluirá soporte de PremoniSense™ completo para otras áreas, como la ingeniería de requisitos, el despliegue o el soporte postventa.

Pero bueno, centrándonos en el presente, a continuación describo las áreas en las que esta tecnología estará presente en VS2010, con algunas capturas de pantalla de ejemplo que demuestran la increíble potencia de esta tecnología, que a veces sólo se puede describir con la palabra “magia”.
  • Arquitectura (Architectural PremoniSense™) 
    El motor de inferencia será capaz de detectar, en función del tipo de aplicación que estemos desarrollando, la arquitectura más apropiada a utilizar en cada momento, refactorizando el proyecto para adaptarse a la misma.

    Por ejemplo, si estamos creando una aplicación MVC con servicios de uso genérico será capaz de crear un proyecto independiente WCF capaz de permitir accesos securizados desde el exterior. Si observa que utilizamos procesos basados en flujos de trabajo, será capaz de mover el código a un proyecto independiente implementando dichos procedimientos con Workflow Foundation. Si detecta acceso a datos desde capas no permitidas (p.e., desde controladores en MVC), los trasladará a un proyecto independiente (el típico DAL, o capa de acceso a datos), actualizando las referencias automáticamente.

    Asimismo, será capaz de detectar funcionalidades duplicadas transversales al sistema (por ejemplo, acceso a logs, seguridad, gestión transaccional), extraer sus implementaciones e inyectarlas de nuevo utilizando técnicas propias de la programación orientada a aspectos (AOP), todo de forma automática.

    En teoría, el sistema soporta arquitecturas de hasta 7 capas, con posibilidad de entender y plantear soluciones integrando tecnologías tanto de Microsoft (como Sharepoint, Office, Exchange, WWF, EF, WCF, JASP, WPF, MVC, MVP o HTML) como open source (Rails, PHP, NHibernate, MySQL, Oracle, EJB, FSB, UPNP, ó MRW).
  • Desarrollo (PremoniSense™ for Code)
    El funcionamiento de PremoniSense™ en esta área es conceptualmente muy similar a las ayudas del IDE e incluso al soporte ofrecido desde herramientas externas como Resharper, el objetivo es ayudarnos a codificar más rápidamente y sin errores, pero llevado al extremo. Por citar unos ejemplos, ahí van los que más me han llamado la atención (podéis ver la lista completa en la documentación oficial):

    • Autogeneración de funcionalidades CRUD. Es decir, el sistema detectará cuándo estamos realizando un mantenimiento típico (altas, bajas, modificaciones y consulta) y lo implementará por nosotros. Para ello, analizará la estructura de la base de datos y creará el código más apropiado y correcto según su experiencia acumulada en el tipo de sistema de que se trate.
    • Autoasignación de propiedades. PremoniSense detectará cuándo estamos poblando las propiedades de un objeto y las establecerá por nosotros, determinando de forma automática el origen de la información. Por ejemplo, si estamos ante el clásico bloque de código para traspasar datos desde los campos de un formulario a una entidad, lo detectará y generará el código por nosotros; o en la implementación de un constructor con parámetros, asignará automáticamente los miembros internos coincidan con éstos.
    • PremoniStence, el nombre interno que han dado a lo que vendría a ser un “EF++”, que mantiene sincronizado el mecanismo de persistencia (¡sea cual sea!) con el resto de capas de la aplicación. Esto es realmente espectacular: por ejemplo, si ampliamos el tamaño de un campo de la base de datos, ya no hay que preocuparse por modificar los formularios, pues las propiedades de los controles visuales detectados por el motor se actualizarán de forma automática; o si cambiamos el nombre de una propiedad en una entidad, se actualizará el nombre del campo en la base de datos, e incluso las etiquetas de descripción a nivel de interfaz de usuario. Pero ojo, que no se trata de un mecanismo de binding o mapeo como los existentes en la actualidad, sino de un proceso totalmente automático que nos vigilará continuamente e irá realizando estas tareas de forma silenciosa.
    • Autoimplementación de métodos, que utiliza la gigantesca base de conocimiento acumulado durante años para implementar métodos de forma automática, basándose únicamente en su nombre y signatura (parámetros de entrada y tipo de retorno). Por ejemplo, ya no será necesario implementar más el clásico método “long factorial(long n) ”: él lo hará por nosotros :-))

      Premonisense: auto-implementación de métodosComo puedes comprobar, PremoniSense™ además nos regala la reaparición estelar de nuestro viejo amigo Clippo, esta vez disfrazado de adivino. Al parecer se trata de un guiño de los desarrolladores de Visual Studio al equipo de Office… humor friki, supongo ;-D
    • Simplificación de algoritmos, un mecanismo capaz de replantear el código de métodos complejos, reescribiéndolos de forma más simple y mantenible de forma automática, basándose en los parámetros de entrada, los resultados de salida y el análisis semántico del procedimiento implementado. Internamente se conoce como PremoniKiss, por las iniciales de KISS, el famoso principio para el desarrollo de software que aboga por la simplicidad de las creaciones.
    • En la misma línea, PremoniYagni, un detector prematuro de funcionalidades y características inútiles, que nos alertarán cuando estemos comenzando a introducir en nuestras aplicaciones características que estadísticamente se conoce que no son utilizadas por los usuarios, o estemos entrando en el pantanoso terreno de la sobreingeniería. YAGNI son las iniciales de You ain’t gonna need it (“no vas a necesitarlo”).
    • Detección automática de dominios de aplicación, personalizando su comportamiento para hacer la experiencia de desarrollo más liviana. Así, una vez inferido el dominio del sistema, será capaz de generar automáticamente las entidades de datos, gestores, e incluso lógica de negocio más apropiada, y adaptar sus deducciones, consejos y acciones a dicho dominio.


    Algo realmente interesante de PremoniSense™ for Code es que el código que genera es dinámico, es decir, que es capaz de seguirle el rastro y modificarlo de forma automática cuando se produce algún cambio en las premisas de las que partió en el momento de inferir su generación, manteniéndolo siempre actualizado y correcto.
  • Calidad (PremoniSense™ for Quality)
    PremoniSense™ también está preparado para utilizar su enorme base de conocimiento con objeto de incrementar exponencialmente la calidad de nuestras aplicaciones en varios ámbitos. Por citar algunos:

    • Generador de pruebas unitarias, que utilizará la base de conocimiento para generar por cada clase un set de pruebas de unidad  lo suficientemente extenso como para asegurar que son mayormente correctas. Basado inicialmente en el proyecto Pex de Microsoft Research, incrementa la calidad del código hasta niveles nunca vistos anteriormente, puesto que las pruebas serán generadas teniendo en cuenta el dominio de la aplicación y los fallos que se suelen producir en cada uno.
    • Autodocumentador de diseño, aunque sólo disponible para los poseedores de la próxima suite Office 2010, que creará partiendo de plantillas prediseñadas todo el juego de documentación de diseño de la aplicación, siguiendo los estándares definidos por diversas metodologías (entre las que se encuentra, curiosamente, Métrica 3).
    • Como una extensión del punto anterior, pero que vale la pena destacar de forma independiente, PremoniSense™ será capaz de generar un borrador de manual de usuario del sistema, basándose principalmente en tres factores: el conocimiento del comportamiento de los usuarios acumulado durante años, el interfaz que se haya creado y las funcionalidades implementadas. Obviamente, no nos dará todo el trabajo hecho, pero lo que hasta ahora se trataba de una ardua tarea de redacción se reducirá a un simple repaso y retoque de los textos.
    • Detección de procesos no finitos, que se acerca a la solución del clásico problema de la parada para máquinas de Turing, utilizando el motor estadístico de PremoniSense™ para determinar cuándo los algoritmos empleados en un sistema presentan incorrecciones que le harán entrar en un bucle infinito, antes de que esto se produzca. 

      Detección de procesos no finitos
    • Detección de publicación prematura, se trata de un mecanismo de control sobre la base de código capaz de determinar cuándo un producto está suficientemente maduro para ser publicado (o desplegado a un servidor en producción) en función de su complejidad, número de pruebas funcionales y unitarias realizadas, y la extensa base de experiencias anteriores.

En resumen, que en breve vamos a asistir a lo que será, en mi opinión,  el avance más significativo para los desarrolladores desde la invención del copiar y pegar. Los vídeos que aparecen en el sitio oficial son simplemente espectaculares, no te los pierdas; si quieres pasar unos buenos ratos en estos días de fiesta, puedes descargar este software en la web de Microsoft. Pero ojo, si puedes, ve aumentando tu RAM, que PremoniSense™ se adueñará de 2GB cuando esté en ejecución. 

[Actualizado 29/12]
Obviamente la noticia no es real, se trata simplemente de una broma del Día de los Inocentes. Pero molaría que fuera verdad, ¿eh? ;-DD


Publicado en: Variable not found.

Crossposteado desde PremoniSense, la gran novedad de Visual Studio 2010 @ Variable not found

Disponible ASP.NET MVC 2 Release Candidate

 

ASP.NET MVC 2 RC disponible Recién salida del horno, tenemos ya la versión candidata de ASP.NET MVC 2, la última antes de la versión final que aparecerá antes de marzo, el mes previsto para el lanzamiento de Visual Studio 2010.

Como indica Haack en su post, la mayor parte del trabajo se ha centrado en corregir bugs e introducir mejoras a funcionalidades existentes, como:

  • relativas a la validación en cliente:
    • separación de los scripts de validación a un único archivo, que puede ser incluido en cualquier parte de la vista.
    • soporte para la globalización de mensajes.
    • mejora de Html.ValidationSummary() para que soporte mensajes de error a nivel de modelo, y no vinculados a campos concretos.
    • se permite la inclusión de botones que se salten la validación de formularios.
    • se permite especificar cuándo se ejecutan las validaciones: mientras el usuario teclea, cuando se pierde el foco en un control o sólo en el submit.
  • otras mejoras:
    • plantillas T4 que generan código según la versión del framework destino.
    • el código HTML generado por el diálogo “Add View” es consistente con los templated helpers.

Enlaces:

Rápidamente crossposteado desde: Disponible ASP.NET MVC 2 Release Candidate @ Variable not found  

Publicado por José M. Aguilar | 2 comment(s)

Procesar peticiones a acciones inexistentes en ASP.NET MVC

Los controladores ASP.NET MVC que heredan de la clase Controller permiten procesar muy fácilmente las peticiones realizadas a acciones no definidas. Para ello, lo único que hay que hacer es sobrescribir el método HandleUnknowAction() e implementar la lógica que queremos que se ejecute en estos casos.

En el siguiente código, las peticiones realizadas a /Home/Index y /Home/About serán procesadas normalmente, pero /Home/BeberCerveza  será procesada por HandleUnknowAction, cuya  implementación mostrará la vista “Index” con un mensaje personalizado:

public class HomeController : Controller
{
    public ActionResult Index()
    {
        ViewData["Message"] = "Welcome to ASP.NET MVC!";
        return View();
    }
 
    public ActionResult About()
    {
        return View();
    }
 
    protected override void HandleUnknownAction(string actionName)
    {
        ViewData["Message"] = "¿Estás intentando " + actionName + "?";
        View("Index").ExecuteResult(this.ControllerContext);
    }
}

 

 

image

Alegremente crossposteado desde: Procesar peticiones a acciones inexistentes en ASP.NET MVC @ Variable Not Found

Borrado de registros con jqGrid y ASP.NET MVC

 

En un post anterior dedicado a jqGrid y ASP.NET MVC vimos lo sencillo que resultaba implementar un potente grid para mostrar datos tabulares, permitiendo paginación, ordenación y redimensionado de columnas.

Pero, como ya comenté entonces, jqGrid es mucho más que eso. En este artículo estudiaremos la implementación de la funcionalidad de borrado de filas integrada en el propio componente, utilizando intercambio de datos Ajax con el lado servidor para actualizar el modelo.

Partiremos del ejemplo que desarrollamos anteriormente, y nos centraremos únicamente en introducir los cambios necesarios para introducir esta nueva capacidad. También, como en el caso anterior, encontraréis al final un enlace al proyecto de demostración, para Visual Web Developer Express 2008 SP1 y ASP.NET MVC 1.0.

1. Preparamos la infraestructura

Selección de módulos en jqGridAntes de nada, y es algo que es importante recordar cuando trabajemos con jqGrid, debemos pensar qué módulos de este componente necesitamos en nuestro proyecto.

En el post anterior descargamos exclusivamente los necesarios para implementar la visualización del grid; ahora, dado que vamos a utilizar más funcionalidades del componente, debemos seleccionar en la herramienta de descarga aquellos que nos harán falta, el base y los relativos a la edición de datos.

En teoría deberíamos seleccionar los módulos estrictamente necesarios para nuestros fines, pero en la práctica no es fácil adivinar cuál de ellos implementa justamente lo que estamos buscando. De hecho, es bastante frecuente encontrarse con errores de script cuando no acertamos con el módulo exacto, al que siguen bastantes iteraciones prueba-error hasta que conseguimos averiguar cuál debemos indicar en la descarga.

Por eso en este caso, seleccionaremos todos los módulos relativos a la edición; eso nos permitirá, además, seguir implementando funcionalidades como el alta o la modificación sin tener que volver a descargar componentes.

Aparte de los módulos a incluir en la descarga, el resto de los pasos de preparación de la infraestructura son idénticos a los descritos en los puntos 1 al 3 del post inicial:

  • Copiar los archivos de script (jqGrid y el archivo de localización) en el proyecto.
  • Descargar el tema visual de jQuery UI y añadirlo al proyecto.
  • referenciar las librerías de scripts y estilos en la master (o vistas donde vayamos a usarlo).

2. El modelo

Vamos a ampliar la clase GestorDeAmigos anterior para que sea capaz de emular el almacenamiento en una base de datos, pero utilizando como soporte una colección en memoria que haremos persistir en una variable de aplicación. Además, aprovecharemos para añadirle un método Delete() que nos permita eliminar del almacén la persona cuyo “Id” le pasemos como parámetro.

public bool Delete(int id)
{
    Amigo amigo = DatosAmigos.FirstOrDefault(a => a.Id == id);
    if (amigo == null)
        return false;
 
    DatosAmigos.Remove(amigo);
    return true;
}
 
No profundizaré más en el modelo, pues el código es de lo más convencional. El que tenga curiosidad por ver cómo se implementa el almacén en una variable de aplicación, que acuda al fuente del proyecto de demostración.

 

3. La vista

En la vista debemos hacer muy pocos ajustes para permitir la eliminación de los datos. Básicamente, tendremos que habilitar un panel de botones en el grid, indicar que deseamos que aparezca el botón de eliminación, y configurar el comportamiento de éste, para lo cual invocaremos al método navGrid() después de la llamada de inicialización del grid que ya vimos el otro día:

<script type="text/javascript">
    jQuery(document).ready(function() {
    
        jQuery("#list").jqGrid({
            [...] // OMITIDO. Idéntico al del post anterior.
        });
 
        $("#list").navGrid(
                null,
                { refresh: true, add: false, edit: false, del: true, search: false },
                null, // parámetros para el alta
                null, // parámetros para la edición
                {     // parámetros para la eliminación
                    url: '<%= Url.Action("Eliminar") %>',
                    width: 500,
                    afterSubmit: function(r, d) {
                        return [r.responseText=="", r.responseText];
                    }
                }
        );
});

 

 

Los parámetros que estamos pasando al método navGrid() son los siguientes:

  • el primer parámetro debería ser jQuery('#pager'), que es la referencia hacia el control de paginación que estamos utilizando. En este caso es un nulo porque esta referencia se incluyó en la inicialización de jqGrid.
  • A continuación, creamos un objeto anónimo en el que establecemos a true las propiedades del y refresh, que indica que queremos mostrar los botones de eliminación y recarga de datos. El resto de propiedades predefinidas, add, edit, y search, equivalentes a los botones de añadir, editar y buscar registros, respectivamente, las establecemos a false con objeto de que no aparezcan botones para invocarlas; ya las activaremos en otros posts ;-)
  • el siguiente parámetro se trata de un objeto donde configuramos el comportamiento del botón de alta de registros. Dado que no vamos a implementarlo ahora, lo establecemos a nulo.
  • el cuarto parámetro es lo mismo que el anterior, pero para configurar la edición, por lo que también se encuentra establecido a null.
  • a continuación, y por último, el objeto en cuyas propiedades definimos el comportamiento del botón de eliminación:

    • La URL a la acción que se invocará en servidor, que la obtenemos utilizando el UrlHelper de MVC. En este caso, invocaremos a una acción llamada “Eliminar”, a la que el sistema enviará el Id del registro activo.
    • El ancho del cuadro de diálogo de confirmación.
    • En afterSubmit implementamos una función callback que jqGrid llamará cuando haya recibido el resultado de la petición Ajax. El primer parámetro que nos envía es el objeto XMLHttpRequest donde encontraremos la respuesta obtenida desde el servidor; el segundo parámetro contiene los datos que han sido enviados a la petición.

      El retorno de la función callback debe ser siempre un array con dos elementos. El primero es un booleano indicando si la eliminación ha tenido éxito, y el segundo es el mensaje de error, en caso de que se haya producido:

      return [ exito , "mensaje de error" ];

      Es importante ahora resaltar una cosa: salvo los parámetros de entrada y el tipo de retorno descritos anteriormente, jqGrid no nos impone ninguna particularidad más respecto a cómo debemos implementar este método, el tipo de información que recibiremos desde el controlador o cómo la procesaremos al recibirla. Somos totalmente libres de elegir la forma en la que haremos las cosas.

      En nuestro caso, vamos a hacer una implementación muy simple en base a la siguiente convención: el controlador retornará un string con una descripción del error en caso de que se produzca algún problema borrando el registro, y retornará un nulo cuando todo vaya bien. Esto nos permite implementar el callback utilizando la siguiente expresión:
    • return [r.responseText=="", r.responseText];

      Si observáis, estamos llenando el array de retorno de tal forma que el primer parámetro será cierto si la respuesta obtenida está vacía (o sea, no hay error), y en el segundo parámetro introducimos la respuesta tal cual la hemos obtenido del servidor.

4. El controlador

Dado que la lógica la tenemos implementada en el modelo, y que la vista ya está preparada para ponerse en contacto con el controlador vía Ajax y para recibir el feedback sobre el éxito de la operación, sólo nos queda implementar muy rápidamente el método de acción:

public ActionResult Eliminar(int id)
{
    if (!gestorDeAmigos.Delete(id))
        return Content("Error eliminando el registro");
 
    return null;
}

 

Podréis observar que si se produce un error, retornamos un ContentResult describiendo el problema; en otros casos, devolvemos un nulo.

Para probar el funcionamiento de una eliminación errónea podéis, por ejemplo, abrir dos navegadores contra la aplicación, borrar un registro desde uno de ellos e intentar borrarlo también desde el otro.

Y… ¡Voilá!

Una vez habiendo implementado el modelo, la vista y el controlador, sólo nos queda probar la aplicación, que veremos que funciona perfectamente ;-P

jqGrid+ASP.NET MVC en acción

Resumiendo, en este post hemos seguido profundizando en las capacidades de jqGrid, implementando paso a paso la funcionalidad de eliminación de registros. Hemos podido observar también el escaso código (¡a pesar de la longitud del post!) que hay que añadir para disponer de esta funcionalidad, y de lo sencillo y reutilizable que resulta su implementación.

Descargar proyecto de demostración:

 

Publicado en: Variable not found.

Hojas con estilo usando .less

 

.Less: CSS Dinámicos para .NET Seguro que más de una vez has sufrido de lo lindo al tocar un archivo de hoja de estilos. Suelen estar mal organizados, ser muy extensos, difíciles de mantener, hay declaraciones duplicadas por todas partes… Y seguro que siempre te has preguntado si hay otra forma menos complicada de conseguir lo mismo.

K. Scott Allen nos habla en su post Keeping CSS Files DRY de .Less (dot less), un interesante componente portado del mundo Ruby capaz de “compilar” hojas de estilo que utiliza una notación extendida y mucho más potente del estándar CSS, permitiendo las siguientes características:

  • Uso de variables. Less permite declarar y utilizar variables para los valores que utilizaremos frecuentemente en nuestra hoja de estilos, así:

    @nice-blue: #5B83AD;
     
    #header { color: @light-blue; }
    #footer { color: @light-blue; }

  • Operaciones. Podemos utilizar expresiones para obtener valores, por ejemplo:

    @base: 5%;
    @filler: @base * 2;
    @other: @base + @filler;
     
    color: #888 / 4;
    background-color: @base-color + #111;
    height: 100% / 2 + @filler;

    Fijaos en un detalle de lo más interesante: Less es capaz de distinguir cuándo está operando con colores y cuándo con unidades, ofreciendo el resultado correcto en cada caso.

  • Mezclas (mixins), otra potente capacidad de Less que seguro que puede venirnos bastante bien, que consiste en incluir dentro de un conjunto de reglas dentro de otro, sólo haciendo referencia a su selector:

    .bordered {
      border-top: dotted 1px black;
      border-bottom: solid 2px black;
    }
     
    #menu a {
      color: #111;
      .bordered;
    }
     
    .post a {
      color: red;
      .bordered;
    }

  • Reglas anidadas, que nos permiten definir reglas siguiendo una estructura más legible y fácilmente reconocible por su paralelismo con la estructura del documento:

    #header {
      color: black;
     
      .navigation {
        font-size: 12px;
      }
      .logo {
        width: 300px;
        :hover { text-decoration: none }
      }
    }

  • Visibilidad de variables. Las variables pueden ser declaradas de forma global (como en el primer ejemplo) o asociadas a algún selector, lo que les aporta ámbito de visibilidad de forma muy similar a los lenguajes tradicionales:

    @var: red;
     
    #page {
      @var: white;
      #header {
        color: @var; // color será “white”
      }
    }

  • Accesores. Less permite utilizar valores de propiedades y variables como contenido para otras propiedades de forma muy natural:

    #defaults {
      @width: 960px;
      @color: black;
    }
     
    .article { color: #294366; }
     
    .comment {
      width: #defaults[@width];
      color: .article['color']; 
    }

    Fijaos que el bloque #defaults no tiene por qué corresponder con un elemento del documento a formatear, se trata sólo de una forma de agrupar variables y reglas, consiguiendo un efecto empaquetador muy similar a los espacios de nombres.

  • Comentarios en línea, al más puro estilo C++ ó C#:

    // Get in line!
    @var: white;

  • Importación de archivos, permitiendo acceder a reglas y variables definidas en otros archivos:

    @import "library"; // Si la extensión es .less, se puede omitir
    @import "typo.css";

¿Y cómo funciona esto? Muy sencillo. En primer lugar, escribimos la hoja de estilos en un archivo .less, en el que podemos utilizar las características descritas anteriormente, y lo incluimos en las páginas donde nos interese, como lo hacemos siempre:

<link href="/Content/estilos.less" rel="stylesheet" type="text/css" />

Es interesante tener en cuenta que Less es compatible con CSS, lo que quiere decir que si simplemente renombramos el .css a .less, ya tendremos una hoja de partida.

A continuación, debemos introducir en el web.config una declaración que asocie la extensión .css al compilador Less:

<httpHandlers>
  <add validate="false" path="*.less" verb="*"
    type="dotless.Core.LessCssHttpHandler, dotless.Core" />
</httpHandlers>

Cuando llega una petición, el handler se encargará de compilar el archivo .less, generar el .css correspondiente y enviarlo minimizado al cliente, cacheando el resultado para posteriores usos, si se lo indicamos en la configuración, una sección opcional del web.config.

.Less está diseñado para ASP.NET 3.5 o superior, y por supuesto, es válido tanto para Webforms como para ASP.NET MVC. Se distribuye bajo licencia Apache 2.0, y está aún en fase beta, por lo que habrá que estar atento a la evolución de este interesantísimo proyecto.

Enlaces:

Crosspostergado desde: Hojas con estilo usando .less @ Variable not found 

Indicios de que tu interfaz de usuario fue creado por un programador

¡Programadiseñadores al poder! Ya sabemos lo que suele ocurrir cuando los programadores diseñamos interfaces de usuario ;-). Para seguir profundizando en este curioso e inevitable fenómeno, Ian Voyce ha publicado hace unas semanas el divertido post The 7 signs your UI was created by a programmer, en el que recoge pistas que nos ayudarán a determinar si el interfaz de la aplicación que usamos a diario está creado por un diseñador experto en usabilidad, o ha sido el propio desarrollador el encargado de dichas tareas.

Bueno, en realidad se trata más bien de una lista con malos hábitos en el diseño de interfaces, y no necesariamente creados por programadores, pero ciertamente me he reconocido en alguno de los puntos y me han hecho gracia. Los cito y comento a continuación:

  • Iconos de exclamación en cuadros de diálogo
    Efectivamente, cuando incluimos una exclamación en un cuadro de diálogo no pensamos en que es posible que el usuario vea el mensaje 50 veces al día, con lo que dejaría de ser ese mensaje asombroso y puntual que pensamos en un principio. Hoy en día, muchas aplicaciones utilizan un tono mucho más neutral y cercano para enviar mensajes al usuario.
     
  • Mensajes con dobles negaciones en cuadros de diálogo, del tipo “¿Seguro de que no quieres perder tus cambios?” -- Hmm… sí… digooo, no…  También son muy propias construcciones más complejas de la cuenta, paradójicas o inesperadas, como “Su mensaje no se ha enviado, ¿desea descartarlo?”, que ponen a prueba los reflejos de cualquier usuario. 
     
  • Orden de tabulación inexistente, o incorrecto, que fuerzan a que la navegación se realice exclusivamente a golpe de ratón. Y especialmente en aplicaciones de entrada masiva de información, el cambio de teclado a ratón y viceversa es terrible. Y paraGroupboxes para todo.. porque mola hacernos conscientes de ello, nada como dar vacaciones un rato al animalito y ver lo bien que manejamos determinadas aplicaciones.
     
  • Groupboxes rodeándolo todo… porque hay sitios donde queda bonito, aunque no esté agrupando nada ;-). Me confieso: durante años, he rodeado de groupboxes todo lo que se me ponía por delante, porque me parecía más agradable a la vista. Ahora lo estoy dejando ;-)
     
  • IconoIconos creados en el IDE, je, este es otro de mis puntos fuertes: soy un fiera diseñando iconos desde el editor de Visual Studio. El problema es que, a pesar de lo orgulloso que estoy siempre de mis creaciones artísticas, no quedan bien e incluso introducen un cierto tufillo a cutre en las aplicaciones.
     
  • Utilización de grids, debido a lo que llama Voyce “la enfermedad de considerar que Excel es lo máximo en diseño de interfaces de usuario”, que fomenta la creación de rejillas con controles de lo más potentes y variopintos en las filas de datos (calendarios, gráficos, etc.).
     
  • Message Boxes no implementados, el equivalente en los interfaces de usuario a los TODO en el código fuente :-DD. Buena analogía. Algo parecidos serían los mensajes del tipo “Este mensaje no debería aparecer nunca” que alguna vez he encontrado por ahí, auténticos actos de rebeldía contra sus respectivos creadores.
     
  • Uso excesivo de cuadros de diálogo, incluso en situaciones en las que perfectamente podríamos obviarlos por no ofrecer información útil para el usuario, que no va a poder entender, o donde no se requieren decisiones por su parte.

    Los componentes necesarios han sido detectados. Accediendo...Esto es algo muy frecuente de ver. Por ejemplo, el otro día accediendo a una aplicación web para la que es necesario contar con certificado digital, me saltó la alerta que muestro a la derecha:

    Sí, probablemente los componentes Java necesarios para la autenticación y firma electrónica hayan sido detectados con éxito, pero… ¿realmente era necesario interrumpir mi navegación para informarme de ello? ¿Un usuario, sabrá interpretar el mensaje? Y en cualquier caso, ¿le aporta algo? No sé, no sé… me da que es un alert() creado por un desarrollador durante las pruebas y que al final se quedó ahí para la posteridad.

Hasta aquí las citadas en el post original, aunque siguiendo en la misma línea, puedo añadir algunas más de propia cosecha:

  • Cuadros de diálogo con mensajes y botones inconsistentes. Mensajes del tipo “¿desea cancelar la operación?” en cuyo pie aparece un botón “Aceptar” y otro “Cancelar” siempre me dejan pensando un rato. Si pulso aceptar, ¿estoy aceptando la cancelación, o justo lo contrario?… Muchas veces, parece que sólo podemos confiar en el azar.
  • Cuadros de mensaje con demasiados (o demasiados pocos) botones. No siempre atendemos a la Ley de Hick, que nos dice que “el tiempo que se tarda en tomar una decisión aumenta a medida que se incrementa el número de alternativas”, y eso nos lleva a crear cuadros de diálogo con muchos, demasiados, botones que el usuario debe estudiar.

    Y justo en el lado contrario, pero igualmente desconcertantes, son los cuadros de diálogo de introducción de datos sin ningún botón de aceptación, en los que su cierre (con la X de la esquina superior) implica el salvado de datos.
  • Interfaz descuidado. Y no hablo de bonito o feo, términos bastante subjetivos, sino de descuidado: controles sin alinear, con tamaños no proporcionales al contenido pretendido, sensación de desorden, faltas de ortografía… Esto no es un problema de interfaces creados por desarrolladores, sino creados por gente que no pone el más mínimo cariño en lo que hace, independientemente de su puesto de trabajo. 
     
  • Interfaces monocromos, mi especialidad. Como inútil reconocido en temas de diseño, me considero absolutamente incapaz de crear algo medio aparente con más de un color (eso sí, empleando distintas tonalidades ;-)). Nunca he entendido la compleja lógica que hay detrás del “eso no pega con aquello”, que algunos/as parecen dominar de serie. 
     
  • Controles con trastorno de personalidad. Un clásico es encontrar un grupo de checkboxes actuando como un grupo de radios. A ver, si sólo vamos a poder elegir una de las opciones, ¿por qué no usamos directamente el control específicamente ideado para ello? ¿Y un desplegable con las opciones "Activar" y "Desactivar"? ¿No sería más claro poner un checkbox?  

Artículo original: The 7 signs your UI was created by a programmer

Crossposteadísimo desde: Indicios de que tu interfaz de usuario fue creado por un programador @ Variable not found

Más artículos Página siguiente >