ASP.NET MVC – ¿Por qué usar los helpers para formularios?

Hace nada mi compi Javier Torrecilla (Little Tower para los amigos) ha escrito un post sobre los helpers de ASP.NET MVC.

En este post quiero centrarme en por qué debes usar los helpers para formularios de ASP.NET MVC. La respuesta “por qué están ahí” no vale. Hay muchas cosas que están por ahí y no deberían usarse salvo casos muy concretos (incluso cosas del .NET Framework).

Iremos como los New Kids on the Block, es decir paso a paso. Por supuesto si ya sabes todo lo que te aportan los helpers entonces este post no te aportará mucho, pero eres bienvenido a seguir leyendo por supuesto (y a comentar, claro) 🙂

Pregunta: Como recibo en un controlador los datos mandados en un <form>?

Esa es una pregunta típica que se hace cualquiera que empieza con ASP.NET MVC. Si vienes de ASP antiguo (el clásico, el de Interdev) o de otra tecnología como PHP, pues igual empiezas a buscar alguna propiedad llamada Form en la Request o algo así. ¡No busques! No lo hagas porque la encontrarás y entonces la usarás y te perderás uno de los elementos más poderosos de ASP.NET MVC: el model binder.

Para responder a esta pregunta vamos a usar esta vista:

Da de alta una cerveza:

 

<form method="POST">

    Nombre:

    <input type="text" name="Name" />

    <br />

    Categoría:

    <input type="text" name="Category" />

    <br />

    <input type="submit" value="Enviar!"/>

</form>

Todo HTML, que de moment no usamos Helpers 🙂

Veamos como no acceder a los valores de un formulario. Si vienes de PHP o Interdev probablemente habrás llegado a teclear algo como esto:

public ActionResult Index()

{

    if (Request.Form["Name"] != null && Request.Form["Category"] != null)

    {

        // Procesar alta

        return null;

    }

    else

    {

        return View();

    }

}

Parece lógico ¿no? Buscamos si los datos de formulario existen, y si exiten es que venimos del POST y los procesamos. En caso contrario es que nos han hecho un GET por lo que devolvemos la vista.

Vale… olvida este código. Así no se hacen las cosas en ASP.NET MVC. Primero la lógica del GET (mostrar una vista) y del POST (procesar una alta) están mezclados y eso no es buena señal (¿conoces el SRP)? Por suerte ASP.NET MVC nos permite separar la lógica del GET de la del POST definiendo dos métodos y decorando el que gestiona el POST con el atributo [HttpPost].

Tener la lógica separada sería un poco mejor y de hecho hay por Internet código parecido al siguiente:

public ActionResult Index()

{

    return View();

}

 

[HttpPost]

public ActionResult Index(FormCollection form)

{

    if (form["Name"] != null && form["Category"] != null)

    {

        // Procesar alta

    }

    return null;

}

La clase FormCollection que aparece es una clase propia de ASP.NET MVC que tiene la misma información que Request.Form. Este código funciona y de hecho verás código así por Internet (se ve de todo en este mundo) pero la realidad es que hay muy pocas razones para usar FormCollection hoy en día. ASP.NET MVC tiene un concepto mucho más poderoso, un amigo al que debes conocer y entender: el model binder.

Ya he hablado en este blog sobre el model binder, así que ahora solo introduciré la regla más básica: Si tu acción tiene un parámetro cuyo nombre es idéntico al de un atributo name de un campo de un formulario, el model binder enlazará el valor del campo al parámetro automáticamente.

Es decir, el código anterior nos puede quedar como:

[HttpPost]

public ActionResult Index(string name, string category)

{

    // procesar alta

    return null;

}

Esa es la regla básica del model binder. Estoy seguro que esto te parece precioso pero a la vez es posible que una inquietud atormente tu nueva felicidad: si tengo un formulario con 20 campos… se deben declarar 20 parámetros en la acción?

Por suerte el equipo de ASP.NET MVC se dio cuenta de ello y así surge la segunda (y última que veremos en este post) regla del model binder. Ojo, que ahí va: Si tu accíón recibe como parámetro, un objeto de una clase que tiene una propiedad cuyo nombre es igual al de un atributo name de un campo del formulario, el model binder enlazará automáticamente el valor de este campo a la propiedad correspondiente del objeto recibido como parámetro. Quizá te parezca un poco rebuscado, pero lo que significa es que puedo tener una clase como la que sigue:

public class BeerModel

{

    public string Name { get; set; }

    public string Category { get; set; }

}

Y ahora en mi controlador recibir los datos simplemente usando un parámetro de tipo BeerModel:

[HttpPost]

public ActionResult Index(BeerModel beer)

{

    // procesar alta

    return null;

}

El model binder se encarga de crear el objeto beer y de rellenarlo con los parámetros del formulario. ¿Genial, no?

Pregunta: Vale, pero esto que tiene que ver con los helpers?

De momento nada, pero ahora veamos que ocurre si no los ponemos. Imagina que alguien me da de alta una cerveza pero hace submit del formulario sin entrar el nombre que es obligatorio. Evidentemente yo tengo que mostrarle de nuevo la vista de dar de alta una cerveza. Una forma no muy correcta de hacerlo es la siguiente:

[HttpPost]

public ActionResult Index(BeerModel beer)

{

    if (string.IsNullOrEmpty(beer.Name))

    {

        return View(beer);

    }

    // procesar alta

    return null;

}

Si el nombre de la cerveza está vacío devuelvo la vista como resultado, pero le paso la propia cerveza. ¿Para qué? Pues porque ahora tengo que meter código para recuperar el estado de los controles. Es decir, debo rellenar el atributo value de los dos <input> con los valores que me vienen de beer, ya que si no el usuario recibiría… ¡una vista completamente vacía!. Si vienes de Webforms este es un buen punto para empezar a odiar ASP.NET MVC, pero si lo sobrepasas luego, estoy seguro, lo empezarás a amar irremediablemente.

En resumen nuestra vista ahora queda algo como así:

@model MvcApplication1.Models.BeerModel

@{

    ViewBag.Title = "Index";

    var nameValue = Model != null ? Model.Name : null;

    var categoryValue = Model != null ? Model.Category : null;

}

Da de alta una cerveza:

 

<form method="POST">

    Nombre:

    <input type="text" name="Name" value="@nameValue"/>

    <br />

    Categoría:

    <input type="text" name="Category" value="@categoryValue"/>

    <br />

    <input type="submit" value="Enviar!"/>

</form>

Aquí me estoy aprovechando de una característica de Razor que es que si igualamos un atributo de un elemento HTML a null, entonces Razor ni nos renderiza el atributo. Es decir si nameValue o categoryValue vale null, el atributo value ni aparece en el HTML generado.

Vale… ahora imagínate un formulario con 20 campos y ya puedes empezar a llorar.

Y esta es la primera razón por la que usar helpers. Si modificamos la vista para usar los helpers:

@model MvcApplication1.Models.BeerModel

@{

    ViewBag.Title = "Index";

}

Da de alta una cerveza:

 

<form method="POST">

    Nombre:

    @Html.TextBoxFor(m=>m.Name)

    <br />

    Categoría:

    @Html.TextBoxFor(m=>m.Category)

    <br />

    <input type="submit" value="Enviar!"/>

</form>

Automáticamente los controles del formulario recuperan su estado automáticamente. ¡Hey! El que venía de Webforms… sigues ahí? 😉

Pregunta: Y como muestro que campo es el que ha producido el error?

Jejejeee… muy buena pregunta. Hemos redirigido de nuevo al usuario porque se ha dejado de introducir el nombre de la cerveza, pero estaría bien que le informáramos de ello. Antes he dicho que el código del controlador no era muy correcto. Y no lo es porque no usa un objeto de ASP.NET MVC llamado ModelState. El ModelState guarda entre otras cosas todos los errores que se producen al validar los datos por parte del controlador.

Así que lo suyo es introducir el error en el ModelState cuando lo detectamos:

[HttpPost]

public ActionResult Index(BeerModel beer)

{

    if (string.IsNullOrEmpty(beer.Name))

    {

        ModelState.AddModelError("Name", "¿Donde has visto una cerveza sin nombre?");

        return View(beer);

    }

    // procesar alta

    return null;

}

El método AddModelError añade un error a la propiedad indicada (el segundo parámetro es el mensaje de error). Ahora si ejecutamos el proyecto y envías el formulario dejando el campo nombre vacío, el código HTML generado de vuelta será algo como:

<form method="POST">

    Nombre:

    <input class="input-validation-error" id="Name" name="Name" type="text" value="" />

    <br />

    Categoría:

    <input id="Category" name="Category" type="text" value="sdsd" />

    <br />

    <input type="submit" value="Enviar!"/>

</form>

Fíjate que el <input> generado para tener el nombre de la cerveza ahora incluye una clase llamada “input-validation-error”. Esta clase la añade el helper cuando ve que hay un error asociado en el ModelState.

Luego ya por CSS es cosa tuya personalizar esta clase para que haga algo útil (p. ej. mostrar el campo en rojo). Lo importante es que el helper la añade automáticamente sin que tu tengas que hacer nada.

Ahora imagínate sin los helpers… para un formulario con 20 campos, tener que consultar para cada uno si existe un error en el ModelState… buf, sería para morirse.

Y es por esto, básicamente, que debemos utilizar los helpers!

Un saludo!

PD: Ojo, hay mejores maneras de validar la entrada de datos en ASP.NET MVC. Basta decir que el código de validación no debería estar en el controlador y que ASP.NET MVC ofrece dos mecanismos built-in que son DataAnnotations y la interfaz IValidatableObject. No hemos visto nada de esto porque no es el objetivo de este post.

ASP.NET MVC: Autocompletado con enums

La verdad es que el tema de los enums y ASP.NET MVC da para hablar bastante (yo mismo hice un post hace ni mucho). Pero hace algunos días mi buen amigo y a veces rival, Marc Rubiño publicó en su blog un interesante artículo sobre como crear combos que mostrasen valores de enums.

En este post voy a mostrar una técnica parecida, pero a través de las data list, un concepto nuevo de HTML5 que como pasa muchas veces está recibiendo menos atención de la que merece. Y es que las data list nos dan una manera fácil y sencilla de tener cajas de texto que se autocompleten.

Qué son las data lists de HTML5?

Bueno, pues como ya debes deducir de su nombre las data list no es nada más que la posibilidad de definir un concepto nuevo, que es esto: una lista de datos.

