Unobtrusive Ajax en MVC3

Buenas! Una de las novedades más interesantes de MVC3 es el soporte para eso que se llama Unobtrusive Ajax. La verdad es que no encuentro una buena traducción para Unobtrusive (discreto no me convence).

La idea del Unobtrusive Ajax es evitar mezclar código script con código HTML. De la misma manera que CSS nos permite separar completamente el código HTML de su representación, con Unobtrusive Ajax vamos a poder separar el código javascript del código HTML.

Pero mejor, veamoslo con un ejemplo, ultra sencillo 🙂

Imaginad que tengo una vista con este contenido:

<h2>Normal Ajax</h2>
<% using (Ajax.BeginForm("PostData", new AjaxOptions() { HttpMethod="Post", UpdateTargetId="datadiv"})) { %>

<label for="name">Name: </label>
<input type="text" name="name" id="name"/>
<input type="submit" value="Send" />
<% } %>
<hr />
Aquí irá el resultado: <p />
<div id="datadiv">
</div>

Esta vista genera un <form> con un campo de texto y envía los datos a una acción llamada “PostData” e incrusta el resultado de dicha acción (que será una vista parcial) en el div cuyo id es “datadiv”.

Este es el código HTML generado por esta vista en MVC2:

<h2>Normal Ajax</h2>
<form action="/Home/PostData" method="post"
onclick="Sys.Mvc.AsyncForm.handleClick(this, new Sys.UI.DomEvent(event));"
onsubmit="Sys.Mvc.AsyncForm.handleSubmit(this, new Sys.UI.DomEvent(event), { insertionMode: Sys.Mvc.InsertionMode.replace, httpMethod: &#39;Post&#39;, updateTargetId: &#39;datadiv&#39; });">
<label for="name">Name: </label>
<input type="text" name="name" id="name"/>
<input type="submit" value="Send" />
</form>
<hr />
Aquí irá el resultado: <p />
<div id="datadiv">
</div>

Fijaos en que en el tag <form> se le ha incrustado código javascript para gestionar el onclick y el onsubmit (para poder realizar el envío via ajax).

Bien… y esta misma vista (idéntica) que código genera en MVC3? Pues el siguiente:

<h2>Normal Ajax</h2>
<form action="/Home/PostData"
data-ajax="true" data-ajax-method="Post" data-ajax-mode="replace"
data-ajax-update="#datadiv" method="post">

<label for="name">Name: </label>
<input type="text" name="name" id="name"/>
<input type="submit" value="Send" />
</form>

<hr />
Aquí irá el resultado: <p />
<div id="datadiv">
</div>

Fijaos que diferencia… No hay nada de javascript mezclado en el código. Todo es HTML. Simplemente al tag <form> se le añaden unos cuantos atributos (los que empiezan por data-ajax) que indican como se debe comportarse este formulario a nivel de Ajax.

Y quien realiza “la magia”? Pues quien va a ser… nuestra amada jQuery, junto con una extensión de Microsoft (el fichero jquery.unobtrusive-ajax.js)! Para que esto funciona teneis que añadir tanto jQuery como la extensión de MS (yo los pongo en la master):

<script src="<%: Url.Content("~/Scripts/jquery-1.4.1.js") %>" type="text/javascript"></script>
<script src="<%: Url.Content("~/Scripts/jquery.unobtrusive-ajax.js") %>" type="text/javascript"></script>

Esta adaptación de los helpers en MVC3 para soportar esta característica es a lo que nos referimos cuando decimos que “ASP.NET MVC3 da soporte para Unobtrusive Ajax”, y es una doble gran noticia. Digo doble porque por un lado nos permite seguir usando los helpers con la garantía de que vamos a generar código “limpio” de javascript y por otro lado el helper de Ajax usa ¡por fin! jQuery. A diferencia de MVC2 donde el Helper Ajax usaba la Ajax Library de Microsoft. De hecho, aunque en los templates de proyecto se sigue poniendo, si me aceptas un consejo: bórrala y no la uses. Puedes borrarla con total tranquilidad porque en MVC3 ningún helper la usa.

Unobtrusive Ajax viene habilitado por defecto en los nuevos proyectos MVC3 pero lo podéis deshabilitar (y entonces generar el mismo código que en MVC2, usando la Microsoft Ajax Library). Podeis deshabilitarlo a nivel de vista o para todo el proyecto.

Para deshabilitarlo a nivel de vista, basta con incluir:

<% HtmlHelper.UnobtrusiveJavaScriptEnabled = false; %>

Para deshabilitarlo para todo el proyecto, puedes incluir ese mismo código en el global.asax.cs o bien usar web.config:

<configuration>
<appSettings>
<add key="UnobtrusiveJavaScriptEnabled" value="false"/>
</appSettings>
</configuration>

Lo mismo para habilitarlo. Si no aparece la entrada UnobtrusiveJavaScriptEnabled en el <appSettings> el valor por defecto es false. Es por eso que si haces un upgrade de un proyecto de MVC2 a MVC3, no tendrás esta entrada en el web.config y por eso Unobtrusive Ajax estará deshabilitado!

Un saludo!

PD: El hecho de que los atributos que se usan para que Unobtrusive Ajax funcione empiecen por “data-“ es porque HTML5 reserva estos atributos “para usos propios de los scripts del site”, tal y como podéis leer en la especificación de Custom Data Attributes.

How to: Obtener controles de un formulario con generics II (Linq al rescate).

Ayer Lluis escribía este gran post: How to: Obtener controles de un formulario con generics. Como bien dice es una pregunta… recurrente en todos los sitios 🙂

Lo bueno de eso del desarrollo es que para todo hay varias soluciones, así que aquí os propongo otra, pero usando Linq. Personalmente me encanta Linq, supongo que es porqué siempre me han fascinado los lenguajes funcionales…

Antes que nada tenemos que solucionar un temilla: Linq funciona sobre IEnumerable<T> pero la propiedad Controls de un Control devuelve un objeto de tipo ControlCollection (otra de esas n-mil clases que no tienen sentido alguno y que existen sólo porque no teníamos generics en la versión 1 del framework). Así que el primer paso es obtener un IEnumerable<Control> a partir de una ControlCollection. Con un método extensor eso es trivial:

public static IEnumerable<Control> AsEnumerable (this Control.ControlCollection @this)
{
foreach (var control in @this) yield return (Control)control;
}

Ale… listos, con eso transformamos la CollectionControl en un IEnumerable<Control> y tenemos acceso a todo el potencial de Linq… Y como queda el método para obtener todos los controles de un tipo? Pues así:

public static IEnumerable<T> GetAllControls<T>(this Control @this) where T : Control
{
return @this.Controls.AsEnumerable().Where(x => x.GetType() == typeof(T)).
Select(y=>(T)y).
Union(@this.Controls.AsEnumerable().SelectMany(x => GetAllControls<T>(x)).
Select(y=>(T)y));
}

¡No me diréis que no es precioso: no hay bucles, no hay ifs… Os he dicho que me encanta Linq? 😉

Hay una pequeña diferencia entre la versión de Lluís y esta mía: la versión de Lluís usa una List<Control> en la que añade todas las referencias (copia las referencias a la lista. Ojo: las referencias, no los controles). Esa versión que usa Linq, no copia las referencias en ningún sitio, sinó que simplemente itera sobre la coleccion original: no crea listas internas, ni nada… Ese es el poder de Linq!

