martes, 30 de marzo de 2010

[VS2008] Visual 2008 casca al adjuntarse a un proceso

Post rapidito… Me encontré que VS2008 me rebentaba al adjuntarme a un proceso para depurar. Aunque salía el mensaje de “Visual Studio ha causado un error y debe cerrarse”, dándole a OK, Visual Studio no se cerraba y realmente se adjuntaba al proceso… eso sí, todas las ventanas se iban a la posición que les daba la gana… lo cual da bastante por el saco si tienes que ir poniéndolas cada una a su sitio de nuevo…

En fin, si te ocurre esto o bien VS2008 te da errores cuando mueves y/o desacoplas ventanas, descárgate el patch que se encuentra en http://code.msdn.microsoft.com/KB960075 y listos.

Ah, por cierto, y si no te ha pasado nunca no te confíes… yo hasta hoy no lo había necesitado… En fin…

Saludos!

pd: Para variar… un crosspost desde mi blog en geeks.ms

sábado, 20 de marzo de 2010

Mi modesta comparativa entre PURE y jQuery-tmpl

Hola! En mis dos últimos posts he estado hablando un poco de PURE, una herramienta para generar código HTML a partir de una plantilla y un objeto Json. Hace poco Microsoft ha presentado jquery-tmpl, su propuesta (todavía abierta y en fase de discusión) para realizar exactamente lo mismo: generar HTML a partir de plantillas y json. Más detalles los podeis encontrar en este post de Stephen Walher.

Ni soy (ni me considero) un experto ni en PURE ni en javascript, pero me he permitido realizar una pequeña comparativa entre PURE y la propuesta de microsoft para templates, para ver en que se parecen y en que se diferencian. Esta es mi comparativa, sin duda limitada por mi propia falta de conocimientos, pero que comparto con vosotros por si os parece de interés.

Al final del post encontraréis en enlace con el código fuente del proyecto, que en este caso es un lector de los feeds de geeks.ms, implementado en ASP.NET MVC. Básicamente hay dos vistas: una que se genera usando PURE y otra usando jquery-tmpl.

No voy a comentar nada de la parte “servidor” del proyecto, puesto que teneis el códgo fuente en el zip (y obviamente si alguien tiene alguna duda puede contactar conmigo). Lo que quiero comentar es el código de generación usando PURE y jquery-tmpl.

1. Generación usando PURE

La generación usando PURE no tiene mucho secreto. La plantilla está definida de la siguiente manera:

<div id="pure">
<h1>
</h1>
<h2>
</h2>
<span class="t1" id="generator"></span>
<p class="feed">
<a></a>
<br />
<span></span>
</p>
</div>





Toda la vista está generada usando PURE, puesto que el objeto JSON tiene toda la información necesaria.



El código para generar el template, también es sencillo:




var directive = {
'h1': 'Channel.Title',
'h2': 'Channel.Description',
'span#generator': 'Powered by: #{Channel.Generator}',
'p.feed': {
'feed<-Channel.Items': {
'a': 'feed.Title',
'a@href': 'feed.Link',
'span': function(ctx) {
return stripped = ctx.item.Description.replace(/(<([^>]+)>)/ig, "").
substring(0, 350) + "...";
}
}
}
};

$("#pure").render(data, directive);





Como casi siempre, se declara una directiva que controlará el renderizado y finalmente se llama al método render.



Si echamos un vistazo a la directiva vemos una cosa que no había mostrado en los posts anteriores, y es la posibilidad de llamar a métodos javascript: en este caso en el span se colocará el resultado que devuelva el método anónimo declarado en la directiva. Cuando se llama a una función javascript desde un bucle, PURE le pasa un objeto que tiene, entre otras, la propiedad item que es el objeto correspondiente a la iteración que se está renderizando. El método es sencillo: lo que hace es eliminar el código HTML del feed y truncarlo a 350 carácteres.



2. Generación usando jquery-tmpl



Antes que me olvide: jquery-tmpl requiere jquery 1.4.2. Con la versión 1.4.1 (que es la que usa el generador de proyectos de ASP.NET MVC 2 RC2) no funciona.



A diferencia de PURE, jquery-tmpl opta por tener los templates dentro de un tag <script> cuyo type sea text/html. Este no es un type correcto para un tag <script> por lo que es ignorado por el naveagador. Eso hace que, a diferencia de PURE, los elementos que forman el template no pertenecen al DOM del documento, puesto que son ignorados. Podréis ver la diferencia ejecutando el proyecto y consultando primero los datos con PURE y luego con jquery-tmpl. En el primer caso vereis como el template, a pesar de ser elementos vacíos, es visible ya que tienen estilos de colores y borders. Esto tampoco es que represente más problema: generalmente usando PURE el template está oculto inicialmente y se muestra cuando ha terminado la generación (p.ej. usando el método toggle() de jQuery). Yo no le hecho adrede, para que veáis el efecto.