Una lista de datos se usa como fuente de datos para un control que pueda tener una y lo bueno es que el viejo y a veces injustamente denostado <input type=”text”> entran en esta categoría. Honestamente no entiendo como el <select> no tiene soporte para data lists. Quiero suponer que hay alguna explicación, pero la verdad no la encuentro. Bueno, da igual… ¡al tajo!

La definición de una data list es muy simple:

<datalist id="sexo">

    <option value="N">Nada</option>

    <option value="P">Poco</option>

    <option value="M">Mucho</option>

    <option value="D">Demasiado</option>

</datalist>

Ahora en HTML5 podemos usar el atributo list de la etiqueta <input> para asociar a un <input> (p. ej. una caja de texto) una lista de valores:

<input type="text" id="txtSexo" list="sexo" placeholder="Sexo" autocomplete="on" />

Básicamente tan solo se trata de usar el atributo list con el valor del id de la data list a usar.

El atributo id es requerido por Chrome (si no se aplica al <input> Chrome ignora el atributo list). Este es el resultado en Chrome, Opera e IE10:

image

Podemos ver las diferencias entre navegadores:

  • Chrome nos muestra el value a la izquierda y la descripción a la derecha
  • Opera tan solo nos muestra el value
  • IE10 tan solo nos muestra la descripción

Sin duda el mejor mecanismo, en mi opinión, es el de Chrome, luego el de Opera y por último el de IE10. No he podido probar FF ni Safari por no tenerlos a mano en el momento de escribir el post.

Igual te sorprende que prefiera que el desplegable muestre N, P, M y D como hace Opera en lugar de Nada, Poco,… que muestra IE10. Eso es porque el valor que se debe teclear en el textbox es el de value. El valor de value es el que se almacena en el textbox, y por lo tanto es el que debe teclearse (a diferencia de la <select> que permite mostrar un valor y mandar otro el <input type=”file” /> no tiene esta opción).

Ten presente siempre que estamos usando un <input type=”text” /> por lo que el usuario puede entrar lo que quiera. No es como una <select> donde debe elegir un elemento de los que le indiquemos. Así pues los elementos de la data list son ¡más una sugerencia que una obligación!

Un ejemplo.

Veamos como podríamos usar esto con enums y MVC…

Lo primero es definirnos un enum:

    public enum Beers

    {

        Estrella_Damm,

        Voll_Damm,

        Moritz,

        Epidor,

        Mahou,

        Cruzcampo

    }

Ahora vamos a extender la clase HtmlHelper con un método de extensión nuevo que llamaremos TextBoxEnumerableFor:

    public static class HtmlHelperEnumExtensions

    {

        public static IHtmlString TextBoxEnumerableFor<TModel, TProperty>(this HtmlHelper<TModel> helper, Type enumerationType, Expression<Func<TModel, TProperty>> expression)

        {

            var httpContext = helper.ViewContext.HttpContext;

            var html = new StringBuilder();

            if (httpContext.Items[enumerationType.FullName] == null)

            {

 

                httpContext.Items.Add(enumerationType.FullName, GenerateDataList(enumerationType, html));

            }

            var textbox = helper.TextBoxFor(expression, new {list = enumerationType.FullName});

            html.AppendLine(textbox.ToString());

            return MvcHtmlString.Create(html.ToString());

        }

 

        private static string GenerateDataList(Type enumerationType, StringBuilder html)

        {

            var id = enumerationType.FullName;

            html.AppendFormat(@"<datalist id=""{0}"">", id);

            html.AppendLine();

            foreach (var item in Enum.GetNames(enumerationType))

            {

                html.AppendFormat(@"<option>{0}</option>", item.Replace(‘_’, ‘ ‘));

                html.AppendLine();

            }

            html.AppendLine("</datalist>");

 

            return id;

        }

    }

Aspectos a comentar de este código:

  1. Uso HttpContext.Items para “hacer un seguimiento” de si ya se ha creado una datalist. Esto es para no definir en el mismo HTML dos veces la misma datalist si se generan dos (o más) cajas de texto vinculadas al mismo enum.
  2. Sustituyo los guiones bajos (_) por un espacio. Eso debería tenerse en cuenta luego al recibir los datos.

Este código es muy sencillo y realmente debería sobrecargarse el método para dar más opciones (p. ej. especificar atributos html adicionales que ahora no es posible). Pero como ejemplo, creo que sirve.

Su uso es muy sencillo:

@using MvcApplication1

@using MvcApplication1.Models

@model MvcApplication1.Models.BeerModel

 

@{

    ViewBag.Title = "Index";

}

Entra el nombre de tu cerveza preferida aquí:

 

@Html.TextBoxEnumerableFor(typeof(Beers), m=>m.BeerName)

BeerName es una propiedad (string) del modelo BeerModel. Y este es el HTML generado:

Entra el nombre de tu cerveza preferida aquí:

<datalist id="MvcApplication1.Models.Beers">
<option>Estrella Damm</option>
<option>Voll Damm</option>
<option>Moritz</option>
<option>Epidor</option>
<option>Mahou</option>
<option>Cruzcampo</option>
</datalist>
<input id="BeerName" 
    list="MvcApplication1.Models.Beers" 
    name="BeerName" 
    type="text" value="" />

No generamos atributo value, en este caso dicho atributo toma el valor del propio texto del option.

Y un pantallazo de como se ve en Opera:

image

Puedes ver como se usa los elementos del enum para mostrar una sugerencia de autocompletado del textbox. Pero recuerda: el usuario puede entrar el valor que quiera.

Como digo, el código se puede mejorar mucho, pero como idea y ejemplo creo que es suficiente.

Un saludo!

Usando las herramientas para Git de VS2012

Muy buenas! En este post voy a contar (o al menos intentarlo) como usar las herramientas de Git para VS2012 y trabajar con un repositorio Git instalado en TFS Services.

Nota: Este post está muy orientado a gente que viene de TFS, está acostumbrada a TFS y se siente un poco “perdida” con esto de Git. No pretende ser, ni mucho menos, un tutorial de Git.

Requerimientos previos

Debes tener instalado VS2012 y al menos el Update 2. Ve a la página de Updates de VS2012 para descargarte el último update (en la actualidad es el 3).

Una vez tengas el VS2012 actualizado, debes instalarte las herramientas para Git de VS2012, que te puedes descargar desde el gestor de extensiones de VS2012 o bien desde la propia página de las Visual Studio Tools for Git.

Finalmente debes tener una cuenta de TFS Services, que te puedes abrir en http://tfs.visualstudio.com. Abrir una cuenta es totalmente gratuito y te da acceso a un TFS listo para 5 usuarios de forma totalmente gratuita.

Antes que nada: diferencias entre Git y TFS clásico

El control de código fuente clásico de TFS es lo que se conoce como un CVS (Concurrent Version System), mientras que Git es un DCVS (Distributed Concurrent Version System). En TFS está claro quien es el servidor: te connectas a un servidor TFS, te bajas código de él y subes código en él.

Con Git, cada máquina es también un repositorio de código fuente. Vas a tener tu propio repositorio de código fuente en local. No tendrás solo “la última versión” si no todo el control de código fuente. Esto hace que operaciones como Branch o Merge sean mega rápidas (y puedas hacerlas offline).

De hecho, no hay en Git, un flujo que obligue a que haya un “servidor de Git centralizado”: puedes tener varios repositorios remotos con los cuales te sincronizas.

De todos modos, dado que es bastante habitual un flujo donde haya un servidor Git “central”, este es el que veremos en este post. En nuestro caso este “servidor git central” será TFS Services con el control de código fuente Git habilitado.

En Git no hay check-in o check-out, ni tampoco lock. En su lugar las operaciones básicas que tenemos son:

  1. Commit: Pasa cambios de tu working folder a tu repositorio git local pero NO al remoto.
  2. Push: Pasa cambios de tu repositorio git local al remoto
  3. Pull: Pasa cambios del repositorio git remoto al local y a la working folder.

Ahora sí…. empecemos.

Primer paso: Crear el team project

Para tener un repositorio Git en TFS debemos tener un team project. Así que dale al botón “New Team Project” desde la página principal de TFS Services y en las propiedades del proyecto asegúrate de seleccionar Git como gestor de código fuente:

image

Una vez tengas el team project creado estás listo para empezar a trabajar con Git.

Añadir la solución al control de código duente.

Para ello desde el solution explorer seleccionamos la opción “Add solution to Source control”. VS2012 nos preguntará si deseamos usar Git o bien el control de código fuente de TFS. Hasta ahora simplemente habíamos creado un repositorio de Git, pero no le habíamos indicado a VS2012 que íbamos a usarlo en nuestra solución:

image

Asegúrate de marcar la opción de Git y dale a Ok. Ahora si que estamos listos para hacer commits.

Al marcar la opción de Git, VS2012 nos crea un repositorio de Git local que está en el mismo directorio que nuestra solución. De hecho si abres con el explorador de archivos la carpeta de la solución, verás una carpeta oculta, llamada .git que es la que contiene el repositorio local.

Commit

Vamos a guardar nuestro código o en el repositorio de Git local. A diferencia del TFS clásico en Git no se protegen o desprotegen ficheros, así que no hay opciones de check-in o check-out. Básicamente Git mira todos los cambios que se producen entre la working folder y el repositorio Git local y son los ficheros que tengan cambios (de cualquier tipo) los que podemos “commitear” y enviar al repositorio Git local. Para hacerlo vete a la página inicial del team explorer pulsando el icono de home:

image

 

Ahora pulsa sobre de “Changes”. Con eso VS2012 te mostrará todos los archivos de la working folder que tengan algún cambio respecto al repositorio de Git local (es decir que se hayan añadido, borrado o modificado).

image

 

Los ficheros que aparecen en Included Changes son los ficheros que se incluirán en el commit. Los que aparecen en Excluded Changes son los que no se incluirán en el commit. Puedes pasar ficheros de un sitio a otro arrastrándolos. Una vez tengas el commit listo, debes añadir un mensaje de commit y ya podrás darle al botón “Commit”.

Una vez lo hayas hecho VS2012 te mostrará un mensaje diciendo que el commit ha sido correcto y continuarás en la misma página de commits, para seguir haciendo commits si quedan ficheros con cambios (es decir, ficheros que antes habías colocado en “Excluded Changes”). Eso es así porque con Git es costumbre hacer muchos commits y muy pequeños.

Push

Si ahora vuelves a la página inicial del Team Explorer (con el icono de la casita) y pulsas sobre el enlace “Commits” te aparecerá una ventana como la que sigue:

image

VS2012 nos pide que repositorio Git va a ser el remoto. Este debe estar vacío. Recuerda que antes hemos creado uno en TFS Services, pero todavía no habíamos dicho en ningún sitio que queríamos usarlo. Ahora ha llegado el momento. Copia la URL del repositorio Git remoto (la puedes encontrar en la página de TFS Services si vas al team project creado y al apartado “Code”) y dale al botón “Publish”. Con este proceso asociarás tu repositorio de Git local con el repositorio de Git remoto y además harás un push de los commits pendientes (es decir pasarás el código de tu repositorio Git local al remoto).

Cuando haya terminado te aparecerá una página parecida a:

image

¡Felicidades! Ya tienes un repositorio de Git local y remoto enlazado a tu solución.

Veamos ahora como añadir un cambio.

Para ello modifica un par de archivos del proyecto. Puedes hacerlo directamente: recuerda, olvida el concepto de check-out (desproteger). Vete a la página de Changes del team explorer (ve a la página principal a través del icono de casa y luego pulsa en Changes) y te aparecerán los dos archivos modificados:

image 

Arrastra uno de los dos a Excluded Changes, añade un mensaje de commit y dale a commit. Te aparecerá una pantalla como la siguiente:

image

Ahora arrastra el otro archivo a Included Changes, pon otro comentario y dale a commit de nuevo. Con esto has generado dos commits en el repositorio git local.

 

Si ahora te vas a la página de Push (vete a la inicial con el icono de la casa) y dale Commits (debajo de Changes) verás algo parecido a:

image

En outgoing commits te aparecerán los commits que has hecho en el repositorio local y que no están en el repositorio remoto. Si le das a “Push” subirás los cambios (los commits) desde el repositorio local al remoto.

Obtener los cambios de otro usuario

Vale, a no ser que estés tu solo en un proyecto (y sí, si estás tu solo en un proyecto te recomiendo encarecidamente también usar control de código fuente) en algún momento deberás incorporar los cambios que hay en el repositorio remoto al tu repositorio local.

Vete a la página de commits del team explorer (ya sabes, desde la home le das al enlace de Commits):

image

Debajo de “Incoming Commits” hay dos opciones:

  1. Fetch: Te permite obtener el listado de cambios que tienes pendientes de integrar. Es decir, el listado de commits que están en el repositorio remoto pero NO en tu repositorio local.
  2. Pull: Se trae los commits desde el repositorio remoto al repositorio local y actualiza la working folder.

Si le das a Pull el proceso es automático (Git se trae los commits del repositorio remoto al local y desde el local hacia la working folder resolviendo los conflictos si hiciese falta).

Si le das a Fetch, te aparecerán los commits que existen en el repositorio remoto, pero están pendientes de ser integrados a tu repositorio local:

image

Viendo los detalles de un commit

A veces, antes de integrar el código del repositorio remoto (es decir, antes de hacer pull) te interesa ver como este commit puede afectar a tu trabajo. Para ello, una vez has obtenidos los commits pendientes de integrar (es decir, una vez has hecho fetch) puedes ver los detalles de un commit pulsando con el botón derecho sobre él y seleccionando la opción “View Commit Details”:

image

Esto te permite ver los ficheros que conforman el commit, ver su contenido y compararlo con la versión anterior de este mismo fichero.

Resolviendo conflictos.

Imagina la situación en que tienes modificado un fichero en tu carpeta local pero NO has hecho commit de este fichero en tu repositorio git local. Imagina que haces pull y hay un commit que tiene este fichero modificado.

Cuando esto ocurrer recibirás el siguiente mensaje de error:

image

Si te aparece este mensaje debes primero hacer commit de tus cambios pendientes en tu repositorio Git local. Una vez hayas hecho el commit (no es necesario que hagas el push hacia el repositorio remoto) ya puedes volver a hacer el pull.

Entonces, Git intentará hacer merge automático entre el contenido de tu repositorio local y los commits que vienen del repositorio remoto). Pero puede que el merge automático no sea posible:

image

Nota: En esta pantalla “Incoming Commits” son los dos commits que estoy intentando integrar y en “Outgoing Commits” hay el commit que he hecho para colocar los cambios de la carpeta local hacia mi repositorio remoto. Como solo he hecho commit pero no push, por eso me aparece aquí.

Ahora pulsas sobre el enlace “Resolve the conflicts” y te aparece la página de resolución de conflictos:

image

Pulsando sobre cada uno de los archivos en “Conflicts” tendremos las opciones clásicas de tomar el archivo local (el de nuestro repositorio Git local), el remoto (el del repositorio Git remoto) o combinarlos:

image

Si le damos a “Merge” nos aparecerá la clásica ventana de Merge:

image

 

 

 

Una vez hayamos resuelto todos los conflictos, aceptamos el merge y repetimos la operación por todos los archivos. Al final llegaremos a una página parecida a:

image

Donde no habrá nada en “Conflicts” y todo estará en “Resolved”. Ahora tenemos estos cambios en nuestra working folder, pero NO en nuestro repositorio local de Git (y mucho menos en el remoto obviamente). Así que le damos a “Commit Merge” para pasar estos cambios de nuestra working folder a nuestro repositorio Gi local. Al hacerlo iréis a la página de Changes con el commit listo para ser enviado al repositorio local:

image

Fíjate que el commit que se me ha creado incluye tanto el archivo Program.cs (que es el que tenía conflictos) como el archivo App.config que pertenecía al otro “incoming commit”.

Una vez le deis a commit ya lo tendréis en el repositorio git local.

Finalmente os vais a la página de “Commits” y vereis los commits pendientes de integrar hacia el repositorio remoto (es decir, pendientes de hacer push):

image

Ahora puedes hacer push sin ningún problema y tu código integrado ya está en el reposiorio Git remoto.

Conclusiones finales

Cuando se trabaja en Git, no se trabaja a nivel de fichero, como con el control de código fuente clásico de TFS, si no que trabajamos a nivel de commit.

Debido a esto, haz commits pequeños.

Recuerda siempre el concepto de que hay un repositorio Git local y otro de remoto. A diferencia del control de código fuente clásico de TFS donde solo hay el remoto.

Si intentas hacer pull y tienes algún fichero modificado en tu working folder (del cual no has hecho commit hacia tu repositorio local) que está incluído en los commits que vas a integrar desde el servidor remoto, te dará error y deberás hacer primero un commit de este fichero (es decir pasarlo a tu repositorio local).

Espero que con esto os quede un poco más claro como funciona Git usando esta extensión de VS2012.

Finalmente: esta extensión no es, ni de lejos, el mejor cliente Git para Windows. Para flujos sencillos funciona bien, ya que está integrada en Visual Studio, pero para flujos de trabajo más complejos se queda corta. En estos casos es mejor usar un cliente de Git específico como SoureTree.

Saludos!

[Editado 20/07/2013] – Corregido un error (mirar comentario de Enrique Ortuño).

 

Katana: Cortando el framework

Si eres desarrollador en tecnologías Microsoft y especialmente desarrollador web, acúerdate de esas dos palabras: Proyecto Katana. Este proyecto representa el futuro a corto plazo de todas las tecnologías de desarrollo web de Microsoft y, no me cabe duda de ello, el futuro a medio plazo del propio .NET.

No dejes que esa introducción te confunda: Katana no es una nueva tecnología, ni una nueva plataforma, ni un lenguaje nuevo, ni tan siquiera realmente una API nueva. Es simplemente un cambio de filosofía. Es como Microsoft entiende que debe ser la evolución de ASP.NET para poder seguir dando respuestas al, cada vez más, cambiante mundo del desarrollo de aplicaciones web. Katana se circumscribe sólo a ASP.NET, pero no me cabe duda de que al final todo .NET irá en la misma dirección.

¿Y qué dirección es esa? Para saber donde vamos hemos de saber de donde venimos…

Hace 12 años (año arriba, año abajo)…

Microsoft sacó .NET y con él una “nueva versión” de ASP. Conocida primero como ASP+, luego ASPX para adoptar la terminología de ASP.NET al final. La intención de MS con ASP.NET era doble. Por un lado que los desarrolladores de “toda la vida” en tecnologías web de Microsoft (que en un 95% usaban ASP) empezaran a usar la nueva versión. Pero también que esa nueva versión pudiese ser atractiva y usable rápidamente por la gran mayoría de los desarrolladores que usaban tecnologías Microsoft, lo que era equivalente a decir VB6 en aquella época.

Es este segundo objetivo el que hace que Microsoft apueste por el modelo de Webforms para que “desarrollar para web sea igual a hacerlo para escritorio”. No olvides eso: el assembly principal de ASP.NET (System.Web.dll) está totalmente acoplado a IIS.

ASP.NET se integró dentro de lo que se pasó a llamar (y se llama) .NET Framework: Un framework monolítico del que ASP.NET era tan solo una parte y que se actualizaba cada cierto tiempo (entre 1 y 2 años y pico), básicamente cada vez que había una nueva versión de Visual Studio.

En medio de todo este tiempo varios cambios en el paradigma del desarrollo web a los que ASP.NET no podía responder con prontitud por su modelo de distribución y actualización, hicieron que alguien en Redmond se diese cuenta de que alguna cosa se tenía que hacer.

Hace 4 años (año arriba, año abajo)…

Veía la luz ASP.NET MVC. Era un nuevo framework, basado en ASP.NET que venía a complementar a Webforms. Desde este momento había dos APIs para desarrollar aplicaciones web en tecnologías Microsoft: Webforms y ASP.NET MVC. Ambas funcionaban bajo toda la plataforma de ASP.NET (y el .NET Framework subyacente que era el 3.5SP1 por aquel entonces).

Pero la novedad principal de ASP.NET MVC no era que fuese una nueva API si no el hecho de que se distribuía independientemente del .NET Framework. Era la primera vez que una API de Microsoft veía la luz fuera del “paraguas” aglutinador del .NET Framework. Esto suponía la posibilidad para Microsoft de evolucionar ASP.NET MVC mucho más ágilmente. Para que te hagas una idea: junto con el .NET Framework 4, salió ASP.NET MVC2. Después salieron MVC3 y MVC4 sin que hubiese ninguna versión más del Framework, hasta que salió Visual Studio 2012 y el .NET Framework 4.5.

ASP.NET MVC marcaba una nueva tendencia (que luego ha seguido Entity Framework) en donde se intenta huir de un framework monolítico para pasar a otro “framework” formado por varios módulos que se actualizan de forma separada.