Es cierto que al usar IEnumerable<T> como valor de retorno, perdemos el método ForEach() (puesto que IEnumerable<T> no lo tiene y Linq no lo proporciona), pero hacer un método ForEach es trivial:

public static void ForEach<T>(this IEnumerable<T> @this, Action<T> action)
{
foreach (T t in @this)
{
action(t);
}
}

De todos modos, si os preguntáis porque Linq no ofrece un método ForEach quizá deberíais leeros este post de Octavio: Observaciones con respecto a un método extensor ForEach<T>.

Un  saludo a todos y gracias a Lluís por darme “la excusa” de hacer otro post! 😉

Opinión: De si Silverlight está muerto, agonizante o mejor que nunca

Andamos todos revolucionados estos dias, a raíz de unas declaraciones de Bob Muglia donde decía “Our strategy with Silverlight has shifted”. Eso unido al énfasis que se dio a HTML5 en el keynote del PDC y la no mención en absoluto de nada referente a Silverlight han disparado los rumores.

Así pues… ¿está Silverlight muerto, agonizante o por el contrario está mejor que nunca? Primero un disclaimer: este es un post de opinión, todo lo que yo afirmo tajantemente en este post son cosas que yo creo. No tengo ni el conocimiento ni la razón absoluta, y además en MS nunca se sabe, así que como suele decirse: al final el tiempo dará y quitará razones 🙂

La estrategia inicial de Microsoft para posicionar Silverlight fue darle el nicho de aplicaciones web: Con Silverlight tus aplicaciones web tendrán una experiencia de usuario mejor, decían. En estos tiempos, comparar Silverlight con Flash era habitual, aunque Microsoft insistía en que no eran comparables: que Silverlight era para aplicaciones completas y no para pequeños adornos. Cuando hacía tiempo que Flash había asumido que no sustituiría a HTML, parecía ser que Silverlight quería asumir el relevo.

Pero con Silverlight 3 todo cambió: la posibilidad de ejecutar aplicaciones out-of-browser, la interoperabilidad COM y mejoras en el acceso a dispositivos locales, convertían a SL3 en algo distinto. Estaba claro que Silverlight llegaba a sitios donde HTML no sueña con llegar nunca. La batalla que inicialmente se libraba en el navegador ahora se trasladaba también al escritorio, y el rival era Adobe AIR. Habéis visto Seesmic Desktop 2? Es un excelente cliente de Twitter, para Windows y Mac, es una aplicación de escritorio… y está hecha con Silverlight.

¿Y donde estamos ahora?

El otro día en twitter conversaba con Alejandro Mezcua (@byteabyte) y Dani Mora (@LoDani) sobre el futuro de Silverlight. Yo argumentaba que no creo ni mucho menos que Silverlight esté muerto. De hecho le veo un gran futuro a Silverlight en aplicaciones de escritorio y en aplicaciones para Windows Phone 7 (bueno, aquí depende de como le vaya a WP7, claro). Pero no le veo futuro para aplicaciones web. Quiere decir esto que Silverlight va a desaparecer del browser? No, tiene su nicho en sites específicos donde HTML no llega: p. ej. nada mejor que Silverlight para streaming de video. Sí, sí… HTML5 va a soportar vídeo, pero no soportará DRM ni bitrates variables. Si necesitas esto, necesitarás algo externo que te lo de, y ahí entra Silverlight. Pero para la creación de aplicaciones web, no lo veo, y diría que Microsoft tampoco 🙂

¿Y RIA? La clave de RIA es lo que significa la I. Dani me comentaba que para él, la I de RIA era “aquel entorno que el administrador de sistemas podía controlar”. Entonces no estamos hablando de internet… llamésmole intranet o alguna otra cosa, pero no internet. Si quieres desarrollar una aplicación para internet no hay mejor opción que HTML. Y los tiempos han cambiado mucho: de acuerdo que hace tiempo desarrollar con javascript era poco menos que un infierno, pero ahora tenemos a jQuery que convierte eso en un juego de niños. Antes Ajax era doloroso y entraba con vaselina: ahora con un par de líneas puedes usar Ajax sin preocuparte de que navegadores se usan ni nada parecido. Y sí: hay jQuery para dispositivos móviles. Aunque de todos modos la experiencia parece demostrar que en los móviles la gente prefiere aplicaciones nativas antes que webs.

Así que si quieres estar presente en aplicaciones web dentro de poco tiempo, aprende ya HTML5. No hagas caso de los que dicen que no está terminado, que no saldrá hasta dentro de no se cuantos años: son verdades a medias. Es cierto que la especificación no está lista, pero muchas partes ya estan terminadas, y ya están siendo implementadas en los navegadores. No ha salido hace poco la noticia de que IE9 era el navegador más compatible con HTML5? Como podría ser si no hubiese salido nada de HTML5? Dentro de poco ya comenzaremos a ver sites desarrollados en HTML5. El momento de lanzarse no es dentro de un, dos o tres años: es ahora.

Pero seguiremos viendo Silverlight en el navegador? Pues creo que sí: en la gran internet en aquellos sitios junto a HTML, donde se requieran aspectos que HTML5 no va asumir y en intranets (o entornos controlados) también supongo que podrán verse… Aunque en estos entornos, porque obligar al usuario a usar un navegador? Las capacidades out of browser de Silverlight hacen que el usuario tenga la sensación de usar una aplicación nativa, con toda la seguridad del sandbox de Silverlight y la misma facilidad de actualización que si una aplicación web se tratara.

Y fuera del navegador? Pues sin duda. Veremos cada vez más aplicaciones de escritorio desarrolladas en Silverlight, y tener todas sus ventajas: actualizaciones automáticas, seguridad y multiplataforma (Windows, Mac). Y ojalá Microsoft se lance en serio a Linux y haga su implementación de Silverlight (y no lo deje medio abandonado con Moonlight).

De hecho, para resumir este post, si hay alguien que deba sentirse amenazado por Silverlight no es ni mucho menos HTML5… en todo caso, si hemos de buscar a alguien, se trataría de WPF. Cada versión de Silverlight acorta distancias con su hermano mayor y no es descabellado pensar si algún dia llegarán a unirse.

Y a vosotros que os parece?

Un saludo!

ASP.NET MVC3 Beta: Mis impresiones

Buenooo… ayer fue un día movidito en Microsoft: anunciaron de golpe la beta 2 de WebMatrix, la beta de MVC3 y un gestor de paquetes OSS para Visual Studio llamado NuPack. También he visto a través del Web PI que está la CTP2 de Compact SQL 4.

MVC1 salió con 5 (creo) previews antes de la beta, con MVC2 juraria que hicieron un par o tres, y con MVC3 sólo un preview1 y luego ya la beta… a ese ritmo MVC4 cuando salga lo hará ya con el SP1 incorporado. 😛 Esos de Microsoft van cada vez más rápido.

Bueno, aquí van un poco mis impresiones sobre la Beta 3 de MVC 🙂

Me centraré básicamente en lo que ha cambiado desde la preview1 hasta la Beta.

1. Creación de aplicaciones

Cuando le damos a nuevo proyecto en el Visual Studio, nos sale una sola opción de MVC3:  ASP.NET MVC 3 Web Application y una vez la seleccionamos es cuando nos deja elegir si queremos una aplicación vacía o la estándard (con los controladores Account y Home) y que View Engine queremos usar. Son las mismas opciones que teníamos en MVC2 (excepto lo del view engine) pero mejor organizadas.