La gran diferencia de jquery-tmpl respecto PURE, es que no existe el concepto de directiva: el propio template contiene la directiva embebida en su interior. Para ello Microsoft ha recurrido a una sintaxis que nos resulte lo más familiar posible. La definición del template queda así:




<script id="template" type="text/html">
<h1>{%= Channel.Title %}</h1>
<h2>{%= Channel.Description %}</h2>
<span class="t1" id="generator">{%= Channel.Generator %}</span>
{% for (var i=0, l = Channel.Items.length; i<l; i++) { %}
<p class="feed">
<a href="{%= Channel.Items[i].Link %}">
{%= Channel.Items[i].Title %}
</a>
<br />
<span>{%= strip(Channel.Items[i].Description) %}</span></p>
{% } %}
</script>





Se puede observar el uso de la sintaxis {% y %}, que recuerda a la <% y %> usada en páginas aspx para el código de servidor. De esta manera {%= expr %} se traduce por el valor de expr donde expr se evalúa en el contexto del objeto json. Así {%= Channel.Description %} se traduce por el valor de la propiedad Description, de la propiedad Channel del objeto json.



También vemos el uso de {% y %} para iterar: Las etiquetas {% y %} nos permiten colocar código javasacript en nuestro template, en este caso un bucle for para iterar sobre todos los elementos de la colección Channel.Items.



Finalmente fijaos en la llamada a la función strip dentro del <span>. La función strip sirve para eliminar el código html del feed y truncarlo a 350 carácteres, y está definida dentro de un tag <script> en la propia página:




function strip(str) {
return str.replace(/(<([^>]+)>)/ig, "").substring(0, 350) + "...";
}





Hemos visto como definimos el template… y como lo aplicamos? Pues muy fácil, primeramente necesitamos el contenedor (o sea, el sitio donde se va a colocar el código HTML generado):




<div id="mstemplate">
</div>





Y una vez tenemos los datos en json, seleccionamos el template (usando un selector de jquery) y llamamos a su método render, pasándole los datos json y finalmente llamamos a appendTo para que el resultado se coloque dentro del contenedor que indiquemos.




$("#template").render(data).appendTo("#mstemplate");





3. Algunas conclusiones mías



La principal diferencia es que en PURE el template es un conjunto de objetos DOM conocidos por el navegador, mientras que en jquery-tmpl, el template es ignorado por el navegador ya que está dentro de un <script> cuyo tipo es “text/html”. En el apartado de discusiones del documento de propuesta de microsoft, se comenta el hecho que los templates no son objetos DOM para evitar efectos colaterales. Esto es cierto si se adopta la filosofía de que el template tenga la directiva embebida. Si mi template tiene algo como:




<img src="{%= ImageUrl %}" />





Si el template fuese un objeto DOM reconocido por el navegador, éste intentaría cargar la imagen “{%= ImagenUrl %}”, la primera vez. Esto es algo que NO podemos evitar haciendo que el template sea invisible. Es por ello que Microsoft opta por poner el template dentro de un tag <script> cuyo type sea “text/html” y de esta manera sea ignorado por el navegador.



En PURE no hay este problema, ya que la directiva está separada y además PURE puede crear atributos que no estén en el template si la directiva así lo indica. De este modo, el template para el caso anterior en PURE queda como:




<img />





Y la directiva asociada es la que crea el tag src dentro del tag <img>:




var directive = { 'img@src' : 'ImageUrl'};





De este modo el template puede ser un objeto DOM sin efectos colaterales.



No sé si el hecho de que los templates sean objetos DOM reales o no, tiene mucha importancia. Lo que si me hizo notar @tchvil es que con PURE la página sigue siendo xhtml, mientras que con jquery-tmpl no, por el uso de la sintaxis {% … %} y que eso puede tener su importancia en según que casos.



4. ¿Es necesario un estándard de templates en jquery?



Si no he entendido mal, la intención de microsoft con jquery-tmpl (que recuerdo está en fase de definición) es que se convierta en el método estándard de templates en jquery. Yo no se si es necesario que haya un mecanismo estándard de templates en jquery. Pienso que es bueno que el core sea lo más pequeño posible y que se use el mecanismo de plugins para ir enchufando las distintas funcionalidades. De este modo cualquiera podrá usar el mecanismo de templates que más le convenga en cada momento. Aunque en el documento se justifica la inclusión de un mecanismo estándard de templates en el core de jquery para que así si alguien quiere desarrollar un plug-in para jquery pueda usar templates sabiendo que estarán soportados…



Lo que sí me ha parecido ir leyendo en algunos posts, es que Microsoft va a ir abandonando su Ajax Library (me refiero a la parte de cliente, no a los controles ajax de asp.net) para ir centrando esfuerzos en jquery. Si realmente es así me parece una decisión excelente y que apoyo plenamente.



Como comenté al principio aquí tenéis el código del proyecto para que hagáis con él lo que queráis! (Está en mi skydrive).