Pero recuerda: ASP.NET MVC seguía funcionando bajo System.Web.dll y por lo tanto estaba “atada” a IIS (y también al .NET Framework, ya que System.Web.dll forma parte del .NET Framework).

Hace tres año (año arriba, año abajo)…

La aparición de ASP.NET MVC3 supuso otro hito importante en esta historia, no por MVC3 en si mismo, si no por un componente que le acompañaba: NuPack.

NuPack, luego llamado NuGet, era una extensión de Visual Studio que permitía (permite) instalar paquetes de forma muy sencilla. Estos paquetes pueden ser cualquier cosa, usualmente librerías de terceros (pero también templates de proyectos, etc). Era como tener PEAR pero en .NET.

No sé si cuando salió alguien fue consciente de la importancia que adquiriría NuGet para el desarrollo en tecnologías Microsoft…

Hace un año (año arriba, año abajo)…

Salió ASP.NET MVC4 y el hito final de esta pequeña historia. No por MVC4 si no por un componente que le acompañaba (otra vez) y que parecía que formaba parte de MVC4 cuando realmente poco tenía que ver con ella: WebApi.

La gran novedad de WebApi es que no tenía ninguna dependencia contra System.Web.dll. Es decir, WebApi podía evolucionar de forma completamente independiente de ASP.NET (eso no era del todo cierto en ASP.NET MVC aunque se distribuyera via NuGet) y además al no depender de System.Web.dll implicaba que no se estaba atado a IIS por lo que se podía hospedar WebApi en otros procesos (p. ej. una aplicación de línea de comandos).

La idea de WebApi es pues la idea final: una parte del framework sin dependencias externas, que evoluciona a su propio ritmo y que se distribuye automáticamente via NuGet.

Hoy (día arriba, día abajo)…

El proyecto Katana es quien ha tomado el relevo de esta filosofía que se inaugura con WebApi. La idea del proyecto Katana es tener un conjunto de componentes cada uno de los cuales pueda evolucionar independientemente y por supuesto sin ninguna dependencia a System.Web.dll.

Katana es un conjunto de componentes, implementados por Microsoft y que son componentes OWIN. Estos componentes van desde hosts, servidores hasta componentes funcionales (p. ej. hay una versión de SignalR “katanizada” o la propia WebApi). Recuerda la clave: cada componente podrá evolucionar a su propio ritmo y podrá ser sustuído por otro en cualquier momento. Olvídate de decir que “esta web es ASP.NET 3.5” y que todo el mundo sepa que es esto. Tu web serà una combinación de distintos módulos, cada uno con su propia versión. Desde NuGet seleccionarás cual de los módulos quieres usar (p.ej. un nuevo módulo de autenticación).

Y a futuro… extiende esta visión a todo el framework. Cierra los ojos e imagina que ya no hay un .NET Framework 6 ó 7 o el que toque y que en su lugar tu proyecto usará System.Collections en una versión y también System.Net y el entity framework que toque y nada más. Incluso, porque no, seleccionar que CLR (o CLRs) quieres atacar. Yo al menos lo veo así (aunque desde que predije el fracaso del iPad tengo claro que como pitoniso no me ganaré la vida).

OWIN

Antes he mencionado que Katana es (será) básicamente un conjunto de componentes OWIN y me he quedado tan ancho. Pero… ¿qué es básicamente OWIN? Pues inspirándose de nuevo en la comunidad de Ruby (en concreto en Rack) la comunidad de .NET ha creado una abstracción entre el servidor web y los componentes del framework que se use. La idea es que:

  1. Se puedan (y usar) crear nuevos componentes de forma sencilla y fácil
  2. Sea más sencillo portar aplicaciones entre hosts.

No entraré en detalles sobre OWIN ahora (lo dejo para un post siguiente) pero básicamente sus puntos clave son:

  1. El servidor Web OWIN es el encargado de rellenar los datos OWIN (environment según la especificación) a partir de la petición. A la práctica estos datos son un diccionario (IDictionary<string,object>). Las cadenas usadas como claves están definidas en la propia especificación de OWIN.
  2. Los componentes deben implementar el application delegate (AppFunc). Básicamente la idea es que los componentes reciben el diccionario anterior y devuelven una Task con la definición de la tarea a realizar.

Estos dos puntos parecen una chorrada, pero quédate con un par de ideas al respecto:

  • OWIN está diseñado con visos de ser asíncrono (de ahí que los componentes devuelvan un Task). La idea es parecida a la de nodejs: procesar asíncronamente aumenta el rendimiento en aquellas peticiones que básicamente dependen de E/S que son la mayoría.
  • Los componentes OWIN son encadenables de forma sencilla.

Conclusión

El modelo de un framework monolítico podía ser válido hace, pongamos, siete años, pero cada vez es necesario poder evolucionar más rápidamente.

Evolución rápida implica que sea al margen del .NET Framework. ASP.NET MVC abrió el camino y el proyecto Katana es la culminación de esa idea: tener todas las APIs y componentes de ASP.NET (y algunos más) pero sin dependencias contra System.Web.dll (y por lo tanto con el .NET Framework y con IIS). Para la implementación de Katana, Microsoft se ha apoyado en OWIN, una iniciativa de la comunidad .NET, que define una interfaz entre servidores web y frameworks de desarrollo web modulares.

En próximos posts iremos ampliando la información sobre OWIN y Katana.

Saludos!

Novedades de IE11: SPDY

Como ya es habitual una nueva versión de Windows (en este caso la 8.1) viene acompañado de un nuevo IE11. Y como siempre IE11 viene con varias novedades siendo quizá las dos más destacadas el soporte para WebGL (gráficos 3D) y el tema de este post, el soporte del protocolo SPDY.

Este será un post ligeramente distinto a los habituales del blog porque no hablaré de nada de desarrollo 🙂

Un protocolo de Nivel 7

Niveles OSI (sacado de Wikipedia)

El nivel OSI de un protocolo de comunicaciones determina “cuan alejado del medio físico” está este protocolo. Apareció a mediados de los 80 y determina 7 capas por las cuales los datos deben pasar para llegar desde el medio físico (p. ej. un cable) hacia el usuario final.

Para una descripción detallada de cada nivel OSI podéis consultar este artículo de la microsoft. Por curiosidad os puedo comentar que el protocolo IP (tanto v4 como v6) está situado en el nivel 3, TCP está situado en el nivel 4, SSL (o su variante más moderna TLS) está situado en el nivel 5 y que HTTP está situado en el nivel 7. Por cierto, aprovecho para comentar que TCP e IP son dos protocolos, así que cuando decimos que internet “usa el protocolo TCP/IP” realmente queremos decir que usa “el protocolo TCP funcionando bajo el protocolo IP”. Pero bajo IP pueden funcionar otros protocolos (como UDP p. ej.) o al revés TCP puede funcionar bajo otros protocolos de nivel 3 como IPX.

Pues bien, SPDY (pronunciado speedy) es un nuevo protocolo, situado en la capa 7 de OSI que viene a reemplazar HTTP.

La frase en negrita del párrafo anterior es la frase sensacionalista, porque como ahora verás el hecho de que SPDY sustituya a HTTP no te afecta para nada como desarrollador de aplicaciones web. Dejemos esto bien claro a partir de ya, ¿ok? SPDY tiene impacto cero para los desarrolladores de aplicaciones web pues toda la semántica de HTTP se mantiene. De hecho familiarmente hay quien se refiere a SPDY como HTTP 2.0 pero eso no es ni mucho menos una denominación oficial y además se presta a confusión puesto que hay una especificación (en borrador) de HTTP 2.0 (que además se basa en SPDY pero eso es otra historia).

32x, 3.1Ghz, 12 Mpx…

Los seres humanos somos simplistas por naturaleza, generalmente nos agobia trabajar con demasiados datos y deseamos reducirlo todo a un solo indicador. Por supuesto los que se encargan de venderte cosas ya escogerán el indicador que les vaya mejor a ellos para venderte el producto con independencia de que dicho indicador sea el que mejor refleje la calidad global del producto. En los tiempos de las unidades de CD los fabricantes se esmeraban en tener un valor de “equis” lo más alto posible. Y así se asoció que una unidad de 50x era mucho mejor que una de 32x aunque esto era la velocidad punta y no la sostenida que sería mucho más importante (pero menos impactante) para dar cuenta del rendimiento de la unidad. Pero es que incluso la velocidad sostenida por si sola tampoco da toda la información. Algo parecido ocurre con los ordenadores que se suelen vender publicitando más los Ghz del procesador aunque este dato por si solo es totalmente irrelevante. O con las cámaras de fotos y los megapíxeles (solo hay que ver el revuelo que se está armando con este futuro nokia de 41 de megapíxeles). Los megapíxeles importan sí, pero es mucho más importante la calidad de las lentes, el estabilizador y, a partir de cierto número de Mpxs, el tamaño físico del CCD. Pero analizar todo esto son demasiados datos y como digo somos simplistas…

Esto mismo aplica a tu conexión de internet. Y es que… es mejor una conexión de 20 Megas que una de 15 no? Pues no, necesariamente. No voy a escribir el porque una conexión de 20 Megas puede ser peor que una de 15 (asumindo que ambas funcionen al 100% de su velocidad) porque ya lo hizo el fenómeno de José Manuel Alarcón en su blog. Léete el post porque es un must read.

Volvamos a SPDY. Seguramente la pregunta que nos podríamos hacer sería… ¿por qué un nuevo protocolo para reemplazar HTTP? HTTP funciona y es la base de Internet que es probablemente una de las revoluciones más importantes de la (corta) historia de la humanidad. ¿Entonces?

Hoy en día las páginas web se han echo muy complejas, para tener números actualizados acabo de probar varias páginas para contar cuantas peticiones http se realizan para cargarlas todas (el javascript, las imágenes, css, videos, etc). No quiero fijarme en el tamaño de los elementos transferidos, solo en cuantas peticiones http debe enviar el navegador. Pues bien, esos son algunos números:

  • facebook.com (con usuario logado): 153 peticiones http
  • marca.com: 455 peticiones http
  • twitter.com (con usuaruo logado): 44 peticiones http
  • google.com: 16 peticiones que se disparan a las 29 al empezar a teclear una búsqueda.

(Todas estas mediciones son usando las webs de producción y sin cache).

Como desarrollador web probablemente ya sabes lo importante que es minimizar el número de peticiones HTTP (usando CSS sprites, imágenes en data-uris, compactación de js, etc). Los navegadores también ayudan y suelen usar hasta 6 (antes eran 2) conexiones simultáneas.