Pero vamos… nada nuevo bajo el sol 😀

2. Donde está IServiceLocator?

En mi post sobre la preview1 comentaba que el soporte para IoC se basaba en la interfaz IServiceLocator, un proyecto de codeplex para proporcionar una interfaz común a distintos contenedores IoC y así permitir independizarnos de ellos. Decía también que la Preview1 contenía una implementación propia de IServiceLocator en lugar de usar la del assembly que está en codeplex, pero que se esperaba que en la versión final eso no fuera así.

Pues bien… al final eso ha cambiado un poco: Ahora, a través de la clase DependencyResolver podemos indicar a MVC3 que debe usar para resolver sus dependencias. Y tenemos tres posibilidades:

  1. Pasarle un objeto que implemente una interfaz nueva llamada IDependencyResolver (propia de MVC3). Dicha interfaz está definida:
  2. public interface IDependencyResolver
    {
    object GetService(Type serviceType);
    IEnumerable<object> GetServices(Type serviceType);
    }

  3. Pasarle dos delegates, el primero del cual se corresponde al método GetService y el segundo al método GetServices
  4. Pasarle un object. En este caso el object debe implementar la interfaz IServiceLocator. Pero (imagino que) ASP.NET MVC3 invocará los métodos via reflection, por lo que cualquier IServiceLocator vale, no es necesario usar el assembly de codeplex. Supongo que es para evitar tener una dependencia de MVC3 hacia un assembly “externo”.

Veamos un ejemplo:

P.ej. si usásemos Unity podríamos crear un IDependencyResolver propio:

public class UnityDependencyResolver : IDependencyResolver
{
private IUnityContainer _ctr;

public UnityDependencyResolver(IUnityContainer ctr)
{
_ctr = ctr;
}

public object GetService(Type serviceType)
{
return _ctr.Resolve(serviceType);
}

public IEnumerable<object> GetServices(Type serviceType)
{
return _ctr.ResolveAll(serviceType);
}
}

Y registrarlo en el global.asax:

UnityContainer ctr = new UnityContainer();
DependencyResolver.SetResolver(new UnityDependencyResolver(ctr));

Guay! Pero también podríamos usar la versión que admite un object y pasarle una implementación de IServiceLocator. La versió 2.0 de Unity ya viene con el assembly del service locator incorporado y un adaptador creado (si usais versiones anteriores de Unity os podéis descargar tanto la implementación de IServiceLocator como el adaptador para Unity desde codeplex).

En nuestro caso nos basta con agregar una referencia al ensamblado Microsoft.Practices.ServiceLocation.dll (que viene con Unity o os habéis descargado de codeplex) y usar:

UnityContainer ctr = new UnityContainer();
DependencyResolver.SetResolver(new UnityServiceLocator(ctr));

(Si usáis una versión de Unity anterior a la 2.0 necesitaréis también una referencia a Microsoft.Practices.Unity.ServiceLocatorAdapter.dll que también os habréis descargado de codeplex).

Listos! Con esto MVC3 ya usará nuestro DependencyResolver para crear los controladores, así que ya podremos inyectarles dependencias (fijaos que no ha sido necesario crear una factoría propia como hacíamos en MVC2).

3. IControllerActivator

IControllerActivator es una interfaz nueva de MVC3 cuya responsabilidad es devolver una instancia de un controlador de un determinado tipo. Antes esa responsabilidad era una más de las responsabilidades de la factoría de controladores: cuando en MVC2 queríamos usar DI generalmente implementábamos una factoría propia derivada de DefaultControllerFactory y refefiniamos el método GetControllerInstance:

P.ej. esa sería una factoría de controladores para usar IServiceLocator en MVC2:

public class ServiceLocatorControllerFactory : DefaultControllerFactory
{
private readonly IServiceLocator _sl;
public ServiceLocatorControllerFactory(IServiceLocator sl)
{
_sl = sl;
}
protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
IController controller = null;
controller = _sl != null ? _sl.GetInstance(controllerType) as IController :
base.GetControllerInstance(requestContext, controllerType);
return controller;
}
}

En MVC3 han separado los dos comportamientos: Por un lado el decidir que tipo de controlador instanciar se queda en la factoría de controladores y por otro lado instanciar el controlador es responsabilidad de IControllerActivator.

El uso de IControllerActivator nos permite definir como se crean nuestros controladores. Si no hay ningún IControllerActivator MVC3 usará el DependencyResolver (así que no es necesario implementar IControllerActivator sólo para DI). Si no hay DependencyResolver pues supongo que usará Reflection.

Como MVC3 busca las cosas en varios sitios podemos pasarle el IControllerActivator a la DefaultControllerFactory (tiene un parámetro que acepta un IControllerActivator) o bien, registrarlo en el DependencyResolver. O sea podemos usar esto:

// Registramos un singleton con nuestro IControllerActivator
ctr.RegisterInstance<IControllerActivator>(new CustomControllerActivator());
DependencyResolver.SetResolver(new UnityServiceLocator(ctr));
// No registramos IControllerFactory por lo que se usará la DefaultControllerFactory

O bien esto:

// Registramos la DefaultControllerFactory como la factoría a usar
// pasándole el IControllerActivator deseado
ctr.RegisterInstance<IControllerFactory>
(new DefaultControllerFactory(new CustomControllerActivator()));
DependencyResolver.SetResolver(new UnityServiceLocator(ctr));

Pero ambas cosas a la vez no (os dará error).

4. Nuevos Helpers

Bueno, ahora tenemos un conjunto de Helpers nuevos (bueno, nuevos para MVC, porque ya estaban en WebMatrix). Para usar esos helpers basta con agregar una referencia a System.Web.Helpers (viene ya por defecto):

<div>
El Hash de eiximenis es: @Crypto.Hash("eiximenis")
</div>

Diria que no están disponibles todos los helpers que hay en WebMatrix… Espero que en la versión final sí estén todos, porque hay algunos que son una pasada!

5. Ajax no “obtrusivo” (unobtrusive)

El helper Ajax de MVC3 puede generar código Ajax que no está mezclado con el código HTML, aprovechando jQuery y una de las nuevas capacidades de HTML5… P.ej. el siguiente código en una vista:

@Ajax.ActionLink("Pulsa aquí para modif el DIV", "AjaxAction", new AjaxOptions() { UpdateTargetId="mydiv"})
<div id="mydiv">Div a modificar</div>

Nos genera el código HTML:

<a data-ajax="true" data-ajax-mode="replace" data-ajax-update="#mydiv" href="/Home/AjaxAction">Pulsa aqu&#237; para modif el DIV</a>
<div id="mydiv">Div a modificar</div>

Fijaos que hay atributos “data-ajax-*“ que son interpretados por jQuery para realizar las acciones: de esa manera no hay ninguna llamada a ningún script mezclado con el código. Los atributos que empiezan por “data-“ son considerados especiales en HTML5 (custom HTML5 attributes): HTML5 no admite que pongamos nuestros propios atributos a no ser que empiecen por data-. Esto permite separar el comportamiento (definiendolo en estos atributos especiales) del código.

6. Soporte para Razor