Un saludo!

jueves, 11 de marzo de 2010

Lector de Twitter con ASP.NET MVC y Pure

Hola a todos! El otro día os comentaba lo mucho que me está gustando pure… Hoy, a modo de ejemplo, os presento como realizar fácilmente un lector de Twitter que muestre los últimos tweets…

El proyecto asp.net mvc

No voy a mencionar mucha cosa del proyecto asp.net mvc en general puesto que al final hay un enlace con todo el código. Consta de dos controladores:

  1. Home: Que tiene las acciones Index, Login y Tweets. La acción Index redirige a Login o bien a Tweets en función de si el usuario ha entrado o no login y password de twitter. La acción Login muestra un formulario de login y redirige a la acción tweets, que es la que hace el trabajo.
  2. La acción Tweets se limita a mostrar una vista que es la que hace gran parte del trabajo.

1. La vista Home/Tweets

Esta vista, hace una llamada ajax a la acción List del controlador Tweeter (nuestro segundo controlador). Esta acción debe devolver la información json con los últimos tweets del usuario. El código, es gracias a jquery, trivial:

$(document).ready(function() {
$.getJSON (
"<%= Url.Action("List","Twitter") %>",
function(val)
{
renderPure(val);
});
});





El método renderPure será el que usara Pure para mostrar los datos.



2. El controlador Twitter



Twitter dispone de una fantástica API REST con la cual podemos hacer casi cualquier cosa. Esta API está preparada para devolver datos usando distintos formatos, entre ellos json.



Algunas de las funciones de dicha API son públicas, pero otras (como la  de ver los últimos tweets) requieren que nos autentiquemos en twitter. Hay dos formas de hacerlo: usar OAuth o bien basic authentication.



OAuth es un protocolo de autenticación, pensado para integrar distintas aplicaciones, y una de sus ventajas es que el usuario no debe dar su contraseña en ningún momento, además de que puede granularizar sus permisos (puede dar permisos de lectura pero no de escritura p.ej.). Es sin duda el método recomendado para integrarnos con Twitter. Si usais cualquier cliente Twitter (web o escritorio) que no os haya pedido el password y que además hayais tenido que darle permisos desde la web de twitter, entonces este cliente está usando OAuth.



Basic authentication por otro lado es muy simple: se basa en poner una cabecera en la petición http, con nombre Authorization y cuyo valor sea Basic login:password, donde la cadena “login:password” está en Base64. Es un método totalmente inseguro (salvo que se uso junto con SSL) y además el usuario debe proporcionar su usuario y su contraseña al cliente (lo que bueno… mucha confianza no genera). Pero es muy sencillo de implementar, y es el que yo he usado.



El controlador Twitter tiene una sola acción List que realiza una llamada a la API REST de Twitter y pasa el resultado (json) tal cual:




public ActionResult List()
{
UserModel um = this.Session["user"] as UserModel;
string url = "http://api.twitter.com/1/statuses/friends_timeline.json";

// Realiza la petición a twitter
HttpWebRequest req = (HttpWebRequest)WebRequest.Create(url);
req.Headers.Add("Authorization", string.Format("Basic {0}", um.Base64Data));
req.Method = "GET";
HttpWebResponse res = (HttpWebResponse)req.GetResponse();

string data = null;
if (res.StatusCode == HttpStatusCode.OK)
{
using (StreamReader sr = new StreamReader(res.GetResponseStream()))
{
data = sr.ReadToEnd();
}
}

return !string.IsNullOrEmpty(data) ? (ActionResult)this.RawJson(data) : (ActionResult)View("Error");
}





Fijaos que el código es muy simple: Recojemos un objeto UserModel de la sesión. Ese objeto contiene el login y password de twitter del usuario. Con esos datos construimos una petición a la URL de la API REST de Twitter y añadimos la cabecera Authorization. Finalmente leemos el valor devuelto (una cadena json, salvo error) y lo mandamos tal cual.



El método RawJson es un método mío de extensión que devuelve un objeto RawJsonActionResult:




public static RawJsonActionResult RawJson(this Controller @this, string data)
{
return new RawJsonActionResult() { RawData = data };
}





La clase RawJsonActionResult es un ActionResult mío, que me permite devolver una cadena json. No uso el JsonResult de MVC porque ese está pensado para construir una cadena json a partir de un objeto .NET, y yo ya tengo la cadena json. Como podeis ver, el código es trivial:




public class RawJsonActionResult : ActionResult
{
public string RawData { get; set; }

public override void ExecuteResult(ControllerContext context)
{
var response = context.HttpContext.Response;
response.ContentType = "application/json";
response.Write(RawData);
response.Flush();
}
}