Pero tenemos dos problemas ahí… veamos ambos.

Problema 1: Crear una conexión TCP es “lento”.

La “lentitud” de crear una conexión TCP viene dada porque usa un sistema conocido como 3-way-handshake para establecer dicha conexión:

El cliente primero debe enviar un paquete SYN, el servidor debe responder con un SYN ACK y finalmente el cliente debe responder con un ACK.

En este punto la conexión TCP está establecida y se pueden empezar a mandar datos.

 

Es decir para poder empezar a mandar datos hay una comunicación del cliente al servidor, una del servidor al cliente y otra del cliente al servidor. ¿Y cuando digo lento cuan lento quiero decir? Pues con un buen ADSL (y no me refiero a las megas) pues establecer una conexión TCP te puede costar de 20 a 30ms. Pero ojo… en un móvil estos tiempos se pueden disparar hasta los 300ms por cada conexión TCP.

En resumen: El ancho de banda NO es importante. Lo verdaderamente importante es la latencia (¿te ha dicho tu proveedor de adsl la latencia de tu conexión? ¿No, verdad?).

En HTTP1.0 cada petición HTTP abre y cierra una conexión TCP.

Solución: HTTP1.1 al rescate

En HTTP1.1 es posible usar conexiones keep-alive lo que básicamente significa que bajo una misma conexión TCP se pueden enviar varias peticiones HTTP solucionando así el problema anterior.

Actualmente (casi) todos los servidores y todos los navegadores soportan HTTP1.1 así que… problema arreglado 🙂

Problema #2: Todo el proceso es síncrono

Vale… gracias a las conexiones keep-alive de HTTP1.1 hemos “eliminado” el problema de la “lentitud” de abrir conexiones TCP. Pero nos queda el problema fundamental: Todo el proceso es síncrono:

HTTP sin pipeline

A pesar de que podemos utilizar una sola conexión TCP para enviar los datos, el navegador no puede enviar la segunda petición HTTP hasta haber recibido la primera y no puede enviar la tercera hasta haber recibio la segunda.

Si la segunda petición tarda mucho (por la razón que sea y en la dirección que sea) el navegador estará esperando y esperando hasta recibir la respuesta sin poder enviar la tercera petición.

 

Intento de solución: HTTP1.1 pipelining

HTTP1.1 además de conexiones keep-alive ofrecía un modo pipeline que básicamente viene a romper la sincronidad. Permite que el navegador envíe todas las peticiones de golpe y espere por las respuestas:

Pipelining en HTTP1.1

Con esto el problema #2 parece solucionado ¿no? El navegador puede enviar todas las peticiones y luego esperar a que lleguen todas las respuestas de golpe.

Pues no.

Primero hay un motivo práctico: Para que el pipeline de HTTP1.1 funcione es necesario que esté soportado por todos los dispositivos intermedios que hay entre el servidor y el funcione (o sea, los proxies) y hay algunos no lo soportan. Por esta razón HTTP1.1 pipelining apenas se usa (de hecho está soportado pero desactivado en casi todos los navegadores de la actualidad).

Pero incluso si HTTP1.1. pipelining se estuviese usando de forma masiva no es la solución ideal al problema. ¿Por qué? Pues porque HTTP obliga a una semántica FIFO en las peticiones. Es decir el navegador DEBE recibir la respuesta de la primera petición antes de recibir la respuesta de la segunda. Y DEBE recibir la respuesta de la segunda petición antes de la respuesta de la tercera. Y así sucesivamente.

Por lo tanto si hay una petición lenta todas las posteriores se verán retrasadas también, porque la respuesta a esta petición lenta debe ser enviada al navegador antes que las respuestas de las peticiones siguientes.

Solución: SPDY al rescate

Aquí es donde entra SPDY. Este protocolo, desarrollado inicialmente por Google, ofrece un pipelining real sobre una sola conexión TCP (además de otras mejoras) para de esta manera reducir los tiempos de latencia y espera.

Además SPDY añade más funcionalidades a HTTP (como server push) y toda la petición es comprimida (en HTTP se puede comprimir la respuesta pero no las cabeceras).

Y lo más importante de todo: SPDY no requiere ningún cambio en la infraestructura de red actual ni en las aplicaciones web desarrolladas. Insisto: a ti, como desarrollador web, que se use SPDY te es totalmente transparente. Recuerda: la semántica de HTTP (verbos, cabeceras, URLs) está totalmente mantenida. SPDY tan solo modifica en como se usa TCP por debajo (una sola conexión y pipelining real)

Para que se use SPDY tan solo es necesario que el servidor web y el navegador lo soporten. IE11 finalmente soporta SPDY y se une así a Firefox, Opera y obviamente Chrome. En Android la ultima versión de Chrome y Opera Mobile soportan SPDY. De Safari, la verdad no tengo ni idea. Y en el mundo de los servidores hay módulos SPDY para los principales servidores web. De IIS no tengo noticias, pero imagino que a partir de Windows 8.1 estará soportado (a nivel de http.sys supongo).

En fin, el soporte para SPDY de IE11 es una muy buena noticia que ayudará a que este protocolo se vaya extendiendo más y que todos tengamos una web (un poco) más rápida!

Como dije… un post diferente 😉

Saludos!

Backbone: El misterioso caso del sync que no se lanzaba

Muy buenas! Estos días he estado resolviendo un misterio que me sucedía con un proyecto utilizando Backbone.

En concreto, se supone que, a partir de la versión 1.0, cuando se guarda un modelo de Backbone al servidor (usando p. ej. save) si la operación tiene éxito, el modelo nos lanza el evento sync para informarnos, precisamente, del éxito de la operación.

Así, una secuencia típica de operaciones, se supone que es:

  1. El usuario hace click en un enlace, botón, o en cualquier elemento del DOM para que se “guarde” algo.
  2. La vista de Backbone recoge este evento y hace lo que tenga que hacer (validaciones, modificaciones de UI en cliente) para terminar modificando el modelo según los datos de la UI.
  3. Una vez el modelo está actualizado la vista llama a save() del modelo.
  4. Alguien (usualmente la propia vista) recoge el sync y hace lo que tenga que hacer (mostrar un mensaje, redirigir el usuario a otra acción del enrutador, etc).

Esa es la idea, sencilla ¿no? Llamaas al método save() del modelo y este te lanza el evento sync cuando el guardado en el servidor ha sido completado.

Todo muy bonito salvo que a mi no me iba. No se me lanzaba el evento sync, a pesar de que la llamada al servidor se completaba y no lanzaba ningún error.

Aquí un pequeño inciso para los que no conozcáis Backbone: el método save() del modelo lo que hace es una petición (usualmente POST) a una url (que se especifica al definir la “clase” del modelo) con los datos a guardar. La idea es que Backbone se integre muy fácilmente con una API estilo REST que tengamos. En mi caso, era una API hecha con WebApi. Pongo aquí el código más relevante del método de servidor invocado:

public void PostOne(TaskDto task)

{

    // Guardar datos…

}

Repito: El método se invocaba correctamente y el objeto TaskDto era guardado. De hecho en la ventana de Network de Chrome tenía lo siguiente:

image

Cuando ya estaba seguro (si es que se puede estarlo alguna vez) de que el error no estaba en mi código, empecé a fijarme en la respuesta enviada al navegador. ASP.NET WebApi devuelve código HTTP 204 (No Content) en los métodos que devuelven void. Tiene toda la lógica y es un uso coherente de los códigos HTTP, el 204 significa precisamente esto: Ha ido todo bien (es un 2xx) pero no hay datos adicionales al respecto.

Entonces me surgió la duda… ¿Y si Backbone no entiende el 204? Por supuesto esto sería un “error” de Backbone, pero lo probé. Mi forma de probarlo, muy pedrestre, fue “obligar” a WebApi a devolver algún resultado. En este caso entonces ya no se usa el 204 (no puede usarse el 204 si hay contenido en la respuesta) si no el más genérico 200.

Modifiqué el método de WebApi:

public string PostOne(TaskDto task)

{

    // Guardar datos…

        return "";

}

¡Genial! con eso ya tenía mi código de respuesta 200:

 image

Y…. ¡funcionó! El evento sync se me lanzaba tal y como se supone que debe ocurrir.

Luego pensé en otra cosa: “¿Y si no es el código 204 lo que BackBone no entiende si no una respuesta vacía?”… Para probar mi teoría modifiqué de nuevo el método PostOne:

public HttpResponseMessage PostOne(TaskDto task)

{

    // Guardar datos…

    return Request.CreateResponse(HttpStatusCode.OK);

}

Ahora estaba mandando el código 200 pero sin nada (ni una cadena vacía) en la respuesta. Pues bien… el evento sync no se lanzaba. Es decir, el problema NO era usar un código distinto a 200, el problema para Backbone era una respuesta sin cuerpo.

Así, que bueno… ya que Backbone parece insistir que quiere un cuerpo en  la respuesta, al final terminé con esta implementación:

public HttpResponseMessage PostOne(TaskDto task)

{

    // Guardar datos…

    return Request.CreateResponse(HttpStatusCode.Created, true);

}

El código Created és el 201, que mira, me parecía un poco más correcto que usar el genérico 200, ya que realmente esta API siempre añadía una tarea.

Y así, todos contentos: Backbone ya tiene su cuerpo en la respuesta y yo mi evento sync que se lanzaba.

Así que recuerda: A Backbone no le gustan las respuestas sin contenido cuando se guarda el modelo.

¡Saludos!

ASP.NET MVC – Tratando con enums.

En un proyecto ASP.NET MVC en el que estoy colaborando, surgió la necesidad de tratar con viewmodels que tenían propiedades cuyo tipo era un enum. Algo así como:

[Flags]

public enum TestEnum

{

    None = 0,

    One = 1,

    Two =2,

    Four =4

}

 

public class FooModel

{

    public TestEnum TestData { get; set; }

}