Pues… sigue bajo mínimos… Es decir, Razor está totalmente soportado en run-time (y ahora con sintaxis VB.NET también!) pero para VS2010 Razor sigue sin existir. Las vistas .cshtml (o .vbhtml) no tienen ni sintaxis coloreada ni tampoco intellisense. Pero es que ni tan siquiera las reconoce como HTML (o sea ni tenemos intellisense de HTML tampoco).

Al margen de esto hay algunas novedades menores para Razor como el soporte de @model para definir el modelo, pero vamos… nada excepcionalmente nuevo.

7. Conclusiones

Las dos grandes novedades de esta Beta, respecto a la preview1, son algunas mejoras más en la inyección de dependencias (DependencyResolver, IControllerActivator) y el soporte para ajax no “obtrusivo”. El resto son mejoras menores (aunque interesantes).

Sigo pensando que, del mismo modo que se llama MVC3 se podría llamar MVC2.5, porque no hay cambios espectacularmente grandes (excepto Razor, pero es simplemente otro ViewEngine, como muchos más que ya existían). Eso no es necesariamente malo: el framework ya está alcanzando cierto grado de madurez.

Para finalizar, leeros las release notes de la beta de MVC3 donde se enumeran las novedades!

Un saludo!

SQL Server Compact 4: Unable to load the native components

Buenas! Un post ligerito, ligerito 🙂

Ando esos días probando cosillas con SQL Server Compact 4 (que os podéis descargar desde su página de descargas o bien usando Web Platofrm Installer).

Habiendo probado código con EF 4 Code First que estaba funcionando bien, empecé otras pruebas usando el proveedor propio de ADO.NET. Así que agregué una referencia a la System.Data.SqlServerCe.dll que viene con SQL Server Compact 4, y cree una conexión:

SqlCeConnection con = new SqlCeConnection()

Pues bien, al crear la conexión (fijaos que aquí no hay cadena de conexión, todavía no me conecto a ningún .sdf) ya me salta una SqlCeException:

Unable to load the native components of SQL Server Compact corresponding to the ADO.NET provider of version 8402. Install the correct version of SQL Server Compact. Refer to KB article 974247 for more details.

Hombre, es de agradecer que la propia excepción te diga un KB que consultar, así que ni corto ni perezoso me dirigo al KB974247, pero nada de lo que dice es aplicable en mi caso.

Descartando el hecho de que sea una mala instalación (puesto que con EF 4 Code First era capaz de conectarme) me puse a mirar que podría estar pasando… y la respuesta final es que se deben copiar los binarios de SQL Server Compact 4 a la carpeta bin de la aplicación.

En mi caso SQL Server Compact 4 se ha instalado en C:Program FilesMicrosoft SQL Server Compact Editionv4.0 y los binarios que he copiado son las carpetas x86 y amd64 que están en la carpeta Private. De hecho en mi caso me basta con la carpeta x86 puesto que mi máquina es de 32 bits.

Así pues en el bin/Debug de mi aplicación tengo mi aplicación, la System.Data.SqlServerCe.dll y la carpeta x86 con los binarios de SQL Server Compact 4.

Y entonces me funciona todo correctamente.

Un saludo!

PD: Y de regalo, os dejo (por si no lo conocíais) el enlace de una extensión, llamada SqlCeToolbox, para vs2010 para poder tratar con ficheros .sdf de la versión 4 (sinó la otra manera que hay es usar WebMatrix que tiene un diseñador de tablas, pero me parece más potente esta exensión).

EF4 Code First, MVC2 y Unity para atarlo todo un poco…

Buenas! No soy ni mucho menos un experto en EF (es más, me acabo de poner), como pueda serlo p.ej. Unai, pero desde que Scott Guthrie publicó un post sobre EF Code First he empezado a mirar algunas cosillas.

Resumiendo rápidamente EF Code First nos permite desarrollar nuestra capa de acceso a datos codificando primero las clases, clases que son POCO. Eso nos permite conseguir lo que se conoce como persistance ignorance (o que las clases que nos representan los datos sean agnósticas sobre cualquier tecnología de acceso a datos).

Que quede claro que Code First no es la única manera de usar objetos POCO en EF: Unai y Alberto hablan del tema aquí y aquí. Y dad por seguro que ellos conocen EF mucho más que yo 🙂

Lo que os quiero comentar es como se puede montar EF Code First en una aplicación MVC2 para poder usar el patrón Repository y Unit Of Work, de forma que los controladores no deban saber nada del mecanismo subyacente de acceso a datos (es decir, los controladores ignoran completamente que están usando EF).

1. Rápida, rapidísima introducción a EF Code First

Usar EF Code First es muy sencillo. En el post de Scott Guthrie está todo explicado paso a paso, pero resumiendo podríamos decir que me puedo crear una clase tal que:

public class Persona
{
public int PersonaId {get; set;}
public string Nombre {get; set;}
}

Esta clase nos permitiría acceder a datos almacenados en una tabla con los campos PersonaId y Nombre (EF Code First es capaz de crear la BBDD a partir del modelo de objetos). Para acceder a la bbdd creamos un objeto derivado de DbContext:

public class MyDatabase : DbContext
{
public DbSet<Persona> Personas {get; set;}
}

Y a partir de aquí instanciamos un objeto MyDatabase y lanzamos consultas linq contra la propiedad Personas… 🙂

2. Patrón repositorio y Unit of work

Esos patrones están explicados por activa y por pasiva en muchos sitios. P.ej. aquí está el patrón repositorio y aquí el Unit of Work (UoW). Básicamente entendemos que el repositorio es una colección de objetos en memoria y que el patrón UoW sincroniza todos los cambios hechos en memoria hacia la base de datos.

Supongamos estos dos interfaces para definir ambos patrones:

public interface IRepository<TEntity> where TEntity : class
{
IQueryable<TEntity> AsQueryable();
IEnumerable<TEntity> GetAll();
IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> where);
}

public interface IUnitOfWork
{
void Commit();
}

Mi repositorio es de sólo lectura (no hay métodos Add ni Remove ni nada, pero todo es añadirlos) y el Unit of Work lo único que tiene es el método Commit() para sincronizar los cambios hechos en los repositorios y mandarlos todos a la base de datos.

EF Code First, implementa de per se el patrón Unit Of Work (a través de DbContext) y también el patrón repositorio a través de DbSet<>, pero recordad que yo quiero “agnostizarme” al respecto de EF, por eso creo las interfaces y ahora debo crear implementaciones de ellas para EF:

public class Repository<TEntity> : IRepository<TEntity> where TEntity : class
{
private IDbSet<TEntity> _dbSet;

public Repository(IDbContext objectContext)
{
_dbSet = objectContext.Set<TEntity>();
}
public IQueryable<TEntity> AsQueryable()
{
return _dbSet;
}
public IEnumerable<TEntity> GetAll()
{
return _dbSet.ToList();
}
public IEnumerable<TEntity> Find(Expression<Func<TEntity, bool>> where)
{
return _dbSet.Where(where);
}
}

public class UnitOfWork : IUnitOfWork, IDisposable
{
private readonly IDbContext _objectContext;

public UnitOfWork(IDbContext objectContext)
{
_objectContext = objectContext;
}

public void Dispose()
{
if (_objectContext != null)
{
_objectContext.Dispose();
}
GC.SuppressFinalize(this);
}

public void Commit()
{
_objectContext.SaveChanges();
}
}