Quizá alguien se pregunta porque hago una llamada a un controlador mío (Twitter) que lo único que hace es consultar a la API REST de Twitter y mandarme el mismo resultado (sin tratar). La respuesta es para evitar problemas de cross-domain: de esta manera desde la vista Home/Tweets la única llamada ajax que hay es una llamada a otro controlador del mismo dominio, lo que seguro que no me va a generar ningún problema.



3. La vista Home/Tweets (ii)



Una vez la vista Home/Tweets recibe el objeto json (resultado de la llamada a la acción List del controlador Twitter), debemos generar el html necesario para “dibujarlo”… Aquí entra en acción PURE.



El objeto json que nos devuelve twitter es bastante grandote (aunque gracias a firebug se puede ver muy fácilmente). Por cada tweet yo voy a mostrar:




  1. El avatar de quien ha hecho el tweet


  2. El texto del tweet


  3. El nombre de usuario y el nombre real



Para ello usaré la siguiente plantilla:




<div id="purecontent" style="display:none">
<div class="tweet">
<img class="picprofile"></img>
<p>
<span id="text"></span>
</p>
<div class="userdata"><span id="user"></span> (<span id="realname"></span>)</div>
</div>
</div>





Dentro del div “purecontent” se generaran tantos div “tweet” como tweets haya… Veamos el código del método renderPure:




function renderPure(data)
{
var directive = {
'div.tweet' : {
'tweet<-': {
'span#text' : 'tweet.text',
'span#text@id+' : 'tweet.id',
'span#user' : 'tweet.user.screen_name',
'span#user@id+' : 'tweet.id',
'span#realname' : 'tweet.user.name',
'span#realname@id+' : 'tweet.id',
'img.picprofile@src' : 'tweet.user.profile_image_url'
}
}
};
$("#purecontent").render(data, directive);
$("#purecontent").toggle();
}





Lo más importante (como no) es la directiva, que le indica a pure como renderizar los datos a partir de la plantilla y el objeto json. En concreto esa plantilla dice lo siguiente:




  1. Por cada elemento del elemento json (tweet<-) haz lo siguiente:

    1. En el span cuyo id es text (span#text) pon el valor de la propiedad text del elemento.


    2. En este mismo span modifica su id para que el nuevo valor sea el id previo (text) más el valor de la propiedad Id del elemento.


    3. Haz lo mismo con los spans user y realname, pero usando las propiedades screen_name y name del objeto user que está dentro del elemento.


    4. Finalmente en el atributo src de la img cuya clase es picprofile (img.picprofile@src) coloca el valor de la propiedad profile_image_url del objeto user que está dentro del elemento.







Que… fácil, eh? Al principio igual cuesta acostumbrarse pero una vez se le pilla el truquillo es muy potente y natural.



Finalmente llamamos al método render de PURE y por último hacemos visible el div padre (purecontent) mediante el método toggle de jQuery…



Y este es el resultado (por cierto José M. Aguilar… ponte ya un avatar :p)



image



Os dejo un zip con el proyecto entero para que le echeis un vistazo.



Saludos!!!



PD: Esto es (como no) un crosspost desde mi blog en geeks.ms

martes, 9 de marzo de 2010

Trasteando con PURE…

Estos días he empezado a trastear con PURE (cuyas siglas significan PURE Unobstrusive Rendering Engine). El objetivo de PURE es proporcionar un mecanismo para transformar datos en JSON a HTML. Cada vez más existen multitud de servicios que devuelven datos en formato JSON, y cada vez más es normal consumir estos servicios desde aplicaciones web, via javascript. Si el resultado final es mostrar los datos debemos realizar una conversión a mano y generar usando javascript el HTML que deseemos. Esto es lento, tedioso y pesado.

PURE viene a ayudarnos en este punto: básicamente coje datos en JSON y usando una plantilla HTML, genera código HTML que luego “incrusta” en alguna parte del documento final… Además se integra con jQuery (y otras librerías javascript). Lo poco que he visto de PURE me ha encantado, así que quiero compartirlo con vosotros :)

1. Creación del proyecto web

Usando ASP.NET MVC 2 RC2 (ya lo teneis todos instalado, no?? ;-)) creamos un proyecto ASP.NET MVC vacío (ASP.NET MVC 2 Empty Web Application) y nos ponemos manos a la obra!

El primer paso es descargarnos PURE desde su página web. Tendremos un zip bastante grandote (algo más de 200 KBs), pero de todos los archivos que tiene, sólo nos interesa el pure.js dentro de la carpeta lib. Copiamos este archivo dentro de la carpeta scripts de nuestra aplicación MVC.

Vamos a crear ahora la master page de nuestro proyecto: no vamos a meter mucha cosa en la master page, simplemente vamos a incluir las dos librerias javascript que usaremos: jQuery y PURE. Para ello en View/Shared le damos a Add New Item y seleccionamos MVC 2 View Master Page le damos como nombre Site.master y le añadimos los tags <script> necesarios (dentro del head):

<script src="../../Scripts/jquery-1.4.1.js"></script>
<script src="../../Scripts/pure.js"></script>