Los valores de TestEnum son combinables a nivel de bits (de ahí que esté decorado con [Flags], es decir el valor de la propiedad TestData puede ser cualquiera de los cuatro o bien una combinación (p. ej. One y Two).

ASP.NET MVC no gestiona, por defecto, bien estos casos. Imaginemos que tenemos el siguiente código en el controlador:

public ActionResult Index()

{

    var model = new FooModel();

    model.TestData = TestEnum.One | TestEnum.Two;

    return View(model);

}

Y el código correspondiente en la vista:

@using (Html.BeginForm())

{

    @Html.EditorFor(x => x.TestData)  

    <input type="submit" value="enviar" />

}

Lo que nos generará será un TextBox con los valores del enum (en este caso One y Two) separados por comas:

image

Es obvio que no es una buena manera que el usuario entre esos datos. Por su parte el enlazado funcionará correctamente (es decir en el método que recibe el POST se recibirá que la propiedad TestData vale TestEnum.One | TestEnum.Two, ya que el DefaultModelBinder sí que es capaz de tratar este caso

En el proyecto lo que se quería era que en lugar de mostrar este texto se mostraran tantas checkboxes como valores tiene el enum y que se pudiesen marcar una o varias.

Eso se consigue con relativa facilidad usando un EditorTemplate. En ASP.NET MVC los EditorTemplates (y sus equivalentes de visualización los DisplayTemplates) son vistas parciales que son renderizadas cuando se debe de editar o visualizar un valor de un tipo o propiedad en concreto. Los EditorTemplates se colocan por lo general en la carpeta Views/Shared/EditorTemplates (los Display Templates en la Views/Shared/DisplayTemplates).

Así pues creamos un Editor Template que nos cree tantas checkboxes como valores tiene el enum:

@model Enum

@{

    var modelType = @Model.GetType();

}

 

@foreach (var name in Enum.GetNames(modelType))

{

    var value = Convert.ToInt32(Enum.Parse(modelType, name));

    if (value != 0)

    {

        var isChecked = ((Convert.ToInt32(Model) & value) == value) ? "checked" : null;

        <input type="checkbox" name="@ViewData.TemplateInfo.HtmlFieldPrefix" value="@name" checked="@isChecked" />  @name<br />

    }

}

El código es relativamente sencillo y lo que hace es crear una checkbox por cada clave (valor) del enum y marcarla si es necesario (es decir, si un and a nivel a de bits entre el valor del enum y el valor de cada clave da distinto de cero). Hay una comprobación addicional para no renderizar una checkbox si el valor de la clave es 0 (en nuestro caso sería el None). Para usar este EditorTemplate (que yo he llamado FlagEnum.cshtml) una posibilidad es indicarle al viewmodel que lo use, decorando la propiedad con [UIHint]:

public class FooModel

{

    [UIHint("FlagEnum")]

    public TestEnum TestData { get; set; }

}

Dado que en nuestra vista ya usábamos EditorFor para generar el editor de la propiedad TestData, no es necesario ningún cambio más. El resultado es ahora más interesante:

image

Parece que hemos terminado, eh? Pues no… Aparece un problema. Si le damos a enviar tal cual, lo que ahora recibimos en el método que recibe los datos POST es:

image

¡Ahora no se nos enlaza bien el campo! Eso es debido a como funciona el Model Binder de ASP.NET MVC. La diferencia con antes (cuando había el textbox) es que antes teníamos un solo campo en la petición y ahora tenemos N (tantos como checkboxes marcadas). Si comparamos las peticiones enviadas, lo veremos mejor.

Esta es la petición que el navegador envía si NO usamos nuestro EditorTemplate:

POST http://localhost:19515/ HTTP/1.1
Accept: text/html
Referer: http://localhost:19515/
Origin: http://localhost:19515
Content-Type: application/x-www-form-urlencoded

TestData=One%2C+Two

Y esta otra la petición que se envía si usamos el EditorTemplate:

POST http://localhost:19515/ HTTP/1.1
Accept: text/html
Referer: http://localhost:19515/
Origin: http://localhost:19515
Content-Type: application/x-www-form-urlencoded

TestData=One&TestData=Two

Fijaos en la diferencia (marcada en negrita). El Model Binder por defecto de ASP.NET MVC es capaz de gestionar el primer caso, pero no el segundo, de ahí que ahora no funcione y nos enlace tan solo el primer TestData que se encuentra (y que vale One).

¿Y la solución? Pues hacernos un Model Binder propio que sea capaz de tratar estos casos. Por suerte el código no es excesivamente complejo:

public class EnumFlagModelBinder : DefaultModelBinder

    {

        public override object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)

        {

            if (!(bindingContext.Model  is Enum))

            {

                return base.BindModel(controllerContext, bindingContext);   

            }

 

            var value = bindingContext.ValueProvider.GetValue(bindingContext.ModelName);

            if (value.RawValue is string[])

            {

                var tokens = (string[]) value.RawValue;

                int intValue = 0;

                foreach (var token in tokens)

                {

                    var currentIntValue = 0;

                    var isIntValue = Int32.TryParse(token, out currentIntValue);

                    if (isIntValue)

                    {

                        intValue |= currentIntValue;

                    }

                    else

                    {

                        var modelType = bindingContext.Model.GetType();

                        intValue |= Convert.ToInt32(Enum.Parse(modelType, token));

                    }

                }

 

                return Enum.Parse(bindingContext.Model.GetType(), intValue.ToString());

            }

 

            return base.BindModel(controllerContext, bindingContext);

 

 

        }

    }

El código es muy sencillo:

Si el tipo del modelo no es un Enum derivamos hacia el enlace por defecto de ASP.NET MVC. En caso contrario obtenemos el valor de la propiedad que se está enlazando, consultando a los Value Providers.

Este valor será un string[] con los diversos valores que el usuario ha entrado (p. ej. un string[] con dos elementos “One” y “Two”). Iteramos sobre este array de valores y por cada valor:

  1. Miramos si es un entero. Si lo es, hacemos un or a nivel de bits entre una variable (intValue inicialmente a cero) y este valor.
  2. Si NO es un entero (p.ej. es “One”) obtenemos el valor entero con Enum.Parse y realizamos el mismo or a nivel de bits.

Finalmente devolvemos el valor asociado al valor de entero que hemos obtenido.

P. ej. si el usuario ha marcado las checks “One” y “Two” el array (tokens) tendrá esos dos valores. Al final IntValue valdrá 3 (el resultado de hacer un or de bits entre 1 y 2) y devolveremos el resultado de hacer Enum.Parse de este 3 (que es precisamente One | Two).

Nota: El hecho de mirar primero si el valor de token es un entero, es un pequeño refinamiento para que nuestro EditorTemplate funcione también en aquellos casos en que nos envíen los valores numéricos asociados (que nos envien p. ej. TestData=1&TestData=2 en la petición. Usando nuestro EditorTemplate este caso no se da).

Ahora tan solo debemos indicarle a ASP.NET MC que use nuestro Model Binder para enlazar las propiedades de tipo TestEnum. Aunque nuestro Model Binder podría enlazar cualquier Enum (es genérico) ASP.NET MVC no nos permite asociar un Model Binder a “cualquier Enum”. Para indicarle a ASP.NET MVC que use nuestro Model Binder para las propiedades de tipo TestEnum basta con añadir en el Application_Start:

ModelBinders.Binders.Add(typeof(TestEnum), new EnumFlagModelBinder());

Y ¡listos! Con esto hemos terminado.

Espero que os haya sido interesante.

[WaveEngine] Medidor de fps

Bueno… sigo mi serie de posts sobre WaveEngine. En los dos primeros posts vimos como poner un sprite en pantalla y luego como animarlo. Ambos pasos (y algunos más sobre los que todavía no he comentado nada) están descritos en uno de los hand-on-labs de Wave: el platform game sample.

Antes que nada el disclaimer obligatorio: En todos esos posts sobre Wave, explico la manera que he encontrado yo para hacer las cosas. Eso no significa que sea la mejor, la más óptima o incluso la correcta. Por el momento la documentación sobre Wave es bastante escueta. Algunos tutoriales, código fuente de algunos ejemplos y una referencia de todas las clases (que no sirve apenas para nada).

Bueno, aclarado esto, en este post voy a mostrar como crear un indicador que nos muestre, en todo momento, a cuantos frames por segundo se está ejecutando nuestro juego.

Jerarquía de elementos de Wave

En el primer post vimos que un juego en Wave tiene una jerarquía de elementos realmente sencilla:

  • Escena
    • Entidades
      • Componentes

Básicamente una escena es un conjunto de entidades y cada entidad contiene uno o varios (generalmente varios) componentes. Los componentes son cualquier cosa que es susceptible de formar parte de una entidad. Algunos componentes, como p.ej. Sprite son meros contenedores de datos, pero hay dos tipos de componentes especiales:

  1. Behaviors: Son componentes de “lógica”. Disponen de un método Update() que Wave llama automáticamente en cada iteración del game-loop. Para implementarlos debe derivarse de Behavior.
  2. Drawables: Son componentes de “dibujo”. Disponen de un método Draw() que Wave llama automáticamente en cada iteración del game-loop. Para implementarlos debe derivarse de Drawable2D o Drawable3D según sea el caso.

Por supuesto es posible que un componente dependa de otro componente. P. ej. el componente Sprite depende del componente Transform2D, es decir siempre que a una entidad se le agregue una instancia del primero, deberá agregársele también una instancia del segundo. Para indicar que un componente depende de otro, se usa el atributo RequiredComponentAttribute:

[RequiredComponent]

public Transform2D Transform2D;

Automáticamente Wave inyectará en la variable decorada con [RequiredComponent] el componente del tipo indicado (en este caso Transform2D) que esté añadido a la misma entidad.

Opción 1: Usar un Drawable

Para crear el monitor de fps vamos a crear un componente nuevo. Dado que debe dibujar en la pantalla (el valor de fps) el componente derivará de Drawable2D.

Por el mero hecho de derivar de Drawable2D obtenemos un método Draw que Wave nos llama en cada iteración del game-loop. Además nos pasa un parámetro que es un TimeSpan con el tiempo que ha pasado desde la llamada anterior. Eso es porque Wave no se sincroniza a ninguna velocidad en concreto. Es decir el game-loop de Wave básicamente:

  1. Llama al método Update() de todos los Behaviors
  2. Llama al método Draw() de todos los Drawables

Y eso lo hace continuamente y sin parar. Si queremos que nuestro videojuego vaya, al menos, a 30 fps, eso significa que la ejecución de todos los métodos Update y Draw de todos los componentes de todas las entidades de la escena debe tardar menos de 1/30 segundos. Cuanto menos tarde Wave en ejecutar todos componentes de la escena, nuestro juego irá a más fps. Por lo tanto, el fps no es un valor fijo si no que depende de la complejidad de la escena y del hardware que ejecuta el sistema. De ahí que Wave nos pase el TimeSpan para que podamos saber cuanto tiempo (cuantos milisegundos) han pasado desde la llamada anterior (este valor tampoco es constante ya que pueden aparecer o desaparecer entidades de la escena en cualquier momento).

Si derivamos de la clase Drawable2D veremos que existen DOS métodos Draw. Uno llamado Draw y otro llamado DrawBasicUnit que es abstracto y estamos obligados a implementar. Tras buscar en vano información sobre que diferencia había entre ambos métodos, al final me sumergí (con la ayuda de Resharper) en el código de Wave Engine y he llegado a la siguiente conclusión:

  • El código de renderizado debe ir en DrawBasicUnit. El parámetro que recibimos (un int llamado con el somero nombre de “parameter”) es el orden de nuestro Drawable. Es decir valdrá 0 si se ha invocado el primero, 1 si es el segundo, etc.
  • En el método Draw() debe ir todo aquel código que dependa de saber el valor del TimeStamp que mencionaba antes. Pero NO pongas código de renderizado en él.

Si tu Drawable no requiere sincronizarse con el tiempo (la gran mayoría no lo necesitan, ya que simplemente se dibujan cada vez) entonces no debes redefinir Draw(). Sólo debes implementar DrawBasicUnit.

Armado con esta suposición he codificado mi componente que muestra los fps tal y como sigue:

public class FpsMeasure : Drawable2D

{

    private int _fps;

    private int _currentSecondFps;

    private int _ms;

    private SpriteFont _font;

    private static int _instances;

 

    public FpsMeasure()

        : base("fps" + (_instances++), DefaultLayers.Opaque)

    {

    }

 

    protected override void Initialize()

    {

        base.Initialize();

        _font = Assets.LoadAsset<SpriteFont>("Content/Arial Rounded MT.wpk");

    }

 

    protected override void Dispose(bool disposing)

    {

    }

 

    public override void Draw(TimeSpan gameTime)

    {

        _ms += gameTime.Milliseconds;

        _currentSecondFps++;

        if (_ms >= 1000)

        {

            _fps = _currentSecondFps;

            _currentSecondFps = 0;

            _ms = 0;

        }

        base.Draw(gameTime);

    }

 

    protected override void DrawBasicUnit(int parameter)

    {

        if (_fps != 0)

        {

            spriteBatch.DrawStringVM(_font, _fps.ToString(), new Vector2(10, 10), Color.White);

        }

    }

}

Aspectos a comentar de este código:

  1. En el constructor asignamos un nombre único a este componente (ya que todos los componentes deben tener uno).
  2. En el Initialize() es el lugar donde podemos inicializar todo aquello que dependa de Wave. En este punto los componentes requeridos por nuestro componente (y que hemos decorado con [RequiredComponent] ya tendrán valor). En mi caso cargo la fuente de un asset que he creado
  3. Para crear el asset con la fuente he usado el Assets Exporter, y he añadido una fuente (menú Project –> Add Font). Al exportar me genera el .wpk de la fuente que debo añadir al proyecto de VS2012.
  4. En el método Draw simplemente guardo en una variable (_fps) los frames del segundo actual. Pero NO dibujo nada en la pantalla. Simplemente actualizo el valor de _fps cada segundo.
  5. En el método DrawBasicUnit es donde hay el código de dibujo: llamo al método DrawStringVM del objeto SpriteBatch que como Drawable tenemos. El método DrawStringVM básicamente dibuja un texto en la posición especificada.

Nota importante: Si redefines Draw ¡no te olvides la llamada a base.Draw o el método DrawBasicUnit no se llamará nunca!

Para usar nuestro componente debemos vincularlo a una entidad para poder añadirlo a la escena:

var fps = new Entity("fps").

    AddComponent(new FpsMeasure());

EntityManager.Add(fps);

¡Y listos! Con esto ya tenemos un contador que nos muestra los fps a los que se está ejecutando nuestro juego.

Opción 2: Usar un Drawable y un Behavior

El Drawable que hemos creado tiene cierta lógica en el método Draw: calcular los fps. Para mostrar la naturaleza modular de Wave y la interdependencia entre Componentes, vamos a mover esta lógica y colocarla en un Behavior. El Drawable quedará tan solo con el código de renderizado.

Empezamos quitando todo el código de cálculo de los frames por segundo del Drawable y dejando solo el de renderizado e inicialización de la fuente:

public class FpsMeasure : Drawable2D

{

    private SpriteFont _font;

    private static int _instances;

 

    public int FpsValue { get; set; }

 

    public FpsMeasure()

        : base("fps" + (_instances++), DefaultLayers.Opaque)

    {

    }

 

    protected override void Initialize()

    {

        base.Initialize();

        _font = Assets.LoadAsset<SpriteFont>("Content/Arial Rounded MT.wpk");

    }

 

    protected override void Dispose(bool disposing)

    {

 

    }

 

    protected override void DrawBasicUnit(int parameter)

    {

        if (FpsValue != 0)

        {

            spriteBatch.DrawStringVM(_font, FpsValue.ToString(), new Vector2(10, 10), Color.White);

        }

    }

}

Fíjate como ahora ya no hay necesidad de redefinir Draw, ya que tenemos un Drawable que básicamente lo único que hace es dibujar el valor de la propiedad FpsValue.

Ahora nos toca crear el Behavior. Así pues creamos una clase que derive de Behavior y en el método Update() ponemos el código que teníamos antes en el método Draw():

public class FpsMeasureBehavior : Behavior

{

    private int _currentSecondFps;

    private int _ms;

    private static int _instances;

 

    public FpsMeasureBehavior()

        : base("fpsBehavior" + (_instances++))

    {

    }

 

    [RequiredComponent]

    public FpsMeasure fpsDrawable;

 

    protected override void Update(TimeSpan gameTime)

    {

        _ms += gameTime.Milliseconds;

        _currentSecondFps++;

        if (_ms >= 1000)

        {

            fpsDrawable.FpsValue = _currentSecondFps;

            _currentSecondFps = 0;

            _ms = 0;

        }

    }

}

Observa el uso de [RequiredComponent] para indicar que este Behavior requiere del componente FpsMeasure (el Drawable) para su funcionamiento. Si creásemos una entidad y le añadiésemos este Behavior, pero no le añadiésemos un FpsMeasure, Wave nos daría un error:

SNAGHTMLa191587

¡Listos! Ahora ya tenemos nuestro contador de fps implementado a través de un Behavior (que proporciona la lógica) y un Drawable (que se limita a dibujar datos). Por supuesto, para que funcione hemos de crear una entidad con ambos componentes:

var fps = new Entity("fps").

    AddComponent(new FpsMeasure()).

    AddComponent(new FpsMeasureBehavior());

¿Y cual es mi opción preferida?

La segunda, sin duda alguna. Porque es mucho modular, porque separa mejor las responsabilidades y porque permite reaprovechar más los componentes. Fíjate que realmente el Drawable FpsMeasure, se comporta en realidad como una label (se limita a mostrar texto sin  importarle de donde viene este texto), así que con poco trabajo sería convertible a un componente más genérico (p. ej. TextSprite o algo así) y ser reutilizado en otros sitios.

En fin, espero que os haya resultado interesante 😉

Saludos!

[Wave Engine] Animando a Mai

Buenas! Este post es el segundo sobre WaveEngine, esta maravilla que han parido los chicos de Plain Concepts 🙂

En el post anterior, vimos los fundamentos de Wave Engine y terminamos con un programa que mostraba a Mai Shiranui en la esquina inferior izquierda de la pantalla. Pero Mai Shiranui gana mucho cuando se mueve (¿por qué será?) así que vamos a ver como podemos hacerlo para que nuestra bella protagoniste se anime.

Aunque a tenor del post anterior pueda parecer que tan solo hemos mostrado una imagen por pantalla, desde el punto de vista de Wave hicimos mucho más: definimos una entidad y le asociamos entre otros el componente de sprite.

Hoy vamos a añadir un componente nuevo, el de animación. Y es que, obviamente, en Wave los sprites pueden estar animados.

Sprites sheets

En Wave una animación consta de varios “estados” (varias animaciones realmente) y cada estado consta de varias imágenes. Así podemos tener una animación (realmente un componente de tipo Animation2D) que defina los estados “Idle” (cuando el personaje no hace nada) y  “walk” (cuando el personaje camina). Cada uno de esos estados consta de varios gráficos que conformarán cada de esos estados.

En este post la animación tendrá un solo estado (que llamaremos “idle”) y que constará de 12 imágnes (12 pngs que se llaman mai_idle (0).png hasta mai_idle (11).png).

Por supuesto podríamos usar la herramienta Wave Exporter para generar 12 .wpks (es decir uno por cada png tal y como hicimos en el post anterior) pero es mucho más eficiente usar un sprite sheet. Los que vengáis del mundo web probablemente conocereis este concepto ya que se usa mucho en CSS. La razón es que es mucho más eficiente bajarse una sola imagen grande y mostrar (por CSS) solo la parte que nos interesa que descargarse varias imágenes pequeñas. Pues en videojuegos ocurre lo mismo: es mucho mejor tener una sola textura (recordad que las imágenes son realmente texturas) grande y mostrar tan solo una parte que tener varias texturas pequeñas.

El primer paso consiste en generar una sola imagen (un png) que combine todas las imágenes que formaran parte de mi sprite sheet. Ni se te ocurra hacerlo a mano (cortando y pegando), ya que por suerte hay herramientas para ello. En los tutoriales de Wave hay una explicación muy buena de como hacerlo paso a paso utilizando Texture Packer. Para no repetirme yo haré un ejemplo utilizando otra herramienta, llamada Shoebox. La verdad es que Texture Packer es más completa (pero hay funcionalidades de pago), pero usar Shoebox me va a permitir contaros una cosilla más sobre Wave 🙂

Lo primero es generar el .png que contenga los 12 pngs, esto con Shoebox es muy sencillo. Arranco Shoebox, selecciono los 12 pngs desde el explorador de archivos y los arrastro sobre el icono “Sprite Sheet” de la ventana de Shoebox. Hecho esto me aparece una ventana con el sprite sheet:

image

Con el botón se settings puedo modificar varias propiedades, como p. ej. si la textura debe ser cuadrada, debe tener un tamaño que sea potencia de dos (eso es deseable en según que motores gráficos por temas de rendimiento) y también el formato del fichero XML que nos generará:

image

Sí, he dicho formato XML, porque tener un png grande con todos los pngs pequeños incrustados no sirve de nada si no tenemos manera de saber donde empieza y termina cada “sub-imagen”. ShoeBox nos genera un fichero XML con toda esa información (Texture Packer también, por supuesto).

En mi caso el contenido del fichero XML es el siguiente (en el formato por defecto que genera Shoebox):

<TextureAtlas imagePath="sheet.png">

    <SubTexture name="mai_idle (0).png" x="0" y="189" width="77" height="93"/>

    <SubTexture name="mai_idle (1).png" x="0" y="0" width="77" height="92"/>

    <SubTexture name="mai_idle (2).png" x="0" y="94" width="77" height="93"/>

    <SubTexture name="mai_idle (3).png" x="157" y="98" width="76" height="95"/>

    <SubTexture name="mai_idle (4).png" x="157" y="0" width="76" height="96"/>

    <SubTexture name="mai_idle (5).png" x="235" y="192" width="76" height="95"/>

    <SubTexture name="mai_idle (6).png" x="235" y="0" width="76" height="93"/>

    <SubTexture name="mai_idle (7).png" x="79" y="0" width="76" height="92"/>

    <SubTexture name="mai_idle (8).png" x="79" y="192" width="76" height="93"/>

    <SubTexture name="mai_idle (9).png" x="157" y="195" width="76" height="95"/>

    <SubTexture name="mai_idle (10).png" x="79" y="94" width="76" height="96"/>

    <SubTexture name="mai_idle (11).png" x="235" y="95" width="76" height="95"/>

</TextureAtlas>

Básicamente se incluye el nombre del png original y su posición y tamaño dentro del png “global”.

Ahora usamos Assets Exporter para convertir el png grande a un asset en formato .wpk e incluímos dicho asset y el .xml en el proyecto de VS2012:

image

Y con eso ya tendremos suficiente. Los 12 pngs originales no los necesitamos para nada.

Animaciones 2D en WaveEngine

Ahora el siguiente paso es modificar la definición de entidad para añadirle un componente nuevo de tipo Animation2D. Este componente, a pesar de su nombre, puede contener un conjunto de animaciones. De momento nosotros tendremos tan solo una (Mai en estado idle), pero podríamos tener varias animaciones distintas (andar, saltar, etc) usando un solo sprite sheet.

El código de definición de la entidad es como sigue:

var mai = new Entity("Mai").

    AddComponent(new Transform2D()

    {

        X = 50,

        Y = WaveServices.Platform.ScreenHeight 46,

        Origin = new Vector2(0.5f, 1)

    }).

    AddComponent(new Sprite("Content/mai.wpk")).

    AddComponent(Animation2D.Create<ShoeBoxXmlSpriteSheetLoader>("Content/mai.xml").

        Add("Idle", new SpriteSheetAnimationSequence() {

            First = 1,

            Length = 12,

            FramesPerSecond = 9

        })).

    AddComponent(new AnimatedSpriteRenderer(DefaultLayers.Alpha));

Las dos diferencias respecto el código del post anterior (además del uso de la fluent interface) son:

  1. Que se añade el componente Animation2D
  2. El componente SpriteRenderer es modificado por el AnimatedSpriteRenderer

Centrémonos en el primer punto: llamamos al método estático Animation2D.Create para crear una animación. A dicho método le tenemos que pasar un parámetro genérico que es una clase que va a ser la encargada de indicar donde, dentro del asset gráfico, está cada frame de la animación. En mi caso uso la clase ShoeBoxXmlSpriteSheetLoader. Esta clase me la he creado yo y lo que básicamente hace es leer el fichero XML generado por Shoebox y devolver un array de objetos Rectangle que indican la posición y tamaño de cada frame de la animación. El código es como sigue:

class ShoeBoxXmlSpriteSheetLoader : ISpriteSheetLoader

{

    public Rectangle[] Parse(string path)

    {

        var doc = XDocument.Load(path);

        var descs = doc.Descendants(XName.Get("SubTexture")).

            Select(node => new Rectangle

            {

                X = int.Parse(node.Attribute("x").Value),

                Y = int.Parse(node.Attribute("y").Value),

                Width = int.Parse(node.Attribute("width").Value),

                Height = int.Parse(node.Attribute("height").Value)

            }).ToArray();

 

        return descs;

    }

}

Una vez tenemos el Animation2D creado debemos añadirle todas las animaciones que realmente contiene (recuerda que puede contener varias). En este caso tan solo contiene una, y por ello tenemos tan solo una llamada al método Add. A cada animación le damos un nombre, y luego un objeto SpriteSheetAnimationSecuence que determina cual es el frame inicial (dentro del Animation2D), cual es el final y cuantos frames deben renderizarse por segundo. En mi caso le coloco 9, y así toda la animación (que consta de 12 frames) tardará 1,3 segundos en realizarse lo que me pareció un valor razonable.

Finalmente el resto del código de la escena es parecido al del post anterior:

EntityManager.Add(mai);

mai.FindComponentOfType<Animation2D>().Play(true);

Añadimos la entidad a la escena y empezamos a reproducir la animación. El método Play reproduce la animación actual dentro del componente Animation2D (en nuestro caso solo hay una, si hay más de una se puede usar la propiedad CurrentAnimation del propio componente). El parámetro booleano que vale true es para indicar que la animación se vaya repitiendo en un bucle.

¡Y listos! Con esto ya tenemos a Mai en todo su esplendor: http://screencast.com/t/eUwEVcSVn6xg

Let’s fight!

Espero que os haya resultado interesante…

Un saludo!!!

ASP.NET MVC – Patrón PRG sin sesión

Buenas! El patrón PRG (Post – Redirect – Get) es un patrón muy usado en el desarrollo web. Consiste en que la respuesta de una petición POST es siempre una redirección, lo que genera un GET del navegador y de ahí el nombre.

La idea que subyace tras el patrón PRG es, que dado que dado que las peticiones GET son (¡deberían ser!) idempotentes esas son las únicas que el usuario debe poder refrescar. De hecho los navegadores nos avisan si refrescamos una petición POST:

image

La razón de este aviso no es tanto notificar al usuario que se reenviarán esos datos, la razón es que el hecho de que se envíen via POST hace que se asuma que dicha petición no es idempotente, o dicho de otro modo modifica datos en el sistema (da de alta un usuario, o borra un producto o realiza una compra). Es pues un mecanismo de protección.

Para evitar esto en el patrón PRG cada método de acción que gestiona un POST, no devuelve la vista con el resultado de dicha petición si no que devuelve una redirección a otra acción que es la que muestra el resultado. Así si tenemos una vista que tiene un formulario como el siguiente:

@using (Html.BeginForm())
{
    @Html.TextBox("somevalue")
    <input type="submit" value="send post" />
}

El método de acción podría ser algo como:

[HttpPost]
public ActionResult Index(string somevalue)
{
    // Procesar resultados.
    var id = new Random().Next();
    // ...
    return RedirectToAction("View", new { id = id });
}

El método que procesa el POST realiza las tareas que sean necesarias y luego redirecciona a otra acción, por lo que lo después de enviar el formulario el usuario refresca la página, refrescará la última petición web que es la petición GET (en lugar de intentar refrescar el POST).

Usar el patrón PRG es una muy buena práctica, pero conlleva un pequeño problema: como pasar información desde la acción POST hacia la acción GET.

P. ej. en el POST podemos crear o modificar datos de alguna entidad y luego en el GET podemos mostrar esa entidad creada o modificada. Una solución es pasarle el ID de la entidad (tal y como se hace en el código anterior). Eso es perfectamente válido pero implica un round-trip a la BBDD. En el POST teníamos los datos de toda la entidad, pero en el GET debemos recuperarlos de nuevo ya que solo tenemos el ID.

Si hubiese alguna manera de pasar todos los datos necesarios (p. ej. toda la entidad) des del POST hacía el GET entonces, en algunos casos, nos podríamos ahorrar tener que ir a buscar datos que ya teníamos.

En ASP.NET MVC existe un mecanismo pensado para transferir datos entre redirecciones, que es justo lo que necesitamos y es TempData:

[HttpPost]
public ActionResult Index(string somevalue)
{
    // Procesar resultados.
    var id = new Random().Next();
    var someData = new Person() { 
       Id = id, Name = somevalue 
     };
    TempData["someData"] = someData;
    return RedirectToAction("View", 
          new { id = id });
}
 
[ActionName("View")]
public ActionResult ViewGet(string id)
{
    var data = TempData["someData"] as Person;
    if (data == null)
    {
        // Recargar data usando el id que 
        //tenemos por parámetro
    }
    return View(data);
}

Fíjate que usar TempData no exime de pasar el id igualmente a la acción GET ya que los datos que recogemos de TempData pueden no existir. Si el usuario refresca la página o bien teclea directamente la URL de la acción View los datos de TempData no existirán. Recuerda que TempData es un contenedor que permite una sola lectura (los datos desaparecen una vez leídos).

Bien, el punto a tener en cuenta (y motivo principal de este post) al usar TempData es que éste usa sesión. Y no siempre podemos o queremos usar la sesión. Si por cualquier razón no queremos usar la sesión, ¿podemos seguir usando TempData?

La respuesta es que si, pero debes implementarte un custom TempData provider. Claro que la otra pregunta es donde podemos guardar esos datos. Recuerda que TempData debe persistir entre peticiones (de ahí que por defecto se use la sesión). No hay muchos lugares más donde lo podamos guardar, así que si estás pensando en cookies has dado en el clavo. Si en el POST enviamos una cookie, cuando el navegador realice la petición GET posterior reenviará la cookie que contendrá los datos de TempData.

Para crear un proveedor propio de TempData tenemos que seguir 2 pasos:

  1. Crear una clase que implemente ITempDataProvider. Esta interfaz define dos métodos (SaveTempData y LoadTempData).
  2. Redefinir el método CreateTempDataProvider de la clase Controller y devolver una instancia de nuestra clase que implementa ITempDataProvider. Esto debemos hacerlo en cada controlador que queramos que use nuestro TempData provider o bien lo podemos poner en un controlador base.

No voy a colgarme medallas que no me pertenecen poniendo la implementación de un proveedor de TempData que use cookies, ya que hay varios por internet e incluso en el código fuente de MVC4 viene uno. Está en el código fuente pero no en los binarios, ya que forma parte del paquete Mvc4Futures. Si quieres usarlo, debes instalar primero este paquete usando Install-Package Mvc4Futures desde la cónsola de NuGet o bien usando la GUI.

Y ya puedes usar TempData sin necesidad de usar la sesión! 😉

Saludos!