La única “complicación” es en la implementación de UnitOfWork, que en el constructor uso un objeto de tipo IDbContext, pero esta interfaz no existe en EF: me la he inventado yo, para poder realizar DI luego cuando use Unity. La interfaz IDbContext es muy simple:

public interface IDbContext : IDisposable
{
IDbSet<T> Set<T>() where T : class;
int SaveChanges();
}

Me permite obtener un IDbSet de entidades de tipo T y el método SaveChanges para persistir los cambios realizados en este contexto hacia la BBDD.

3. Uso de todo esto…

Para usar directamente un repositorio me basta con instanciarlo, pasándole como parámetro el IDbContext. Pero recordad que la clase DbContext de EF Code First no implementa IDbContext (que me lo he inventado yo), así que uso el patrón Adapter para traducir los objetos DbContext a IDbContext:

public class DbContextAdapter : IDbContext
{
private readonly DbContext _context;
public DbContextAdapter(DbContext context)
{
_context = context;
}
public void Dispose()
{
_context.Dispose();
}
public IDbSet<T> Set<T>() where T : class
{
return _context.Set<T>();
}
public int SaveChanges()
{
return _context.SaveChanges();
}
}

Ahora sí que ya puedo crear un repositorio:

IDbContext ctx = new DbContextAdapter(MyDatabase);  // MyDatabase deriva de DbContext
var rep = new Repository<Persona>(ctx);
var personas = rep.FindAll();

Para usar el Unit of Work vinculado a un contexto haríamos lo mismo.

4. Añadiendo IoC a todo esto…

Ahora que ya vemos como podemos usar nuestro repositorio, vamos a ver como Unity nos ayuda a tener IoC y independizarnos realmente de EF.

Primero podríamos registrar las implementaciones concretas de IRepository y IUnitOfWork:

// Container es el IUnityContainer
Container.RegisterType(typeof(IRepository<>), typeof(Repository<>));
Container.RegisterType<IUnitOfWork, UnitOfWork>();

