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!