Rutas en minúsculas en MVC 4 y ASP.NET 4.5

Seguro que alguna vez habéis notado que al generar URLs hacia acciones de una aplicación MVC usando helpers como Url.Action() o Html.ActionLink(), éstas son generadas usando mayúsculas y minúsculas según hubiéramos indicado en los parámetros de las llamadas:

Helper URL generada
@Url.Action("index", "home") /home/index
@Url.Action("List","Products", new{ Category="PC" }) /Products/List?Category=PC
@Url.Action("VIEWALL", "PRODUCTS") /PRODUCTS/VIEWALL

Como podemos ver, la URL resultante queda a criterio del desarrollador o, lo que es peor, al puro azar. A veces incluso no es algo que podamos elegir fácilmente, puesto que son generadas por otros componentes como T4MVC. Y no sé si desde el punto de vista del SEO tendrá su impacto, pero desde luego el ofrecer estas direcciones sin un aspecto unificado no da buena impresión.

En versiones anteriores a ASP.NET 4.5, esto podíamos solucionarlo creando helpers, o rutas personalizadas que, heredando de Route, realizaran esta conversión a minúsculas. Sin embargo, ahora disponemos de un mecanismo más cómodo para forzar que las URLs generadas sean normalizadas a minúsculas, así:

1
2
3
4
5
6
7
8
9
10
public class RouteConfig
{
    public static void RegisterRoutes(RouteCollection routes)
    {
        routes.LowercaseUrls = true;
        routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
 
        [...]
    }
}

Un detalle, sin duda interesante, para tener en cuenta en nuestros desarrollos.

Publicado en: Variable not found.

Asincronía en MVC 4

ASP.NET MVCEste es el mensaje que deberíamos interiorizar, si no lo hemos hecho ya, a la vista de las múltiples novedades introducidas en las últimas versiones de la plataforma ASP.NET y MVC:

Asíncronía = bueno

A grandes rasgos, la explicación es la siguiente: IIS tiene disponible un número limitado de hilos (threads) destinados a procesar las peticiones. Cuando llega una petición, uno de estos hilos es asignado en exclusiva a ella y permanecerá ocupado hasta que haya sido totalmente procesada. Si llegan peticiones cuando todos los hilos están ocupados, se introducen en una cola, también limitada. Cuando el tamaño máximo de esta cola ha sido superado, ya al servidor no le queda más remedio que responder con un error HTTP 503 al usuario indicándole que está seriamente ocupado.

Si las peticiones son “despachadas” de forma rápida es realmente difícil alcanzar estos límites. El problema aparece cuando el proceso de las peticiones requieren la ejecución de alguna tarea de larga duración que dependa de recursos externos, como puede ser una consulta pesada a la base de datos o la obtención de información desde un servicio web; en estos casos, el hilo asignado a la petición quedará bloqueado hasta que la tarea finalice.

Imaginemos, por ejemplo, el siguiente controlador:

1
2
3
4
5
public ActionResult ShowTheAnswer()
{
 var result = _oracle.GetTheAnswerToLifeTheUniverseAndEverything();
 return View(result);
}

Si la llamada a GetTheAnswerToLifeTheUniverseAndEverything() tardase, digamos 10 segundos, obviamente el usuario no vería el resultado hasta ese momento, pero, además, estaríamos desperdiciando un valioso recurso: el hilo asignado por IIS, que permanecería todo ese tiempo esperando a que el método realizara su trabajo. Probablemente, durante estos 10 segundos el mismo hilo podría haber atendido muchas otras peticiones.

Y es ahí donde se encuentra la gracia de los controladores asíncronos. Permiten “liberar” el hilo del servidor web para que pueda procesar otras peticiones mientras se espera el resultado de la ejecución de la tarea conflictiva. Una vez ésta ha finalizado, el servidor asignará otro hilo para que procese el resultado obtenido y finalmente retorne el resultado al cliente.

Al igual que antes, el usuario seguirá sin ver nada en su pantalla hasta transcurridos los 10 segundos, la diferencia está en que el servidor estará aprovechando mucho más los threads disponibles y, por tanto, será capaz de gestionar muchas más peticiones. Aunque en aplicaciones con poca carga normalmente no necesitaremos utilizarlos, sí suponen una diferencia importante cuando estamos hablando de dar servicio a una gran cantidad de usuarios concurrentes.

Acciones asíncronas en MVC 4

Los controladores asíncronos están disponibles desde las primeras versiones de ASP.NET MVC y, aunque permitían aplicar esta técnica, resultaban bastante tediosos de utilizar, pues parte de la gestión de la asincronía debíamos implementarla de forma manual. Podéis ver ejemplos aquí (Gisela Torres) o  aquí (Maxi Lovera).