Ahora debemos mapear IDbContext a DbContextAdapter (porque para instanciar tanto IRepository como IUnitOfWork les debemos pasar un IDbContext. Podríamos usar lo siguiente:

Container.RegisterType<IDbContext, DbContextAdapter>();

Este RegisterType está bien, pero el problema nos viene cuando vemos que el constructor de DbContextAdapter tiene un objeto DbContext. Eso significaría que Unity nos crearía un objeto de la clase DbContext y no de la clase derivada MyDatabase, para instanciar los objetos DbContextAdapter. Nota: Una solución sería que la clase DbContextAdapter tuviese en su constructor un objeto que no fuera DbContext sinó MyDatabase pero eso entonces limita la reutilización de todo lo que estamos haciendo!

Por suerte no estamos perdidos! Unity incorpora un mecanismo mediante el cual le podemos decir como crear un objeto de una determinada clase. En mi post sobre Unity 2.0, hablé de las Injection Factories. La idea es decirle a Unity que cuando necesite un objeto de la clase indicada, en lugar de crearlo tal cual use una factoría nuestra.

Es decir podemos hacer una factoría para crear DbContext que en lugar de un DbContext devuelva un MyDatabase, y Unity usará dicha factoría cada vez que deba crear un DbContext:

Container.RegisterType<DbContext>(new InjectionFactory(x => new MyDatabase()));

Listos! Con esto cuando Unity deba pasar a DbContextAdapter un objeto DbContext (como indica el constructor) creará realmente un objeto MyDatabase.

Ahora si que ya puedo hacer:

var rep = Container.Resolve<IRepository<Persona>>();

5. Y finalmente… MVC2

Para integrar esto dentro de MVC2 es muy sencillo: como siempre que queramos inyectar IoC en los controladores lo que debemos tocar es… la factoría de controladores:

public class UnityControllerFactory : DefaultControllerFactory
{
private readonly IUnityContainer _sl;

public UnityControllerFactory(IUnityContainer sl)
{
_sl = sl;
}

protected override IController GetControllerInstance(RequestContext requestContext, Type controllerType)
{
IController controller = _sl.Resolve(controllerType);
return controller;
}
}

Y como siempre establecerla en el Global.asax:

UnityControllerFactory slcf = new UnityControllerFactory(container);
ControllerBuilder.Current.SetControllerFactory(slcf);

Y listos! Ahora si que hemos llegado al punto final. Por fin podemos declarar un controlador tal que:

public class PersonasController : Controller
{
private IRepository<Persona> _rep;

public HandController(IRepository<Persona> rep)
{
_rep = rep;
}
}

Y empezar a trabajar usando el repositorio! 🙂

Fijaos que en este punto (el controlador) no tenemos nada que nos ate a EF: el repositorio es una interfaz y las clases que usa el repositorio son objetos POCO.

6. Y para terminar (pero NO lo menos importante)…

Cuando trabajamos con aplicaciones web, es recomendable que los contextos de base de datos tengan una duración (lifetime) de request (se les llama objetos per-request): es decir si durante una misma petición se necesitan dos repostorios, estos dos repositorios deben de compartir del contexto de base de datos. Con lo que tenemos ahora no sucede esto, ya que cada vez que Unity deba crear un Repository<> creará su DbContextAdapter asociado que a su vez creará un DbContext (MyDatabase) nuevo. Ese comportamiento no es el desado.

Por suerte Unity tiene un mecanismo muy bueno para poder establecer cada cuando debe el contenedor crear un objeto de un tipo determinado: los lifetime managers. Una solución es crearse un HttpRequestLifetimeManager, de forma que los objetos sólo persistirán durante la misma petición y usar este lifetime manager cuando hagamos el RegisterType de DbContext.

En http://unity.codeplex.com/Thread/View.aspx?ThreadId=38588 tenéis una implementación de un HttpRequestLifetimeManager, junto con una interesante aportación sobre el uso (en su lugar) de contenedores hijos que mueran a cada request, que tengan los objetos registrados como singletons y eliminar el contenedor hijo (con Dispose()) al final de cada request. Esto tiene la ventaja de que se llamaría a Dispose() de los objetos contenidos (en esta segunda aproximación es el contendor hijo el que tiene un ciclo de vida per-request).

7. Referencias

Los siguientes posts contienen más información al respecto:

  1. Entity Framework POCO (EF4): Generic Repository and Unit of Work Prototype. Este es el post que me ha servido de inspiración y de ayuda. Tiene una continuación (Unity Extension for Entity Framework POCO Configuration, Repository and Unit of Work) donde comenta el uso de Unity. Estos dos posts son de lectura imprescindible, aunque por un lado haya tenido que modificar algunas cosillas para que funcione con la CTP4 de EF Code first y por otro en el tercer post utilize una StaticFactoryExtension en lugar de una Injection Factory (supongo que usa Unity 1.2 que no soporta Injection Factories). Este tercer post utiliza una aproximación genial que es el uso de una extensión de Unity para configurar todos los mappings para los objetos de EF. Repito: lectura imprescindible.
  2. Los posts de Scott Guthrie sobre EF Code First:
    1. http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx
    2. http://weblogs.asp.net/scottgu/archive/2010/07/23/entity-framework-4-code-first-custom-database-schema-mapping.aspx
    3. http://weblogs.asp.net/scottgu/archive/2010/08/03/using-ef-code-first-with-an-existing-database.aspx

Un saludo!!! 🙂

ASP.NET MVC – Formato de salida según Content-Type

El otro día escribí un post donde vimos como mostrar una vista en PDF o HTML en función de una URL del tipo /controlador/accion(formato)/parámetros. El post estaba centrado básicamente en la tabla de rutas y cómo la URL clásica de ASP.NET MVC /Controlador/Accion/Parámetros no es una obligación sinó básicamente una convención.

Hadi Hariri realizó un comentario, muy interesante a mi jucio. Venía a decir que antes que añadir en la ruta el parámetro formato es mejor usar el campo Accept de la request. Copio literalmente: “La tercera opcion, que lo hace más transparente al usuario y además está en acorde a ReST, es la de usar las el ContentType en la petición, que es lo que yo normalmente hago.

Si quieres exponer una API lo más ReST posible en ASP.NET MVC y que tenga salidas en distintos formatos, sin duda deberías tener en cuenta la sugerencia de Hadi.

1. La cabecera de la petición http

Cuando un cliente envía una petición http a un servidor, que contiene una cabecera con varios parámetros. Dicha cabecera tiene varios campos que permiten especificar determinadas opciones que el cliente desea. Uno de esos campos es el campo Accept que permite indicar que formatos de respuesta acepta el cliente y en que orden.

P.ej. si hago una petición con Firefox a http://www.google.es, el contenido del campo Accept de la cabecera que firefox envia es (lo acabo de mirar con Firebug):

text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8

Que podríamos interpretar (más o menos) como: Mis formatos preferidos son text/html y application/xhtml+xml, si no puedes en ninguno de esos dos, envíamelo en application/xml y si no puedes, pues me tragaré lo que me mandes.

El valor exacto de dicha cabecera depende del browser… P.ej. IE8 para la misma peticion envia el siguiente valor en Accept (lo acabo de mirar con Fiddler):

image/jpeg, application/x-ms-application, image/gif, application/xaml+xml, image/pjpeg, application/x-ms-xbap, application/x-shockwave-flash, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*

Por lo tanto vemos como en el campo Accept el cliente nos dice que formatos de respuesta entiende (y en que orden los prefiere).

2. Acceso a la cabecera desde ASP.NET MVC

Imaginad que tenemos un controlador que puede devolver datos en dos formatos: XML y JSON. Y queremos usar el campo Accept de la cabecera http que envíe el cliente para devolver los datos en uno u otro formato.

Acceder a la cabecera http desde un controlador es extremadamente sencillo, usando Request.AcceptTypes, que es un array con todos los campos de la cabecera accept.

3. Devolver datos en formato XML

ASP.NET MVC no trae ningún mecanismo incluído para devolver datos en formato xml, lo que me va de coña para enseñaros como nos podemos crear un ActionResult propio:

public class XmlActionResult : ActionResult
{
private readonly object _data;
public XmlActionResult(object data)
{
_data = data;
}
public override void ExecuteResult(ControllerContext context)
{
XmlSerializer ser = new XmlSerializer(_data.GetType());
context.HttpContext.Response.ContentType = "text/xml";
ser.Serialize(context.HttpContext.Response.OutputStream, _data);
}
}

Crear un ActionResult propio es trivial: deriváis de ActionResult y implementáis el método abstracto ExecuteResult y en él hacéis lo que sea necesario (usualmente interaccionar con la Response). En este caso simplemente serializo el objeto que se le pasa con el serializador estándard de .NET. Ah si! Y pongo el content-type a text/xml que es el content-type usado para documentos en XML.

Yo suelo acompañar los ActionResults propios con un método extensor para los controladores, para llamarlos de forma similar a los ActionResults que vienen en el framework. Mi método extensor (trivial) es:

public static class ControllerExtensions
{
public static XmlActionResult Xml(this ControllerBase @this, object data)
{
return new XmlActionResult(data);
}
}

Y ahora ya puedo realizar la acción de mi controlador:

public ActionResult List()
{
var data = new GeeksModel().GetAllGeeks();
return Request.AcceptTypes.Contains("application/json") ?
(ActionResult)Json(data, JsonRequestBehavior.AllowGet) :
(ActionResult)this.Xml(data);
}

Simplemente pregunto si está el accept application/json(*) (que parece ser el content-type para JSON). Si lo está envío los datos en json y si no pues en xml! Si abrimos un navegador y vamos a /Geeks/List veremos los datos en XML porque ningún (bueno, ni FF ni IE que son los que he probado :p) envían application/json en el accept de la request.

(*) Ok, acepto que esta pregunta no es del todo correcta: debería mirar si application/json está preferido antes que text/xml (por si me manda ambos). Igual que teoricamente, debería comprobar si no me manda ninguno de los dos, y si es el caso devolver un error 406.

4. Un detallito final…

Bueno, eso parece funcionar, pero lo que chirría un poco es tener que meter este if() para comprobar en cada acción de cada controlador si la request contiene application/json o no y serializar el resultado en JSON o en XML.

Para evitar esto he encontrado dos alternativas en la red:

  1. Usar otro action result y que sea el action result quien decida si serializar los datos en XML o en JSON. Es decir, crearnos un JsonOrXmlActionResult, devolver siempre una instancia de este action result desde los controladores y en el ExecuteResult, preguntar por el campo accept y serializar en un formato en otro. Esta aproximación la he visto en el post “Create REST API using ASP.NET MVC that speaks both Json and plain Xml” del blog de Omar Al Zabir.
  2. Otra aproximación totalmente distinta (pero muy interesante) que usa un action filter para ello. Está en el blog de Aleem Bawany, en el post “ASP.NET MVC – Create easy REST API with JSON and XML”.

Os recomiendo la lectura de estos dos posts.

Un saludo y gracias a todos, especialmente a Hadi Hariri quien con su comentario anterior, ha motivado este post! 🙂

ASP.NET MVC: Mostrar datos en HTML o PDF (pero en el fondo vamos a hablar de la tabla de rutas).

El otro día, uno de los grandes de Webforms (al que, aún que a veces despotrique un poco, MVC le está empezando a gustar :P), publico un excelente artículo sobre como generar PDFs usando MVC. En el artículo Marc mostraba como mostrar datos de un PDF físico en disco o bien usando una vista parcial para generar PDFs al vuelo.

Su artículo me sirve de excusa perfecta para escribir un artículo sobre como podríamos aplicar su solución a un caso más general: vamos a ver como podemos mostrar una misma vista ya sea en formato HTML o bien en PDF. De nuevo, antes que nada leeros el artículo de Marc, ya que este está basado en aquel.

El objetivo que pretendemos es que dada una url, p.ej. http://host/geeks/list nos muestre la información en formato HTML o bien en formato PDF. La primera cosa a resolver es como indicar si queremos que el formato de salida sea pdf o html… Las dos maneras que quizá se os ocurran primero son:

  1. Añadiendo un parámetro de ruta, de forma que tendremos una URL tipo /geeks/list/pdf o /geeks/list/html. No me convence porque no queda claro que este parámetro no es de “negocio”. Me explico, una URL que para ver mis datos podría ser /geeks/view/pdf/edu. El parámetro ‘edu’ és claramente de negocio, pero el parámetro pdf, no…
  2. Añadiendo un parámetro en querystring, de forma que tendremos una URL tipo /geeks/list?format=pdf. No me convence porque aunque MVC se defiende bien con parámetros en querystring, realmente son totalmente innecesarios (y rompen la “amigabilidad” de las URLs que promulga MVC).

Un dia navegando por la MSDN vi otra opción que me pareció interesante. P.ej. si vais a http://msdn.microsoft.com/library/system.string(v=VS.90).aspx obteneis la información de string para el framework 3.5, mientras que si vais a http://msdn.microsoft.com/library/system.string(v=VS.80).aspx la que obteneis es la información para el framework 2.0. El parámetro (v=xxx) indica la versión para el que mostrar información.

Podemos conseguir fácilmente algo parecido en MVC? Pues… por supuesto! 😉

1. La tabla de rutas

La tabla de rutas es uno de los aspectos más desconocidos de ASP.NET MVC. Mucha gente asume que la convención de URLs /controlador/accion/id es una obligación, pero ni mucho menos: las URLs en ASP.NET MVC pueden tener cualquier forma y se definen usando la tabla de rutas. La forma /controlador/accion/id es sólamente la configuración estándard de la tabla de rutas (y digo estándard y no por defecto porque la tabla de rutas inicialmente está vacía, pero el wizard de VS nos genera el código para rellenarla usando dicha convención).

El código para configurar la tabla de rutas está en Global.asax.cs y el código que genera VS es tal y como sigue:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);