Ahora vamos a crear un controlador que nos devuelva datos en JSON… Vamos a la carpeta Controllers, le damos a Add Controller y le damos un nombre (en mi caso HomeController). Esto nos creará el archivo HomeController.cs, con la clase HomeController con un método Index.



Vamos a crear ahora una clase cualquiera con datos que vamos a devolver. En la carpeta Models agregamos la clase TwitterUsers:




public class TwitterUser
{
public string Name { get; set; }
public string Twitter { get; set; }
}





Finalmente en el método Index de nuestro HomeController, creamos una Lista de TwitterUsers y la devolvemos:




public ActionResult Index()
{
var data = new List<TwitterUser> {
new TwitterUser() { Name="Eduard Tomàs", Twitter="eiximenis"},
new TwitterUser() { Name="José Miguel Torres", Twitter="alegrebandolero"},
new TwitterUser() { Name="Gisela Torres", Twitter="0GiS0"},
new TwitterUser() { Name="David Salgado", Twitter="davidsb"},
new TwitterUser() { Name="Toni Recio", Twitter="stormc23"},
};
return Json(data, JsonRequestBehavior.AllowGet);
}





Fíjaos en el uso de Json para devolver los datos en formato JSON y el parámetro JsonRequestBehavior para permitir devolver datos JSON usando GET (ver el post de José M. Aguilar para más información).







Si ponemos el proyecto en marcha y dirigimos Firefox a la URL /Home/Index veremos (gracias Firebug!) nuestros datos en JSON:



image



Es fácil mandar datos en JSON usando ASP.NET MVC, eh?? ;-)



2. Crear una vista para ver los datos



Vamos ahora a crear una vista para ver esos datos usando jQuery y PURE. Para ello primero debemos crear una acción en nuestro controlador Home que nos muestre la vista:




public ActionResult List()
{
return View();
}





Una vez hecho añadimos la carpeta Home dentro de Views y creamos una vista (Add View) llamada List.



Ahora nos toca añadir el código en la vista para:




  • Hacer una llamada AJAX a la url /Home/Index para obtener los datos en JSON


  • Usar PURE para mostrarlos



El primer punto es casi trivial gracias a jQuery. Añadimos el siguiente tag <script> justo antes del <h2>:




<script type="text/javascript">
$(document).ready(function() {
var url="<%= Url.Action("Index", "Home") %>";
$.getJSON(url, process);
});

function process(data)
{
// Código para procesar el resultado json
}
</script>





El método getJSON de jQuery es quien realiza todo el trabajo: Llama a una url usando AJAX y cuando la llamada devuelve llama a una función de callback (process).





Vamos ahora a usar PURE para convertir los datos en JSON a datos en HTML.



3. Usando PURE…



Para usar PURE necesitamos tres cosas:




  1. Unos datos en JSON (ya los tenemos!)


  2. Una plantilla HTML


  3. Unas reglas de conversión (directivas).



La plantilla HTML es simple y se coloca en la propia página, en el sitio donde queremos que se coloque el HTML generado por pure. En nuestro caso en la vista List:




<div id="puredata">
<ul>
<li></li>
</ul>
</div>





El div puredata es nuestra plantilla, en nuestro caso vamos a generar una lista (ul) de elementos (li) a partir de los datos JSON.



Ahora biene lo “bueno”… las reglas de conversión.



En PURE las reglas de conversión (directivas les llaman ellos) se especifican usando variables javascript que básicamente tienen este formato:




var directive={'selector' : 'valor'};





Donde selector es un selector (CSS) para seleccionar un elemento dentro de la plantilla y valor es un valor (propiedad) del elemento json. Nuestro caso es un poco más complejo, ya que queremos mostrar una lista de valores. En este caso debemos usar la sintaxis extendida de directivas:




var directive={
'selector' : {
'variable-loop<-coleccion json': {
directivas-del-loop
}
};





Escrito así parece un poco lioso, pero veamos un ejemplo de como sería nuestra directiva si lo que queremos es mostrar el nombre de nuestros usuarios de Twitter:




var directive = {
'li' :{
'user<-':{
'.': 'user.Name'
}
}
};





Si diseccionamos por parte la directiva:




  • user<- Significa que vaya iterando directamente sobre los elementos de los datos json (nuestro objeto json ya es por sí un array).


  • El operador punto (.) se refiere al propio elemento que se está generando.



Así estamos indicando que por cada elemento del array json genere un tag li y que coloque como texto del propio tag li el valor de la propiedad Name del elemento actual.



Finalmente sólo nos queda realizar la llamada para que PURE realice la generación del HTML… como PURE se integra con jQuery, eso es tan sencillo como:




$("#puredata").render(data, directive);





Con esto le decimos a PURE que use la plantilla dentro del div cuyo id es “puredata” y que la aplique a los datos indicados con las reglas que le decimos.



Y el resultado es el que esperamos:



image



Que… impresionante, eh??? :)