A partir de MVC 4, y gracias a que la asincronía está siendo incorporada de forma masiva en el framework, podemos hacerlo de forma muchísimo más sencilla. Básicamente sólo tenemos que seguir cuatro pasos si usamos Visual Studio 2012 y ASP.NET 4.5:

  • Hacer que la acción retorne un Task<T>, donde T será normalmente del tipo ActionResult o alguno de sus descendientes.
  • Introducir en la declaración del método de acción la palabra clave async de C#, indicando de esta forma que se va a invocar un proceso asíncrono cuyo resultado obtendremos con await. Si no has oído hablar antes de estas dos novedades de la versión 5 de nuestro lenguaje favorito, puedes leer este post del gran Eduard Tomás donde las explica perfectamente.
  • Opcionalmente, si queremos seguir las convenciones de nombrado para métodos asíncronos, debemos añadir a su nombre el sufijo “Async”.
  • Por último, ya en el cuerpo de la acción, llamar a los métodos asíncronos usando await.

Nota: para conseguir lo mismo con Visual Studio 2010 es necesario instalar el Visual Studio Async CTP, aunque por lo que he podido comprobar no es una tarea sencilla.

Vamos a ilustrarlo con un ejemplo. El siguiente código muestra la versión síncrona, o tradicional, de un controlador, así como un método del Modelo que realiza un trabajo de larga duración:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// Controller
public class OracleController : Controller
{
    [...]
    public ActionResult ShowTheAnswer()
    {
        var result = _oracle.GetTheAnswerToLifeTheUniverseAndEverythingAsync();
        return Content("Response: " + result);
    }
}
 
// Model
public class Oracle
{
    [...]
    public int GetTheAnswerToLifeTheUniverseAndEverything()
    {
        Thread.Sleep(10000); // Simulate a very hard task
        return 42;
    }
}

Y a continuación mostramos la versión asíncrona de la misma acción y el correspondiente método del Modelo:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Controller
public class OracleController : Controller
{
    [...]
    public async Task<ActionResult> ShowTheAnswerAsync()
    {
        var result = await _oracle.GetTheAnswerToLifeTheUniverseAndEverythingAsync();
        return Content("Response: " + result);
    }
}
 
// Model
public class Oracle
{
    [...]
    public Task<int> GetTheAnswerToLifeTheUniverseAndEverythingAsync()
    {
        return Task.Factory.StartNew(() =>
        {
            Thread.Sleep(10000); // Simulate a very hard task
            return 42;
        });
    }
}

Desde el punto de vista del Controlador, la llamada al método del Modelo retornará un objeto de tipo Task que representará a la tarea que ha comenzado a realizarse en segundo plano. El uso de await hará que el thread del servidor web sea liberado, y que se vuelva a tomar el control en este punto cuando la tarea haya finalizado y ya tengamos un resultado disponible.

Por tanto, podríamos generalizar el patrón para la implementación de acciones asíncronas que retornan una vista de la siguiente forma:

1
2
3
4
5
public async Task<ActionResult> MyAction(param1, param2…)
    {
        var result = await AsyncOperation();
        return View(result);
    }

¿Más de una tarea asíncrona desde el interior de la acción?

Si estamos ante un escenario en el que necesitamos ejecutar más de una tarea asíncrona en la misma acción, podemos optar por hacerlo de forma secuencial o paralelizar también estas tareas.

El primer enfoque, bastante trivial, consistiría en realizar las llamadas una detrás de otra. Por ejemplo, volviendo sobre el ejemplo anterior, si quisiéramos obtener una segunda opinión sobre las verdades del universo, podríamos hacer como sigue:

1
2
3
4
5
6
public async Task<ActionResult> ShowTheAnswerAsync()
{
    var result1 = await _oracle1.GetTheAnswerToLifeTheUniverseAndEverythingAsync();
    var result2 = await _oracle2.GetTheAnswerToLifeTheUniverseAndEverythingAsync();
    return Content("Response: " + (result1+result2)/2);
}

La primera llamada al método asíncrono retornará el Task<int> representando a la tarea que acabará de comenzar a ejecutarse, y la palabra clave await hará que el hilo quede liberado hasta que se obtenga la primera respuesta, momento en que se volverá a tomar el control para introducirla en result1. A continuación se hará lo mismo para obtener la segunda opinión.

El tiempo total de ejecución, desde que la acción comienza a ejecutarse hasta que el resultado es enviado al cliente será de 20 segundos, asumiendo que cada llamada tarda 10 segundos.

La otra posibilidad es paralelizar ambas llamadas. En este caso, lanzamos las dos tareas en paralelo, obteniendo las referencias hacia las mismas, y usamos awaitWhenAll()  para esperar la finalización de ambas:

1
2
3
4
5
6
7
public async Task<ActionResult> ShowTheAnswerAsync()
{
    var task1 =  _oracle1.GetTheAnswerToLifeTheUniverseAndEverythingAsync();
    var task2 =  _oracle2.GetTheAnswerToLifeTheUniverseAndEverythingAsync();
    await Task.WhenAll(task1, task2);
    return Content("Response: " + (task1.Result+task2.Result)/2);
}

En este caso, dado que ambas tareas se realizan de forma simultánea, el resultado será mostrado al usuario en 10 segundos.

That’s all, folks!

Bueno, hay más cosas que contar, pero de momento vamos a dejarlo aquí 🙂

La implementación de acciones asíncronas para la realización de tareas de cierta duración aporta grandes beneficios en sistemas de alta concurrencia, y con ASP.NET MVC 4 resulta realmente sencillo.

Su uso está recomendado cuando la acción vaya a realizar una tarea de larga duración e involucre el acceso a recursos externos. Es decir, si el proceso a realizar es costoso pero de uso intensivo de CPU, no se obtendrá un beneficio destacable.

Más información en: Using Asynchronous Methods in ASP.NET MVC 4

Publicado en: Variable not found.

TypeScript: un primer vistazo

TypeScriptSiempre he tenido la sensación de que Javascript no ha evolucionado de forma proporcional a las responsabilidades y expectativas que han ido depositando en él. El explosivo crecimiento que ha sufrido en los últimos años no venía acompañado de mejoras en el lenguaje que nos ayudaran a implementar aplicaciones cada vez más extensas y complejas, para las que es muy conveniente disponer de herramientas más potentes que las ofrecidas de serie por este veterano lenguaje.

Y Microsoft ha movido ficha. Hace un par de días, S. Somasesegar, vicepresidente corporativo de la división de desarrollo de la casa, presentó un nuevo lenguaje que pretende acabar con las tradicionales carencias de Javascript, al que han denominado TypeScript. Detrás de este nuevo “invento” está el mismísimo Anders Hejlberg, padre de C# y otras maravillas, que ya sabíamos hace tiempo que andaba liado con algo relacionado con Javascript.
image
En cuanto a sintaxis, TypeScript es un superconjunto de Javascript, es decir, todo código Javascript es código TypeScript. Sin embargo, amplía al original para dotarlo de características como clases, interfaces, módulos, importación de archivos, visibilidades públicas y privadas, o incluso strong typing mediante el uso de anotaciones o nuevas palabras clave, todas ellas de uso opcional.

El resultado de compilar código TypeScript es Javascript estándar, lo que quiere decir que se podemos usarlo normalmente y la salida será directamente utilizable en cualquier navegador sin necesidad de instalar plugins o máquinas virtuales específicas para soportar esta tecnología, al igual que podrá correr sobre Node.js o cualquier intérprete o compilador del estilo.

Como ya empieza a parecer normal en Microsoft, este proyecto es Open Source (licencia Apache 2.0) y es posible acceder al código fuente desde Codeplex. La versión actual es la 0.8.

Por cierto, el motivo de entrecomillar “invento” algunos párrafos más arriba es que conceptualmente no se trata de algo nuevo. Hace tiempo que existen lenguajes que pueden ser compilados a Javascript, como CoffeScript, Dart, Kaffeine, y muchos más, aunque normalmente usan una sintaxis propia más o menos alejadas de sus orígenes, por lo que su adopción es más costosa.

También existen compiladores cruzados como Saltarelle, recientemente presentado, que permiten escribir código C# y generar Javascript, aunque obviamente en este caso estaríamos alejándonos de la realidad que hay por detrás.

Instalación de TypeScript

En primer lugar, he de decir que para una primera toma de contacto con el  lenguaje no es necesario descargar ni instalar nada. La web oficial de TypeScript incluye un completo playground donde podemos escribir directamente en este lenguaje, obteniendo el resultado en Javascript en tiempo real. Muy recomendable echarle un vistazo 🙂
Instalando TypeScript
Pero si preferimos comenzar a usarlo, en la misma web se describe cómo descargarlo en Node.js (distribuido como paquete NPM) y en Visual Studio 2012 (incluida la versión  Express). En este último caso, debemos descargar e instalar el plugin, con el que conseguiremos una integración bastante razonable del lenguaje en el entorno, y casi estaremos listos para entrar en faena.

Una vez instalado, ya podremos crear y editar archivos TypeScript, que tienen extensión .ts, aunque aún tendremos que hacer algún apaño para que éstos sean compilados de forma automática. También podemos compilar a través de línea de comandos, pero obviamente no es la manera más cómoda de hacerlo 😉