La primera sentencia es para que MVC ignore las rutas de tipo algo.axd/algomás (estas URLs pertenecen al sistema de trace de ASP.NET) y la segunda sentencia es la interesante. En ella se mapean las URLs con el formato /controlador/accion/id, y no sólo eso, sinó que se asignan valores por defecto (contrrolador vale Home, accion vale index y id es opcional y no tiene valor). Es por eso que la URL / en MVC equivale a /Home que equivale a /Home/Index.

Lo que ponemos entre llaves ({}) en la definición de la URLs son las variables de la tabla de rutas. El resultado de procesar una URL mediante la tabla de rutas es un conjunto de valores, que conocemos con el nombre de Route Values. Hay dos Route Values que el sistema MVC debe ser capaz de extraer de cada URL: controller y action. El resto de route values son pasadas como parámetros al controlador. Cuando usamos MapRoute para añadir una ruta a la tabla de rutas, le pasamos la mayoría de veces tres parámetros:

  1. Un nombre de ruta
  2. El formato de las URLs que acepta dicha ruta. Lo que esté entre llaves es variable y se mapeará a una route value
  3. El valor de las route values por defecto cuando no se puedan mapear a partir de la URL.

Las URLs que queremos obtener son URLs del siguiente tipo: /geeks/list(pdf) o /geeks/list(html). Obviamente quiero poder añadir parámetros después, es decir /geeks/list(pdf)/edu debe funcionar. Y también quiero que las URL clásicas (/geeks/list o bien /geeks/list/edu) funcionen bien (generando la salida en HTML).

La tabla de rutas que hace posible esas URLs es tal y como sigue:

routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"WithDevice", // Route name
"{controller}/{action}({device})/{id}", // URL with parameters
new { controller = "Home", action = "Index", device = "html", id = UrlParameter.Optional }
);

routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", device = "html", id = UrlParameter.Optional }
);

Fijaos en los dos MapRoute:

  1. El primero mapea URLs del tipo controlador/accion(formato)/param. Los paréntesis deben aparecer. Es decir una url del tipo /geeks/list(pdf)/edu será procesada por esa ruta y asignará los route values:
    1. controller = geeks
    2. action=list
    3. device=pdf
    4. id=edu
  2. Si una URL no tiene este formato (no tiene los paréntesis) no se procesará por la primera ruta y entrará por la segunda. Esta segunda ruta es la clásica de ASP.NET MVC. La única diferencia es que asigna el valor del route value a html. Así pues una url del tipo /geeks/list/edu será procesada por esta segunda ruta (y obtendremos los mismos route values que el caso anterior).

Ahora que ya podemos procesar las URLs que nos interesan, ya podemos generar la salida en formato PDF o HTML, según el valor del route value device.

2.El controlador

El controlador es muy simple. En el ejemplo sólo tengo una acción (List) que devuelve un listado de geeks. No me interesa el parámetro id, pero si el parámetro device (para saber si debo generar la salida en  pdf o html) así pues, declaro el controlador:

public ActionResult List(string device)
{
IEnumerable<GeeksViewModel> geeks = new GeeksModel().GetAllGeeks();
return "pdf".Equals(device, StringComparison.InvariantCultureIgnoreCase) ?
this.Pdf(geeks) : View(geeks);
}

Fijaos como la acción recibe el parámetro device, cuyo valor será el valor del route value device… Os he dicho que me encanta MVC? 🙂

Lo que hace el controlador es poca cosa: obtiene una lista de objetos GeeksViewModel y luego o bien llama a View() para pasar dichos datos a la vista, o bien llama a Pdf que es un método extensor que he hecho, que devuelve los datos en pdf.

El código del método extensor (totalmente basado en lo que publicó Marc) es el siguiente:

public static class ControllerExtensions
{
public static ActionResult Pdf(this ControllerBase @this)
{
return Pdf(@this, null);
}

public static ActionResult Pdf(this ControllerBase @this, object model)
{
byte[] buf = null;
MemoryStream pdfTemp = new MemoryStream();
ViewEngineResult ver = ViewEngines.Engines.FindView(@this.ControllerContext,
@this.ControllerContext.RouteData.Values["action"].ToString(), null);
if (ver.View != null)
{
if (model != null)
{
@this.ViewData.Model = model;
}
string htmlTextView = GetViewToString(@this.ControllerContext, ver);
iTextSharp.text.Document doc = new iTextSharp.text.Document();
iTextSharp.text.pdf.PdfWriter writer = iTextSharp.text.pdf.PdfWriter.GetInstance(doc, pdfTemp);
writer.CloseStream = false;
doc.Open();
AddHTMLText(doc, htmlTextView);
doc.Close();
buf = new byte[pdfTemp.Position];
pdfTemp.Position = 0;
pdfTemp.Read(buf, 0, buf.Length);
}
return new System.Web.Mvc.FileContentResult(buf, "application/pdf");
}

private static void AddHTMLText(iTextSharp.text.Document doc, string html)
{

List<iTextSharp.text.IElement> htmlarraylist = HTMLWorker.ParseToList(new StringReader(html), null);
foreach (var item in htmlarraylist)
{
doc.Add(item);
}

}

private static string GetViewToString(ControllerContext context, ViewEngineResult result)
{
string viewResult = "";
TempDataDictionary tempData = new TempDataDictionary();
StringBuilder sb = new StringBuilder();
using (StringWriter sw = new StringWriter(sb))
{
using (HtmlTextWriter output = new HtmlTextWriter(sw))
{
ViewContext viewContext = new ViewContext(context, result.View, context.Controller.ViewData, context.Controller.TempData, output);
result.View.Render(viewContext, output);
}
viewResult = sb.ToString();
}
return viewResult;
}
}

Las únicas diferencias respecto el código que puso Marc son:

  1. El uso de vistas completas (en lugar de una vista parcial)
  2. El soporte para pasar a la vista un modelo
  3. El uso del ViewData y del TempData que tenga el controlador (en lugar de crear un ViewData y un TempData vacío)
  4. Que la vista se selecciona a través del nombre de acción en lugar de ser una vista concreta.

El resto es tal y como estaba el código de Marc (porque ya os digo que yo iTextSharp no es que lo domine mucho :P).