Otra demo… vamos a generar junto con el nombre, el enlace al twitter de cada persona.



Primero modificamos la plantilla para que quede de la siguiente manera:




<div id="puredata">
<ul>
<li><span></span> <a href="http://twitter.com/">Ver su twitter</a></li>
</ul>
</div>





El tag <span> contendrá el nombre y en el atibuto href del tag <a> vamos a añadir su nombre de usuario de twitter… La directiva que debemos usar es:




var directive = {
'li' :{
'user<-':{
'span': 'user.Name',
'a@href+' :'user.Twitter'
}
}
};





Con esta directiva le indicamos a PURE que: Por cada elemento del array json:




  1. Coja el tag <span> dentro del <li> y coloque el valor de la propiedad Name del elemento


  2. Coja el tag <a> dentro del <li> coja el valor del atributo href y le concatene (el + del final) el valor de la propiedad Twitter del elemento.



Y este es el resultado:



image



Impresionante… verdad?



Espero que el post os haya servido para ver un poco en que consiste PURE y el enorme potencial que atesora…



Os dejo el .zip con el proyecto final (en mi skydrive).



Un saludo!!!!



PD: Esto es un crosspost desde mi blog en geeks.ms!

jueves, 4 de marzo de 2010

Linq To SQL y Repository Pattern… sí, pero ojo!

Hola a todos! Hoy, por temas que no vienen al caso, estaba mirando el tutorial de MVC que hay en asp.net. Hay dos apartados dedicados a explicar como se pueden realizar modelos usando Linq to Sql y EF. Hasta ahí, ningún problema.

El problema viene, cuando en el apartado dedicado a Linq to Sql, una vez han dado un ejemplo de uso de las clases de Linq to Sql desde un controlador, dicen que esta solución, aunque correcta, implica que si en un futuro cambiamos el proveedor de acceso a datos vamos a tener que tocar todos nuestros controladores, ya que usamos las clases Linq to Sql desde ellos. También se menciona que el uso del patrón Repositorio (repository pattern) nos permite aislarnos de Linq to Sql de modo que si más adelante migramos, digamos a EF, no tengamos que modificar nuestros controladores.

Cuando usamos Linq to Sql se nos generan por un lado una clase que hereda de DataContext y que representa nuestra base de datos, y por otro un conjunto de clases que representan a nuestras tablas (nuestros datos). Esas clases no son POCO y están generadas y controladas por Linq to Sql.

Esta es el ejemplo que proporcionan en asp.net sobre el uso del patrón repositorio:

namespace MvcApplication1.Models
{
public class MovieRepository : IMovieRepository
{
private MovieDataContext _dataContext;

public MovieRepository()
{
_dataContext = new MovieDataContext();
}
public IList<Movie> ListAll()
{
var movies = from m in _dataContext.Movies
select m;
return movies.ToList();
}
}
}





El repositorio MovieRepository es quien nos da acceso a los datos contenidos en la base de datos y nos independiza de la clase Linq to Sql MovieDataContext. ¡Bien!



El ejemplo de uso que proporcionan desde un controlador es el siguiente:




public class MoviesController : Controller
{
private IMovieRepository _repository;
public MoviesController(IMovieRepository repository)
{
_repository = repository;
}
public ActionResult Index()
{
return View(_repository.ListAll());
}
}





Aprovechan además para comentarnos que el parámetro repository del constructor podría esta inyectado por un contenedor IoC (con lo que estoy totalmente de acuerdo). Luego nos enfatizan de que la dependencia de este controlador es solamente con IMovieRepository, con lo que por un lado podemos pasar un Mock de IMovieRepository cuando usamos tests unitarios y por otro si algún dia migramos a EF, nos basta con crear un EFMovieRepository y… listos.



Pues no.



Veis el problema? No? Y si cambio el código del método Index() por el siguiente código equivalente?




 public ActionResult Index()
{
IList<Movie> movies = _repository.ListAll();
return View(movies);
}





Detectais ahora el problema? El controlador MoviesController no depende de MovieDataContext, de acuerdo, pero sigue dependiendo de las clases generadas por Linq-to-Sql (en este caso de la clase Movie). Con el ejemplo que hay en asp.net tal y como está, de poco nos sirve implementar el patrón repositorio: cuando migremos a otro proveedor de datos (pongamos EF) vamos a tener que tocar igualmente todos los controladores.



Hay solución para el problema? Como (casi) todo en esta vida tiene solución, pues si que la hay: podemos forzar a Linq to SQL a que utilice clases POCO aunque perdemos algunas de sus capacidades (y por lo que he visto tampoco es que sea trivial). Existe una buena explicación al respecto en http://blogs.msdn.com/digital_ruminations/archive/2007/08/28/linq-to-sql-poco-support.aspx.