Bueno, una posibilidad rápida para probarlo es crear un proyecto MVC 3 (¡sí, MVC 3!), y veremos que aparece una nueva plantilla llamada “TypeScript Internet Application”:

Nueva plantilla de proyecto
En este caso, ya en la carpeta /Script podremos ver algunos archivos .ts y comenzar a curiosear o desarrollar nuestros propios componentes usando este lenguaje.

Pero ojo, que los archivos .ts sólo son compilados cuando se compila el proyecto; es decir, el correspondiente .js no se generará automáticamente al salvar el archivo TypeScript, aunque supongo que esto cambiará en el futuro. En cualquier caso, el archivo .js, obviamente, es el que deberemos referenciar desde nuestras vistas o páginas.

Si preferimos usar otro tipo de proyecto como ASP.NET Web Forms o MVC 4, tendremos que añadir de forma manual el siguiente código en el archivo .csproj para que los ficheros .ts sean compilados junto con el proyecto:

1
2
3
<Target Name="BeforeBuild">
   <Exec Command="&amp;quot;$(PROGRAMFILES)Microsoft SDKsTypeScript.8.0.0tsc&amp;quot; @(TypeScriptCompile ->'&quot;%(fullpath)&quot;', ' ')" />
</Target>

Esta porción de código debemos añadirla al final del archivo .csproj del proyecto, sustituyendo (o complementando, si ya tenemos eventos de compilación) la sección <Target> del mismo.

Eso sí, asegurad que el bloque no está comentado porque entonces este cambio no será tenido en cuenta. Os cuento esto porque por defecto esta sección viene comentada y si incluimos en ella el código y no eliminamos los comentarios no os funcionará (a mí me ha llevado un rato darme cuenta ;-)).

Algunas características de TypeScript

La especificación de TypeScript ya está disponible, y su lectura puede dar una idea de las principales características del lenguaje. Vamos a ver algunas de ellas.

En TypeScript, podemos añadir información de tipo a las funciones, parámetros y variables, siempre opcionalmente:

Tipado

El editor integrado en Visual Studio en todo momento ofrecerá ayuda sobre los métodos disponibles, los tipos de datos, etc… ¡full intellisense power! Incluso en el mismo editor podremos ver los errores que cometemos en el código y harán fallar la compilación.

En un principio, disponemos de los tipos primitivos string, number, y bool, así como de otros tipos especiales como any (similar a object, la clase base para todos los tipos), void (similar a la sintaxis C#), Null, o Undefined. También existe inferencia de tipos al usar var:

Tipos

¿Y a quién no le gusta usar parámetros opcionales?

Parámetros opcionales

También podemos usar una sintaxis tipada para los tipos función, de forma bastante parecida a las habituales expresiones lambda:

Tipos función

Podemos escribir clases, con propiedades, constructor y métodos de forma muy sencilla y natural:

Clases

Y, por supuesto, es posible usar herencia y sobrescribir métodos:

Herencia

Las clases pueden tener miembros públicos y privados e intellisense será consciente de ello. Por defecto, como en Javascript, son públicos:

Visibilidad

¿Y usar interfaces? Pues también:

Interfaces

Es posible organizar el código en módulos que, por supuesto, pueden ser almacenados en archivos externos para hacer más manejables las aplicaciones de cierto volumen:

Módulos

Lógicamente hay muchos detalles más, pero creo que lo visto es suficiente para hacernos una idea del enfoque de TypeScript. Os remito a las especificaciones del lenguaje para ampliar información.

En todos los casos que hemos visto el código Javascript generado al compilar es limpísimo, muy legible, totalmente respetuoso con los estándares, y usa los patrones y buenas prácticas habituales de este lenguaje. Por ejemplo, el Javascript generado correspondiente al módulo anterior es el siguiente:

Javascript generado

Dado que la generación de los archivos .js se realiza en tiempo de compilación, estos ficheros pueden ser incluidos en el proceso habitual de publicación en los servidores de producción, podemos seguir usando los bundles o sistemas de compactación y minimización, etc.

Finalmente, decir que la primera impresión es bastante positiva. Si el concepto ya era atractivo, la potencia y sencillez del lenguaje propuesto por Microsoft y su integración con Visual Studio harán de este producto una interesantísima opción para solventar fácilmente algunas de las trabas que encontramos al trabajar con Javascript puro, a cambio de un esfuerzo de aprendizaje mínimo y usando un procedimiento muy poco intrusivo en nuestros desarrollos. Habrá que estar atentos a la evolución del producto.

Eso sí, todavía quedan algunos detalles por pulir, como la mejor integración con el entorno (por ejemplo, generar Javascript al grabar el archivo en lugar de esperar a compilar), pero seguramente estarán solucionados para la versión definitiva.

Enlaces:

Publicado en: Variable not found.