Y el resultado? Pues si llamamos a /Geeks/List o /Geeks/List(html) tenemos:

image

Mientras que si llamamos a /Geeks/List(pdf) el resultado es:

image

3. Conclusiones

Lo interesante del post no es ver como generar PDFs (de eso ya se encarga el post de Marc), lo interesante es ver como gracias a la tabla de rutas podemos crearnos nuestras propias URLs de forma muy sencilla!

Un saludo a todos y gracias a Marc por el post que me ha dado la excusa para escribir este! 😉

Subir ficheros al servidor en ASP.NET MVC

Buenas! Hoy voy a responder alguna pregunta que me he encontrado en alguna vez, y es como subir ficheros al servidor usando MVC2.

La verdad es que con ASP.NET MVC2 subir ficheros al servidor es extremadamente simple. Vamos a empezar viendo el código de una vista que permite subir un fichero al servidor, junto con una descripción adicional. La vista básicamente contiene un  <form> como el siguiente:

<form action="<%: Url.Action("Upload") %>" enctype="multipart/form-data" method="post">
<label for="descripcion">Descripción del fichero:</label>
<input type="text" id="descripcion" name="descripcion" />
<br />
<label for="fichero">Fichero:</label>
<input type="file" name="fichero" size="40">
<br />
<input type="submit" value="Enviar" />
</form>

Fijaos que es HTML puro y duro, aunque el tag <form> lo podeis generar con Html.BeginForm() si queréis. La clave es añadir el atributo enctype con el valor multipart/form-data. Como se menciona en la especificación sobre formularios del W3C, el valor de multipart/form-data es el que debe usarse cuando se quieran enviar al servidor datos binarios.

El <input type=”file”> es el control HTML que nos permite seleccionar un fichero para enviar.

Y desde el controlador? Pues sencillo, en este caso mi formulario tiene dos parámetros (descripcion y fichero), por lo que necesitaré que la acción del controlador tenga esos dos parámetros. El parámetro descripcion es un string, pero el parámetro fichero… que és?

Pues bien ASP.NET MVC es capaz de ver que el parámetro fichero es un fichero que se ha subido al servidor y sabe mapearlo a un objeto de la clase HttpPostedFileBase. Esta clase nos da acceso no sólo al contenido del fichero subido, sinó a más información (su content-type, su tamaño, el path completo desde donde se ha subido,…).

El método del controlador queda pues, así de sencillo:

[HttpPost]
public ActionResult Upload(string descripcion, HttpPostedFileBase fichero)
{
fichero.SaveAs(Path.Combine(@"d:temp", Path.GetFileName(fichero.FileName)));
return View();
}

Fijaos en los dos parámetros string y HttpPostedFileBase. El método simplemente se guarda una copia del fichero subido en d:temp, pero obviamente aquí podéis hacer lo que queráis.

Y listos! No hay que hacer nada más… qué, sencillo, no??? 🙂

Un saludo

PD: Esta técnica no es ajax, eso significa que mientras se está subiendo el fichero al servidor, la aplicación web no responde (el browser está haciendo la petición). Existe un mecanismo para realizar subidas de ficheros en background, aunque no es directo debido a que con XMLHttpRequest (el objeto del naveagador que hace posible ajax) no se pueden subir ficheros. Si estáis interesados en el siguiente post de John Rudolf se muestra como realizar un upload de fichero en ajax usando jQuery y el form plugin!

Opinión: bool es sólo para true/false

Saludos a todos! Tanto a los que estéis trabajando, cómo aquellos que estando de vacaciones seais tan frikis que leais geeks.ms! 🙂

Hoy quiero hablar un poco sobre bool. Puede parecer un tipo de datos aburridote: a fin de cuentas sólo puede tener dos valores, pero precisamente ahí radica su gracia y de eso os quería contar. La idea del post es muy simple: bool es sólo para true/false.

Por ejemplo, en los arcanos tiempos en que un servidor usaba Visual C++ 6 para el desarrollo de aplicaciones windows, en las MFCs había un método muy divertido llamado UpdateData (que por lo que veo aún está). MFC tenía una cosa muy buena que era la posibilidad de realizar bindings entre variables de la clase que representaba la ventana (usualmente una clase derivada de CWnd) y los controles que contenía dicha ventana. Eso, ahora, puede parecer una chorrada pero por aquel entonces era una auténtica pasada.

El método UpdateData era el que se encargaba de realizar dicho binding. Se llamaba con un parámetro BOOL que si valía TRUE significaba que se pasaban los datos de los controles a las variables y si valía FALSE pues era al revés: se pasaban los datos de las variables a los controles. Eso lo acabo de leer ahora en la MSDN, pero cuanda usaba MFC lo tenía que leer a menudo: nunca me acordaba cual era el significado de TRUE y FALSE en este contexto… En el fondo el parámetro de dicha función no es true/false es “BindingDeControlesAVariables” o “BindingDeVariablesAControles”, es decir un enum con dos opciones.

Y a eso me refiero en este post: un enum con dos opciones no es un booleano, aunque en ambos casos tengamos sólo dos valores posibles. Establecer arbitrariamente un valor a true y otro a false sólo hace que la gente se confunda.

Que código creéis que es más legible?

Fichero.Abrir("foo.txt",true);

O bien:

Fichero.Abrir("foo.txt",ModoApertura.Lectura);

En el primer caso, el segundo parámetro es un bool que si vale true se abre el fichero para lectura y si es false, pues se abre para escritura. Pues vale, pero es una decisión arbitraria y cuando alguien deba usar Abrir probablemente deba consultar que hace exactamente este parámetro bool (y lo mismo si alguien revisa código).

Pero no sólo es por legibilidad de código… Podéis asegurar que dos opciones serán siempre dos opciones? Me explico, y esta vez, como nadie (ni mucho menos yo) está libre de pecado, con un ejemplo de cosecha propia.

En un framework que estamos desarrollando para permitir desarrollos de aplicaciones con ciertas características hay una propiedad de una clase (llamésmola Aplicacion) que indica si cuando se va a la pantalla anterior (las aplicaciones tienen navegación parecida a la de un browser) se debe destruir la vista de la cual se proviene o bien dicha vista se mantiene en memoria.

En su momento implementamos dicha propiedad con un bool (true = destruir la vista, false = no destruirla). Y estuvo bien… hasta cierto día.

Cierto día, nos vimos en la necesidad de que en ciertas aplicaciones la vista se debía destruir sólo si no contenía datos (en caso contrario se podía reaprovechar). A priori esto dependía de cada aplicación. Y ahí tuvimos el problema: como mapeamos esto en nuestra propiedad booleana? Porque ahora teníamos tres valores:

  1. No destruir la vista
  2. Destruir la vista siempre
  3. Destruir la vista sólo si no tiene datos

Nosotros solucionamos el problema añadiendo una segunda propiedad que fuese “que tipo de destrucción de vistas se quería”, y lo hicimos así, simplemente porque todas estas propiedades estaban serializadas en XML (en archivos que algunos ya estaban en producción), por lo que la propiedad original debía ser siendo bool. Total, ahora tenemos dos propiedades a establecer para un mismo comportamiento lo que es susceptible de errores y de confusión. Y todo por no haber usado en su momento un enum 🙂

Así pues, un consejo: cuando creeis propiedades bool, aseguraos de que realmente un bool es lo que necesitais y no un enum (aunque sea con dos opciones!).

Un saludo!!!!