Si buscais “Repository Pattern” “Linq to Sql” en google encontrareis interesantes discusiones y posibles implementaciones al respecto, pero el objetivo de mi post no era tanto ofrecer soluciones al problema, si no hacer notar el pequeño problema en el ejemplo de asp.net y que, como siempre, no debemos creernos directamente todo lo que vemos.



Un saludo!



PD: Esto es un crosspost desde mi blog en geeks.ms!

viernes, 19 de febrero de 2010

Nombres de algunos controladores distintos en ASP.NET MVC (ii)

Bueno… Este post es la continuación / aclaración del post anterior. En el post anterior configuramos la tabla de rutas junto con un RouteHandler propio y vimos que realmente las llamadas se enrutaban al controlador que tocaba: /api/Foo me enrutaba al controlador WarFoo y /Foo me enrutaba al controlador Foo.

Lo que no comenté es lo que deja de funcionar… :)

Supongo que teneis la tabla de rutas de la siguiente manera:

routes.MapRoute("Default","{controller}/{action}/{id}", 
new { controller = "Home", action = "Index", id = "" });
routes.Add(new Route("api/{controller}/{action}/{id}",
new RouteValueDictionary(
new { controller = "Home", action = "Index", id = "" }),
new WarRouteHandler())
);





Suponemos también que tenemos el controlador Foo (al que queremos acceder vía (/Foo) y WarFoo al cual queremos acceder via /api/Foo). Si uso Html.ActionLink para generar los enlaces obtengo lo siguiente:



























Llamada URL obtenida URL que querria yo
Html.ActionLink("Texto", "XX","Foo") /Foo/XX /Foo/XX
Html.ActionLink("Texto", "XX", "WarFoo") /WarFoo/XX /api/Foo/XX


Vamos a ver como podemos empezar a arreglar este desaguisado… Debemos recordar que el orden de las rutas en la tabla de rutas afecta: toda URL se empieza a comprobar en cada ruta, y se usa la primera que encaje. Dado que no hay nada que impida en la ruta Default que haya un controlador llamado WarFoo se usa esa ruta, y por eso obtenemos /WarFoo/XX como URL final.



Uno puede pensar que la solució es invertir el orden de las rutas en la tabla de rutas… Si lo haceis el reultado no es mucho mejor:



























Llamada URL obtenida URL que querria yo
Html.ActionLink("Texto", "XX","Foo") /api/Foo/XX /Foo/XX
Html.ActionLink("Texto", "XX", "WarFoo") /api/WarFoo/XX /api/Foo/XX


Que nos ocurre? Lo mismo de antes, salvo que ahora la primera ruta que se evalúa es la que tiene las URLs que empiezan por api. Pero esta ruta tampoco impone ninguna restricción sobre los controladores, así cualquier nombre de controlador también encaja en esta ruta, y es por eso que obtenemos siempre URLs que empiezan por api.



Cuando tenemos dos URLs que ambas aceptan cualquier controlador y acción, es dificil que ActionLink pueda distinguir entre una u otra, así que generalmente nos usará la primera definida para generar los enlaces. Dado que por norma general no queremos poner /api/ en todas las URLs podemos dejar la ruta “Default” como la primera. Ahora entra en acción RouteLink: podemos usar RouteLink para generar las URLs que deben empezar con /api y ActionLink para las que no. P.ej. puedo usar la siguiente llamada RouteLink, para onbtener la url /api/Foo/XX:




Html.RouteLink("Texto", "api", new RouteValueDictionary(
new { controller="Foo", action="XX"}))





Aquí estoy generando un link a la ruta “api” para generar el enlace. Debemos modificar la tabla de rutas para que la ruta que genera las urls con /api/ se llame api. Esto es tan simple como poner el nombre de la ruta como primer parámetro del método Add:




routes.Add("api", new Route("api/{controller}/{action}/{id}",
new RouteValueDictionary(new { controller = "Home", action = "Index", id = "" }),
new WarRouteHandler())
);





y todo solucionado no??? :)



Pues no… todavía nos queda un fleco… un fleco que también pase por alto en el post anterior.



Los enlaces estan bien generados, uno apunta a /Foo/XX y el otro a /api/Foo/XX. El primero funciona bien pero el segundo da un error… y eso?



Pensemos de nuevo en como ASP.NET MVC evalúa las rutas: por orden. Y la pregunta es la ruta /api/Foo/XX se puede procesar con la ruta {controller}/{action}/{id} (la Default)? Pues sí, suponiendo que controller es “api”, action es “Foo” e Id es “XX”. Es decir la ruta /api/Foo/XX me intenta invocar la acción Foo del controlador Api, pasándole XX como Id.



¿Cual es la solución entonces? Pues añadir una restricción a la ruta (Default) que impida que el nombre de los controladores sea “api”. De este modo si {controller} debe tomar el valor api para satisfacer la ruta, como tenemos la restricción la ruta no será satisfecha y ASP.NET MVC intentará usar la siguiente. Las restricciones se añaden como un nuevo parámetro en MapRoute:




routes.MapRoute("Default", "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" },
new { controller = "restriccion" });






He añadido una restricción que afecta al parámetro controller. Y como se interpreta esta restricción. Pues bien, si es una cadena se interpreta como una expresión regular que debe cumplirse. Si la restricción no se puede (o no sabemos :p) expresar como una expresión regular podemos parar un objeto que implemente IRouteConstraint. Dado que yo soy muy torpe con las expresiones regulares, me he creado una clase que me comprueba que el valor no sea igual a un determinado valor:




public class NotEqualConstraint : IRouteConstraint
{
private string _match = String.Empty;
public NotEqualConstraint(string match)
{
_match = match;
}

public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return String.Compare(values[parameterName].ToString(), _match, true) != 0;
}
}





Finalmente coloco la restricción en la ruta:




routes.MapRoute("Default", "{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" },
new { controller = new NotEqualConstraint("api") });





Ahora sí que sí. Los enlaces /api/Foo/XX no pueden ser procesados por la ruta Default, ya que no se cumple mi restricción (controller vale api), y entonces son procesados por la siguiente ruta (que es lo que queríamos). Ahora pues la url /Map/XX es procesada por la primera ruta y la URL /api/Map/XX es procesada por la segunda y enrutada al controlador WarMap.



Espero que estos dos posts os hayan ayudado a ver la potencia del sistema de rutas de ASP.NET MVC!



Un saludo a todos!



PD: Esto es un crosspost desde mi blog en geeks.ms

Nombres de algunos controladores distintos en ASP.NET MVC

Hola! Un post para comentaros como he implementado una cosilla que necesitaba en ASP.NET MVC (v1). En concreto necesitaba mapear las URLs de tipo /api/{controller}/{action} al controlador especificado, pero con la salvedad de que el nombre del controlador empezaba por War. Es decir la URL /api/Foo/Index debía llamar a la acción del controlador WarFoo (en lugar del controlador Foo).

En resumen lo que quería era:

/Foo/Index –> Llamar a acción Index del controlador Foo

/api/Foo/Index –> llamar a acción Index del controlador WarFoo

/api/WarFoo/Index –> Llamar a acción Index del controlador WarFoo

La primera de las reglas se cumple fácilmente con la definición estándard de la tabla de rutas de MVC:

routes.MapRoute(
"Default",
"{controller}/{action}/{id}",
new { controller = "Home", action = "Index", id = "" }
);





Para dar soporte a las otras dos reglas me he creado un RouteHandler propio…



¿Que son los RouteHandlers?



Los RouteHandlers son clases que implementan la interfaz IRouteHandler y que son las encargadas de procesar las peticiones que vienen via una ruta (casi todas en el caso de MVC). Generalmente un RouteHandler lo que hace es crear un IHttpHandler que procese la petición enrutada. ASP.NET MVC viene con un RouteHandler por defecto (MvcRouteHandler) que termina creando un IHttpHandler por defecto (MvcHandler) que es el que crea el controlador asociado y le cede el control. En mi caso el comportamiento de MvcHandler ya me va bien: no quiero cambiar la política de creación de controladores ni nada, solo quiero añadir el prefijo “War” al controlador.



Por suerte, ASP.NET MVC es muy extensible y nos lo pone realmente fácil para añadir un RouteHandler propio: basta con crearlo y vincularlo con una ruta que tengamos en la tabla de rutas.



Veamos primero como vincularíamos nuestro RouteHandler (que he llamado WarRouteHandler) con una ruta, usando el método Add de la tabla de rutas:




routes.Add(new Route("api/{controller}/{action}/{id}",
new RouteValueDictionary(
new { controller = "Home", action = "Index", id = "" }),
new WarRouteHandler())
);





El primer parámetro es la URL de la ruta, el segundo los valores de la ruta (a diferencia del método MapRoute que acepta un objeto anónimo para esto, aquí nosotros debemos crear específicamente el RouteValuesDictionary) y finalmente el RouteHandler a utilizar!



Ya casi estamos: Sólo nos falta crear el RouteHandler, para ello creamos una clase que implemente IRouteHandler, y en el método GetHttpHandler (que es el único) vamos a añadir “War” al nombre del controlador. Para saber el nombre del controlador nos basta con acceder a los valores de la ruta (RouteData.Values) donde están todos (controlador, acción, id y otros adicionales que hubiesen). El código es simple:




public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
string controller = requestContext.RouteData.Values["controller"].ToString();
if (!controller.ToLowerInvariant().StartsWith("war"))
{
requestContext.RouteData.Values["controller"] = string.Format("War{0}", controller);
}

return new MvcHandler(requestContext);
}





Ahora ya tenemos las URLs de tipo /api/{controlador} enrutadas al controlador War{controlador}.



Un saludo!



PD: Crosspost desde mi blog en geeks.ms (para variar, pásate por allí mejor!)