ASP.NET MVC: Edición de colecciones usando Ajax

Buenas! El otro día me enviaron la siguiente duda:

Imaginate esto:

class Direccion{

string Calle {get;set;}

int Piso {get;set;}

}

class Cliente {

string Nombre {get;set;}

List<Direccion> Direcciones {get;set;}

}

Imagina que tiene mas propiedades cada clase pero para el ejemplo sirve.

Entonces tengo una Vista para definir la información de Cliente:

Donde van a aparecer los campos para rellenar el cliente. Dentro voy a tener un boton que va a ir agregandome vistas  parciales con la info de Direcciones.

Actualmente la parte de ediccion de Direcciones estaría en una Vista Parcial (con su correspondiente Form).

El problema que tengo es que necesito Enviar todo en el submit de Cliente y realmente es como si no tuviese la información de las direcciones.

Vale, de lo que se trata es que des de la vista para crear un Cliente, podamos añadir objetos Direccion, tantos como sea necesario, usando una vista parcial que iremos cargando via Ajax. El problema era que no se recibía la información de las direcciones.

Hace algún tiempo hablé de las peculiaridades del binding de colecciones en ASP.NET MVC (parte 1, parte 2 y parte 3). Si os miráis la primera parte de la serie, allí comentábamos que nombre debían tener los campos que se enlazaban a una colección. Básicamente si queremos enlazar una colección que está en la propiedad Direcciones de nuestro viewmodel, los campos deben llamarse Direcciones[0], Direcciones[1] y así sucesivamente.

Sabiendo esto, queda claro lo que hay que hacer:

  1. Una vista para crear un Cliente. Dicha vista contendrá un <div> en el cual iremos cargando las vistas parciales para crear direcciones
  2. Una vista parcial para crear un objeto direccion

Tan solo debemos tener cuidado de incrustar las vistas parciales dentro del <form> de la vista principal y de seguir la convención de nombres del ModelBinder. Asi pues, empecemos por la vista para crear un Cliente.

Partimos del código que nos genera VS al crear una nueva vista para crear objetos de tipo Cliente:

@model MvcAjaxCol.Models.Cliente

@{
    ViewBag.Title = "Nuevo cliente";
}
<h2>Nuevo Cliente</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script
@using (Html.BeginForm()) {
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Cliente</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Nombre)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Nombre)
            @Html.ValidationMessageFor(model => model.Nombre)
        </div>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

El propio VS no nos genera código para la propiedad “Direcciones” de la clase Cliente porque es una colección. Bueno, el siguiente paso es crear un <div> vacío donde colocaremos via Ajax las vistas parciales para crear objetos del tipo dirección. Por supuesto dicho <div> debe estar dentro del <form>. Luego añadimos un botón y cargamos una vista parcial nueva cada vez que se pulse el botón:

@model MvcAjaxCol.Models.Cliente

@{
    ViewBag.Title = "Nuevo cliente";
}

<script type="text/javascript">
    var idx = 0;
    $(document).ready(function() {
        $("#cmdNewDir").click(function() {
            var uri = "@Html.Raw(Url.Action("NewDir", "Home", new {prefix="Direcciones", idx="xxx" }))";
            uri = uri.replace("xxx", idx);
            idx++;
            $.get(uri, function(data) {
                $("#direcciones").append(data);
            });
        });
    });

</script>
<h2>Nuevo Cliente</h2>
<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"></script>
@using (Html.BeginForm())
{
    @Html.ValidationSummary(true)
    <fieldset>
        <legend>Cliente</legend>

        <div class="editor-label">
            @Html.LabelFor(model => model.Nombre)
        </div>
        <div class="editor-field">
            @Html.EditorFor(model => model.Nombre)
            @Html.ValidationMessageFor(model => model.Nombre)
        </div>
        <hr />
        <input type="button" value="Nueva Direccion" id="cmdNewDir"/>
        <div id="direcciones"></div>
        <p>
            <input type="submit" value="Create" />
        </p>
    </fieldset>
}

Como se puede a cada click del botón llamamos a una acción del controlador llamada NewDir. Esta acción es la que devuelve la vista parcial que añadimos al div:

public ActionResult NewDir(string prefix, string idx)
{
   ViewData["prefix"] = prefix;
   ViewData["idx"] = idx;
   return PartialView("Direccion");
}

Y finalmente la vista Direccion, usa los parámetros prefix y idx para ir generando campos cuyo nombre sea prefix[idx].XXX:

@model MvcAjaxCol.Models.Direccion
<div class="editor-label">
    @Html.LabelFor(model => model.Calle)
</div>
<div class="editor-field">
    @Html.Editor(string.Format("{0}[{1}].{2}", ViewData["prefix"], ViewData["idx"], "Calle"))
    @Html.ValidationMessage(string.Format("{0}[{1}].{2}", ViewData["prefix"], ViewData["idx"], "Calle"))
 
</div>

<div class="editor-label">
    @Html.LabelFor(model => model.Piso)
</div>
<div class="editor-field">
    @Html.Editor(string.Format("{0}[{1}].{2}", ViewData["prefix"], ViewData["idx"], "Piso"))
    @Html.ValidationMessage(string.Format("{0}[{1}].{2}", ViewData["prefix"], ViewData["idx"], "Piso"))
</div>

¡Y listos! Con esto ya podemos ir añadiendo tantas direcciones como sea necesario a nuesro cliente!

¡Un saludo!

ASP.NET MVC: Mandar un byte[]

Este post surge a raíz de la siguiente pregunta en los foros de ASP.NET MVC de MSDN: http://social.msdn.microsoft.com/Forums/es-ES/aspnetmvces/thread/20a6935c-5903-4efd-8ca1-f5a70a047a15. El usuario se pregunta como mandar un byte[] de la vista al controlador. Y comenta que lo hace de la siguiente manera:

<iframe src="<%: Url.Action("GenerarPdf", "Consulta", 
new { documento = Model.Documento})%>" width="725" height="725"></iframe>

En el controlador tiene definida la acción correspondiente con un parámetro llamado documento de tipo byte[]. Y comenta que siempre recibe el parámetro con el valor null.

Veamos porque ocurre esto y cual es la solución.

El porqué

Primero veamos que URL nos genera una llamada a Url.Action como la siguiente:

@{
    var data = new byte[] {0x10, 0x11, 0x12};
}
<script type="text/javascript">
    alert('@Url.Action("Index", "Home", new {documento=data})')
</script>

El código HTML generado es el siguiente:

<script type="text/javascript">
    alert('/?documento=System.Byte%5B%5D')
</script>

Qué ha ocurrido aquí? Pues que al intentar pasar un byte[] a través de la URL, el helper Url.Action ha llamado simplemente al método .ToString() de dicho byte[] (que siempre devuelve System.Byte[]). ¡Es evidente con esto que el Model Binder no va a poder recuperar los valores de dicho byte[]!

La solución

Lo que necesitamos pasar es el contenido del byte[], pero claro… en qué formato podemos hacerlo? Porque recordad que estamos pasando bytes, que es contenido binario, pero en las URLs (y en los cuerpos de las peticiones http) no hay contenido binario, hay texto (un subconjunto de ANSI). La mejor solución es usar Base64. Base64 es un mecanismo para codificar bytes (no carácteres, bytes)  a un subconjunto de carácteres ANSI.

En C# pasar un byte[] a su cadena Base64 equivalente es tan simple como llamar al método Convert.ToBase64String:

<script type="text/javascript">
    alert('@Url.Action("Index", "Home", new {documento=Convert.ToBase64String(data)})')
</script>

Ahora el código HTML generado es:

<script type="text/javascript">
    alert('/?documento=EBES')
</script>

La cadena EBES es la codificación en Base64 del array de bytes. Ahora nos toca recibir esto en el controlador. Primero voy a modificar la vista para que genere un enlace:

@{
    var data = new byte[] {0x10, 0x11, 0x12};
}
<a href="@Url.Action("Ver", "Home", new {documento=Convert.ToBase64String(data)})">Pulsar aquí</a>

Y ahora la acción Ver del controlador:

public ActionResult Ver(byte[] documento)
{
  // Código
}

Y esto es lo que recibimos en el controlador:

image

Como podemos ver ASP.NET MVC ha sido capaz de convertir automáticamentela cadena en formato BASE64 a un array de bytes!

Por supuesto aquí he usado @Url.Action para mandar el array de bytes a través de la URL, pero si usáis un @Html.Hidden para mandarlo a través de un campo Hidden os funcionará igual. La clave es que el byte[] esté codificado usando Base64.

Disclaimer

En mi post he usado MVC3, pero en el foro el usuario usaba MVC2. No tengo ahora una versión de MVC2 a mano para probar si esta conversión automática de cadenas Base64 a byte[] ocurre como en MVC3. Creo que sí (aunque debería comprobarlo) pero supongamos que no.

Supongamos que si mandas una cadena Base64 recibes un null si el parámetro es de tipo byte[]. En este caso… ¿qué deberías hacer?

Pues la solución pasa por crearte tu propio Model Binder. Este Model Binder es el que recibiría una cadena (en formato Base64) y la transformaría a un byte[]:

public class Base64ModelBinder : IModelBinder
{
    public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
    {
        var data = bindingContext.ValueProvider.GetValue(bindingContext.ModelName).AttemptedValue;
        var array = Convert.FromBase64String(data);
        return array;
    }
}

Este ModelBinder convierte el valor en Base64 a un array de bytes. Por supuesto faltaría añadir código de comprobación de errores.

Ahora simplemente debemos decirle a ASP.NET MVC que use este ModelBinder cuando se encuentre con un byte[]:

ModelBinders.Binders.Remove(typeof (byte[]));
ModelBinders.Binders.Add(typeof(byte[]), new Base64ModelBinder());

Fijaos que primero elimino el ModelBinder asociado a byte[] y luego asocio mi Model Binder al tipo byte[]. La primera línea es necesaria porque ASP.NET MVC3 ya tiene un Model Binder asociado a byte[] (lógico,simplemente MVC3 trae de serie lo que estamos comentando justo ahora): Concretamente uno llamado ByteArrayModelBinder (http://msdn.microsoft.com/en-us/library/system.web.mvc.bytearraymodelbinder.aspx).

Insisto en que creo que MVC2 lo tiene también, pero bueno, estamos suponiendo que no (en este caso la llamada  a Remove no sería necesaria claro).

Y listos, con esto ya podemos enlazar cadenas en Base64 a parámetros byte[].

Un saludo!

Lidiando con oAuth (1/n) – Pseudoautenticacion oAuth

Muy buenas! Este post es el primero de una serie de “n” donde veremos como podemos lidiar un poco con oAuth 1.0a. Vamos a ver como implementar un cliente y lo más interesante un proveedor.

Para seguir esta serie de posts recomiendo la lectura del documento “Entendiendo oAuth” que he dejado en Slideshare (http://www.slideshare.net/eduardtomas/entendiendo-o-auth) donde se describe brevemente el procolo oAuth y los distintos flujos asociados a él.

Comentaros también que he dejado en codeplex una librería que os va a permtir crear de forma extremadamente fácil un proveedor de oAuth para ASP.NET y también un cliente. Podéis encontrar la librería en https://epnukeoauth.codeplex.com/ (de momento está tan solo el código fuente, debéis compilarla vosotros mismos).

Breve, brevísima, introducción a oAuth

oAuth es un protocolo de autorización pensado especialmente para aplicaciones web, aunque su uso se ha extendido en aplicaciones móviles y de escritorio. Lo que permite es que una aplicación (cliente) pueda acceder a los recursos de un servidor (usualmente una API estilo REST) en nombre de un determinado usuario, sin que en ningún momento la aplicación cliente tenga acceso a las credenciales de dicho usuario. Para conseguir esto hay un juego a 3 bandas entre la aplicación cliente, el servidor y quien posee les credenciales del usuario (que sí, suele ser el servidor).

Pongamos twitter como ejemplo. Cuando instalas un cliente de twitter, este cliente debe conectarse a tu cuenta para poder leer tweets y enviar tweets en tu nombre. Para ello el cliente no te solicitará tus credenciales si no que en su lugar:

  1. La aplicación cliente hará una petición al proveedor de servicios pasándole su token de aplicación (que identifica a la aplicación cliente). Este es el inicio de todo, signfica que la aplicación “quiere empezar una negociación oAuth”.
  2. Si el proveedor de servicios acepta la aplicación le mandará un token temporal.
  3. La aplicación cliente abrirá una ventana de navegador hacia una URL de quien pueda autenticar el usuario. En este caso twitter. Y le pasará a twitter este token temporal, diciéndole “Hey! El proveedor de servicios me acepta como cliente (tengo este token que lo demuestra) pero necesito que el usuario se autentique).”
  4. El usuario hará login en twitter con tus credenciales. Insisto: en twitter. En ningún momento la aplicación cliente recibe las credenciales.
  5. Twitter validará las credenciales y si son correctas pedirá que confirmes que das acceso a la aplicación”XXX” a tu cuenta. Si confirmas, se genera un token, el cual es enviado (el mecanismo exacto no importa ahora) a la aplicación cliente.
  6. La aplicación cliente, usa este token para hablar con el proveedor de servicios diciéndole: “Hey! Tengo este token que me han dado con el cual se supone que tengo acceso a los servicios”. En el caso de twitter el proveedor de servicios es el propio twitter.
  7. El proveedor de servicios valida este token y si es correcto, le manda otro token al cliente. Este otro token es el que sirve (durante un tiempo determinado) para hacer llamados a los servicios en nombre del usuario que se autenticó. El resto de tokens pasan a ser inváldos.
  8. El cliente usa este segundo token para hacer llamadas al proveedor de servicios.

Este es el escenario más complejo de oAuth (el llamado 3-legged). En el fondo cada vez que hemos hablado de “token” hay realmente dos tokens (uno público que se manda junto con la petición y otro secreto que se usa para firmar digitalmente la petición).

El principal problema a la hora de crear un proveedor de servicios de oAuth es todo el tema de la firma digital, ya que se debe ser extremadamente cuidadoso en este punto. Probablemente el 90% de implementaciones fallidas de oAuth, fallan en este punto.

Pseudo Autenticación oAuth

Este sea probablemente uno de los usos más conocidos de oAuth, el de usarlo para “autenticar” un usuario (el famoso botón de sign-in with twitter p.ej.). Vamos a ver como podemos crear una web que tenga una sección privada a la que se pueda acceder a través de un botón de “sign with twitter”. Vamos a usar la librería que os comentaba antes para hacerlo.

La idea es acceder a una acción de un controlador que sea privada:

  1. public class PrivateController : Controller
  2. {
  3.     [Authorize]
  4.     public ActionResult Index()
  5.     {
  6.         return View();
  7.     }
  8. }

Fijaos que la acción está marcada como privada de la forma estándard en ASP.NET MVC (usando [Authorize]). Si intentamos navegar a /Private/Index al no estar autenticados en ASP.NET MVC seremos redirigidos a la vista de login:

image

La vista muestra un formulario de login estándard (eso seria para entrar sin usar twitter, vamos a obviar este punto) y un enlace para entrar usando twitter. Si miramos el código fuente de la vista /Views/Account/LogOn.cshtml el enlace para usar twitter llama a la acción LogOnTwitter del controlador Account. Esta es la acción que empieza todo el flujo de oAuth. Veamos primero su código:

  1. public ActionResult LogOnTwitter()
  2. {
  3.     var oauthSession = new OAuthClientSession(ConfigurationManager.AppSettings["consumer-key"],
  4.         ConfigurationManager.AppSettings["consumer-secret"],
  5.         new NonceGenerator32Bytes());
  6.     var uri = ConfigurationManager.AppSettings["request_token"];
  7.     oauthSession.RequestTemporaryCredentials(uri, "POST", "http://127.0.0.1:64983/Account/Callback");
  8.     Session["oauth"] = oauthSession;
  9.     return Redirect(oauthSession.GetAuthorizationUri(ConfigurationManager.AppSettings["authorize"]));
  10. }

Creamos un objeto de la clase OAuthClientSession. Esta clase está pensada para encapsular toda una sesión oAuth, desde el inicio hasta la obtención de los tokens finales.

El constructor de OAuthClienteSession (que se encuentra en Epnuke.OAuth) es:

  1. public OAuthClientSession(string ckey, string csecret, INonceGenerator nonceGenerator)
  2. {
  3.     _consumerKey = ckey;
  4.     _consumerSecret = csecret;
  5.     _nonceGenerator = nonceGenerator;
  6. }

Listemos brevemento los parámetros:

  • ckey: Es el consumer key. Este es la clave que te da el proveedor (en este caso twitter) cuando registras tu aplicación en twitter. Todas las aplicaciones que quieran usar oAuth deben ser registradas en twitter.
  • csecret: Es el consumer secret. Es la otra clave que te da el proveedor cuando registras tu aplicación.
  • nonceGenerator: Es el generador de nonces. El nonce es un parámetro que debe ser introducido en cada llamada oAuth y que básicamente “debe ser único”. La librería incluye un generador de nonces llamado NonceGenerator32Bytes. La especificación oAuth no dice NADA al respecto del formato que debe tener el nonce.

Una vez tenemos el objeto oauthSession creado llamamos al método RequestTemporaryCredentials y empezamos la danza de oAuth. Llamara este método hará lo siguiente:

  1. Creará una petición http oAuth. Una petición http es oAuth si tiene una cabecera “oAuth” con un formato determinado.
  2. Envia esta petición alproveedor de servicios. En el caso de twitter esto es la URL https://api.twitter.com/oauth/request_token. Esta petición servirá para indicar al proveedor de servicios que la “aplicación XXX quiere iniciar una sesión oAuth”.

Si miramos con fiddler, ahí podremos ver nuestra petición:

image

Los datos enviados son:

POST https://api.twitter.com/oauth/request_token HTTP/1.1
Authorization: OAuth oauth_consumer_key="dbMEy71w8pGLPpbmxlwg", oauth_signature_method="HMAC-SHA1", oauth_version="1.0", oauth_timestamp="1338538452", oauth_nonce="v1TsKDiFyVZNjQ9dfffWS3AMRsWsvM3iycTJilGMY", oauth_callback="http%3A%2F%2F127.0.0.1%3A64983%2FAccount%2FCallback", oauth_signature="%2BsRXDw7GrMcZZ4Xp%2Bvyf%2FVNnhaE%3D"
Host: api.twitter.com

Y los datos recibidos (elimino algunos no relevantes):

HTTP/1.1 200 OK
Date: Fri, 01 Jun 2012 08:14:17 GMT
Status: 200 OK
Content-Length: 147
Content-Type: text/html; charset=utf-8
Pragma: no-cache
ETag: "6f0566f3a5d5e338f2cefbc9bd1a7c1f"
X-Runtime: 0.01432
X-Transaction: 91371892a8469732
X-MID: 48cd92e044b19b57d1a6b5e471ccd03d2915ce48
Expires: Tue, 31 Mar 1981 05:00:00 GMT
Vary: Accept-Encoding
Server: tfe

oauth_token=P6xUReTuLm7cDxbSAbDZ8jUriKBIO3zvFfTemXc6VM&
oauth_token_secret=yJi3IP7CRts2o68Rl2CQOgNsjRKFvZ8pFxMPJ7TKozY&
oauth_callback_confirmed=true

Fijaos en los datos de la cabecera de la petición (en azul). Esos datos conforman la cabecera oAuth y se van enviando a cada petición. Fijaos la firma digital que se computa usando el consumer secret y en como hemos enviado el consumer key (para identificar la aplicación que hace la petición).

Bueno, ya tenemos la respuesta de twitter: nos ha mandado el primer token (como dije antes siempre hay uno público y el secreto que usaremos para firmar la petición): oauth_token y oauth_token_secret. Este token (oauth_token y oauth_token_secret) es el token que dice que “twitter ha aceptado la aplicación cliente XXX como un cliente oAuth válido)”.

Ahora debemos recojer el token (oauth_token) y mandarlo a quien posee las credenciales, en este caso el mismo twitter (la URL exacta es https://api.twitter.com/oauth/authenticate). Eso es lo que hace el Redirect() de la última línea de la acción LogOnTwitter. Y el resultado:

image

Fijaos en la URL: https://api.twitter.com/oauth/authenticate?oauth_token=P6xUReTuLm7cDxbSAbDZ8jUriKBIO3zvFfTemXc6VM

Hemos pasado el oauth_token que hemos obtenido en el punto anterior. De esta manera el autenticador (twitter) sabe que el proveedor de servicios (la api de twitter) sabe quien es esa aplicación que se autodenomina “Epnuke OAuth Library Test”).

Ahora entro las credenciales de twitter… y continúa el baile:

image

Las dos peticiones marcadas de amarillo son la petición que acabamos de realizar a api.twitter.com/oauth/authenticate (el Redirect() que teníamos) y otra petición a 127.0.0.1/Account/Callback

¿Quien hace esta petición? Pues bien, mi navegador claro pero ¿por que? Pues bien porque una vez me he autenticado twitter debe comunicar a mi aplicación que el usuario está autenticado. Y esto lo hace mandando un código HTTP de redirección hacia esta URL. Y porque esta URL, como la sabe twitter? Pues básicamente porque esta URL (la llamada URL de callback) se introduce cuando se registra la aplicación en twitter. Fijaos en un tema importante: esto tan solo es posible si la aplicación que se quiere conectar con twitter es capaz de exponer un endpoint http. Es decir es, básicamente, una aplicación web. En el caso de que no sea así, existe otro mecanismo (en otro post lo trataremos).

Sigamos… Twitter nos ha mandado via querystring un código, llamado oauth_verifier que sirve para indicarnos que el autenticador ha aceptado las credenciales del usuario. Veamos el código de la acción Callback:

  1. public ActionResult Callback(string oauth_verifier)
  2. {
  3.     var oauthSession = Session["oauth"] as OAuthClientSession;
  4.     oauthSession.Authorize(oauth_verifier, ConfigurationManager.AppSettings["access_token"]);
  5.  
  6.     if (oauthSession.IsAuthorized)
  7.     {
  8.         var name = oauthSession.GetAdditionalData("screen_name");
  9.         FormsAuthentication.SetAuthCookie(name, false);
  10.         return RedirectToAction("Index", "Private");
  11.     }
  12.     else
  13.     {
  14.         return RedirectToAction("Index", "Home");
  15.     }
  16.  
  17. }

Recuperamos el objeto oAuthSession (que habíamos guardado a la sesión), recogemos el oauth_verifier y llamamos al método “Authorize”. Dicho método hace lo siguiente:

  1. Creará una petición oAuth (con el header oAuth y la firma digital)
  2. Enviará dicha petición al proveedor de servicios. Para decirle: “Hey! El autenticador ha aceptado las credenciales del usuario y me ha devuelto este token. Puedes darme el token final? Gracias”.

Si seguimos mirando fiddler:

image

Fijaos como esta última petición NO ha hecho Chrome sino el servidor web (es la petición del método Authorize). De nuevo si miramos los datos de la petición:

POST https://api.twitter.com/oauth/access_token?oauth_verifier=heqcwkOPuopuMdsoqdMYP8My6kycqOuLYFSeIFK33w HTTP/1.1
Authorization: OAuth oauth_consumer_key="dbMEy71w8pGLPpbmxlwg", oauth_signature_method="HMAC-SHA1", oauth_version="1.0", oauth_timestamp="1338539804", oauth_nonce="NQZmiFzvGpaBlY2JW4DAhdL1RBJrGq7qmlygByoIc", oauth_token="P6xUReTuLm7cDxbSAbDZ8jUriKBIO3zvFfTemXc6VM", oauth_signature="kiAPOn%2F8Ybb5oDfd6aPqsG6M750%3D"
Host: api.twitter.com
Connection: Keep-Alive

Y los de la respuesta (quito datos no relevantes)

HTTP/1.1 200 OK
Date: Fri, 01 Jun 2012 08:37:03 GMT
Status: 200 OK
X-MID: ee5c8c5470911804d71f005a28c69a5be0b72cf4
ETag: "4521b768dcce87386275e193df917953"
Expires: Tue, 31 Mar 1981 05:00:00 GMT
Content-Length: 162
X-Frame-Options: SAMEORIGIN
Pragma: no-cache
Last-Modified: Fri, 01 Jun 2012 08:37:03 GMT
X-Runtime: 0.04706
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
Server: tfe

oauth_token=84274067-2TMYBrU2cH4x6B3zsYeKYOdCyOpCrxoxKeYZCuDQ&
oauth_token_secret=TYRlbDfgb6gVGLxcoVWBTuJyR60pwC3w6fvaq2aw9g&
user_id=xxxxxxxx&
screen_name=eiximenis

Bueno, hemos recibido otro par de tokens. Ese par de tokens son el par de tokens “final” y que nos van a permitir hacer llamadas oAuth a twitter en nombre del usuario. Además recibimos el ID interno del usuario y su nombre.

En este punto hemos terminado el baile de tokens oAuth. Ya tenemos los tokens finales para “hacer cosas” en nombre del usuario.

Pero nosotros no queríamos hacer nada en nombre del usuario, tan solo usar oAuth para autenticar el usuario. ¡Bueno, ningún problema! El resto de código de la Accion Callback es puro ASP.NET:

  1. Nos aseguramos que la respuesta de oAuth ha sido correcta y que el usuario está autorizado según oAuth (oauthSession.IsAuthorized)
  2. Recogemos el valor del parámetro screen_name (ese parámetro NO es estándard de oAuth, lo envía twitter)
  3. Llamamos a FormsAuthorization.SetAuthCookie para autenticar el usuario dentro de ASP.NET.

¡Y listos! ¡Hemos terminado!

image

¡Hemos conseguido autenticarnos a nuesta web usando las credenciales de twitter!

En posteriores posts iremos explorando como generar estas peticiones oAuth, siempre apoyándonos en el código fuente de la librería que he dejado en CodePlex.

Un saludo!

PD: Si os descargáis el código fuente de Codeplex está este ejemplo.

El misterioso caso de la ListBox con un solo elemento

Un post rapidito, para comentar algo que sucedió ayer…

Ayer por la tarde puse el siguiente tweet: http://twitter.com/#!/eiximenis/status/202060274260389888. Básicamente mostraba una ListBox en la cual tras añadirle un único elemento soltaba una OutOfMemoryException indicando que había demasiados elementos en la dicha lista:

As3cv7bCMAEOizK 

Vale que winforms tiene sus limitaciones, pero eso parece un poco excesivo, ¿no?

Mirando el valor de lstComandos.Count puedo ver que el elemento se ha añadido (antes de hacer el Add la lista estaba vacía) pero que después me lanza la excepción.

La propiedad InnerException está vacía:

image

Bueno… tras una rápida investigación (basada en F9 y F5) pude elaborar una suposición de lo que ocurría. El objeto que añadía a la lista era de una clase tal como la siguiente:

class Comando

{

    private readonly Guid _id;

    public Comando()

    {

        _id = Guid.NewGuid();

    }

    public Guid Id { get { return _id; } }

    public string Name { get; set; }

    public override string ToString()

    {

        return Name;

    }

}

Y como lo añadia a la lista:

lstComandos.Items.Add(new Comando());

Este Add ya daba la excepción antes mencionada.

¿Cuál es el verdadero problema? Pues simple y llanamente que el método ToString() (que es el que llama la ListBox para convertir los objetos de la clase Comando en una cadena para mostrar) devuelve null.

Basta con modificar el código del ToString:

public override string ToString()

{

    return Name ?? "Unnamed comamand";

}

Y todo pasa a funcionar a la perfección. 🙂

¿Moraleja final? Pues básicamente que si lanzas una excepción asegúrate de que es el tipo correcto de excepción. Porque de “demasiados elementos en la lista” y OutOfMemoryException nada de nada… 😉

Saludos!

Cargar información de reflection sin cargar el assembly

Bueno… veamos un post rapidito. En un proyecto en el que he participado hemos estado personalizando Visual Studio a través de varios custom editors, plugins, packages y demás fauna que pulula por la selva de extensibilidad de Visual Studio.

Estos editores, addines y demás necesitaban acceder a información de Reflection de la propia DLL que se estaba compilando. Teóricamente obtener la información es muy sencillo. Basta con obtener la ruta a la DLL que se está compilando:

private static EnvDTE.DTE DTE

{

    get { return (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE)); }

}

 

public static string ObtenerRutaEnsamblado()

{

    var project = DTE.ActiveDocument.ProjectItem.ContainingProject;

    return project.Properties.Item("LocalPath").Value.ToString() +

        project.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString();

}

 

public static string ObtenerNombreEnsamblado()

{

    var project = DTE.ActiveDocument.ProjectItem.ContainingProject;

    return string.Concat(ObtenerRutaEnsamblado(), project.Properties.Item("OutputFileName").Value.ToString());

}

El método ObtenerNombreEnsamblado da la ruta física de la DLL que se está compilando. A partir de aquí, debería bastar con usar LoadAssembly, para cargar la DLL y listos. Pero por supuesto, si esto fuese así, esta entrada del blog no existiría 🙂

El tema está en que cuando accedemos a un Assembly via Reflection, este assembly se carga en el CLR.  Y una vez un Assembly está cargado no puede ni cargarse de nuevo (para ver las modificaciones, por ejemplo, recordad que estamos cargando la propia DLL que el usuario está creando en VS) ni tampoco descargarse. Además el archivo físico se puede crear bloqueado (lo que en nuestro caso impedía que pudieses compilar el proyecto, ya que estaba bloqueado por el addin). Si alguno de vosotros está pensando en cargar el proyecto “solo para Reflection”, que se olvide. Cargar un assembly “solo para Reflection” lo carga igual y tampoco se puede ni cargar de nuevo ni descargar.

¿La solución? Bueno, pues utilizar un AppDomain nuevo. Para los que no lo sepáis los AppDomains son como “procesos” dentro del CLR. Un programa se ejecuta dentro de un AppDomain pero puede crear más AppDomains, del mismo modo que un proceso puede crear procesos hijos. Por supuesto la comunicación entre dos AppDomains se trata como comunicación interproceso: o a través de proxies (objetos MarshalByRef) o pasando objetos serializables. ¡Viva la vida!

Al final, terminé con una clase AppDomainUtils, con métodos estáticos parecidos a los siguientes:

    /// <summary>

    /// Carga el tipo TObj en un AppDomain nuevo.

    /// TObj DEBE ser MarshalByRef

    /// </summary>

    private static TObj LoadFromType<TObj>(AppDomain appDomain)

    {

        var tokens = typeof(TObj).AssemblyQualifiedName.Split(‘,’);

        var assName = tokens[1];

        var typeName = tokens[0];

        var obj = appDomain.CreateInstanceAndUnwrap(assName, typeName);

        return (TObj)obj;

    }

 

    /// <summary>

    /// Obtiene información (de tipo TR) de un System.Type.

    /// </summary>

    /// <typeparam name="TR">Tipo de información que se devuelve. Debe ser Serializable</typeparam>

    /// <typeparam name="TU">Tipo de la clase que extrae la información a partir del System.Type</typeparam>

    /// <param name="fullName">Nombre del System.Type a cargar (con assembly incorporado)</param>

    /// <param name="locationPath">Ruta fisica real del assembly</param>

    /// <returns>La información extraída del System.Type</returns>

    public static TR GetTypeInfo<TR, TU>(string fullName, string locationPath)

        where TU : TypeLoader

    {

        var appDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString());

        var tloader = LoadFromType<TU>(appDomain);

        var result = tloader.LoadTypeInfo<TR>(fullName, locationPath);

        AppDomain.Unload(appDomain);

        return result;

    }

}

La clase TypeLoader es como sigue:

/// <summary>

/// Carga información de un tipo.

/// </summary>

public class TypeLoader : MarshalByRefObject

{

    /// <summary>

    /// Carga el tipo y extrae la información

    /// </summary>

    public TR LoadTypeInfo<TR>(string fullName, string locationPath)

    {

        var type = Type.GetType(fullName);

        if (type == null)

        {

            var tokens = fullName.Split(‘,’).Select(x => x.Trim()).ToArray();

            var assFileName = tokens[1];

            var assFileNameWithExtension = string.Concat(assFileName.Trim(), ".dll");

            var assembly = AssemblyLoader.CargarAssemblyDesdeByteArray(Path.Combine(locationPath, assFileNameWithExtension));

            var typeName = tokens[0];

            type = assembly.GetTypes().FirstOrDefault(x => x.FullName == typeName);

        }

        return type != null ? (TR)Select(type) : default(TR);

    }

 

    /// <summary>

    /// Este método recibe un Type y debe devolver la info que se necesita de dicho Type.

    /// Este objeto DEBE ser serializable y debe ser una instancia (o casteable) de TR

    /// </summary>

    protected virtual object Select(Type type) { return null; }

La idea es cargar un System.Type, extraer información de él y devolverla. Evidentemente esto debe hacerse en un AppDomain nuevo. El método GetTypeInfo lo que hace es crear este AppDomain nuevo y luego, dentro de este AppDomain crear una instancia de un objeto propio, de un tipo cualquiera TU, pero que TU derive de TypeLoader. Y llama al método LoadTypeInfo de este objeto propio. El método LoadTypeInfo (definido en la clase TypeLoader) es el método que:

  1. Carga el assembly (usando un método propio que lo carga desde un array de bytes para asegurar que el fichero no se queda bloqueado. Simplemente lee todo el contenido del fichero en un byte[] y luego usa Assembly.Load pasándole este byte[]).
  2. Obtiene el tipo (System.Type) especificado.
  3. Llama al método Select que recibe un System.Type y debe devolver un objeto serializable con la información. Este objeto es el que se transmitirá al AppDomain principal (de ahí que deba ser serializable). Y no, System.Type no lo es.

El uso al final es bastante sencillo:

var data = AppDomainUtils.GetTypeInfo<TypeIdInfo, TypeIdInfoLoader>(tag.TypeName, OperativaReader.ObtenerRutaEnsamblado());

En la variable tag.TypeName está el nombre del tipo (full-qualified) a cargar. Al ejecutar esta línea en data tenemos un objeto de tipo TypeIdInfo que contiene la información que nos interesaba extraer del objeto System.Type. La clase TypeIdInfoLoader es la que transforma un System.Type en un TypeIdInfo:

class TypeIdInfoLoader : TypeLoader

{

    protected override object Select(Type type)

    {       

        var data = new TypeIdInfo() { FullName = type.FullName };

        return data;

    }

}

El código del méotdo Select de la clase TypeIdInfoLoader se ejecuta en el otro AppDomain, de ahí que deba devolver un objeto serializable (la clase TypeIdInfo debe estar marcada como tal).

En fin… comentar tan solo que todo este peñazo de usar AppDomains es porque los señores de Microsoft no han tenido a bien proporcionar una API que permite usar Reflection sin cargar la DLL dentro del CLR. Y no, lo siento, pero esta API no me sirve. Quiero algo que para usarlo no deba morir mil veces.

Saludos! 😉

[C# Básico] Métodos con parámetros variables

¡Hey! Dos entradas de la serie C# Básico en menos de un mes… ¿Señal de algo? Quien sabe… 😛

Antes que nada el aviso típico de esta serie: En esos posts exploramos elementos, digamos, básicos del lenguaje. No es un tutorial ni un libro ni nada. Cada post es independiente del resto y pueden ser leídos en el orden en que prefiráis… Dicho esto, al tajo.

Bueno, en este post veremos como funcionan los métodos con parámetros variables en C#. Cuando digo parámetros variables me refiero a que el número de parámetros es variable (o sea le puedes pasar 1 parámetro, ó 10 ó 100).

Primera opción: sobrecarga

Cuando aprendemos C# por norma general nos dicen que al definir un método debemos indicar que parámetros acepta, y que todos esos parámetros deben pasarse para invocar el método. Tranquilo, no te han engañado, es cierto. Todos sabemos que si tengo un método:

class Foo

{

    public void DoBar(int baz, string bazbar) { }

}

Si quiero invocar el método DoBar, debo pasarle los dos parámetros: un int y una cadena. No hay otra. O se hace o el código no compila.

Entonces… ¿como puedo hacer métodos con parámetros variables? Pues, la primera opción, muy usada, para proporcionar la ilusión de un número variable de parámetros (pues en este caso se trata de una ilusión) es aprovechar el mecanismo de sobrecarga. Sobrecargar un método significa que hay dos métodos métodos independientes pero que tienen el mismo nombre. El compilador tan solo pedirá una cosa: que la lista de parámetros sea distinta, o bien en número, o bien en el tipo de parámetros. Luego, cuando se llama el método, el compilador invocará al método que toque según el tipo y número de parámetros que se le pasen. Si alguien viene de C (pero no de C++) igual le sorprende esto, pero es realmente útil. Recuerdas toda aquella pléyade de funciones itoa, ltoa, ultoa y similares? Todas hacían lo mismo, convertir un número a cadena, pero claro la primera convertía un int, la segunda un long y la tercera un unsigned long. Esto mismo en C# se puede conseguir declarando tres funciones, pero todas ellas llamadas igual, p.ej. numberToString y teniendo cada una un tipo de parámetros variables. Así tenemos que recordar un solo nombre de método en lugar de n-mil.

El framework está lleno de métodos sobrecargados. ¿Un ejemplo? El método ToInt32 de la clase Convert:

image

Aquí lo veis, tiene 19 sobrecargas! Eso significa que hay 19 métodos llamados ToInt32 en la clase Convert. Insisto en que se trata de métodos independientes, si se quisiera el tipo de retorno podría ser distinto en cada caso y por supuesto podrían hacer cosas completamente distintas  (aunque hay que estar un poco mal de la cabeza para poner dos métodos que se llamen igual pero hagan cosas distintas :p).

Sobrecargando un método puedo dar la ilusión de que el número de parámetros es variable:

class Foo

{

    public void DoBar(int baz, string bazbar) { }

    public void DoBar(int baz) { }

    public void DoBar(string bazbar) { }

}

class Program

{

    static void Main(string[] args)

    {

        var foo = new Foo();

        foo.DoBar(10);

        foo.DoBar("edu");

        foo.DoBar(20, "edu");

    }

Bueno, esta técnica puede usarse para casos con pocos parámetros, pero en el fondo no tenemos realmente un “número de parámetros variable”, ya que no le puedo pasar 8 parámetros a DoBar.

Segunda opción: params

Antes que nada, a alguien se le ocurre un método en C# en el que se le pueda pasar un número arbitrario de parámetros (uno, diez o cien)? Pues hay varios, pero el más conocido es string.Format.

Seguro que todos habéis usado string.Format. Y sabéis que en función del primer parámetro, se le pasan luego tantos parámetros adicionales como sea necesario. La verdad es que string.Format tiene varias sobrecargas, pero te lo aseguro, no tiene todas las posibles. Entonces, ¿como lo hace? Pues usando la palabra clave params en C#:

class Foo

{

    public void DoBar(int baz, params string[] bazbar) { }

}

class Program

{

    static void Main(string[] args)

    {

        var foo = new Foo();

        foo.DoBar(10, "hola", "adios", "bye");

        foo.DoBar(20);

        foo.DoBar(10, "hi!");

    }

}

Este código compila y es perfectamente válido. El método DoBar tiene realmente dos parámetros: un int y un array de cadenas. Pero el uso de la palabra clave params permite pasar todas las cadenas del array no como un array sino como n parámetros separados por comas. Pero lo que DoBar recibe es un array de cadenas. Así el siguiente programa:

    public void DoBar(int baz, params string[] bazbar)

    {

        Console.WriteLine("# cadenas: " + bazbar.Length);

        for (int i = 0; i < bazbar.Length; i++)

        {

            Console.Write(bazbar[i] + ",");

        }

        Console.WriteLine();

    }

}

class Program

{

    static void Main(string[] args)

    {

        var foo = new Foo();

        foo.DoBar(10, "hola", "adios", "bye");

        foo.DoBar(20);

        foo.DoBar(10, "hi!");

    }

}

Genera la siguiente salida:

image

Así como podéis ver, realmente el método DoBar tiene dos parámetros: un entero que debo pasar siempre y un array de cadenas que el compilador me deja pasar como si fuesen N parámetros, pero que realmente es un array de N cadenas. De hecho, incluso eso es válido:

foo.DoBar(10, new string[] { "hola", "adios" });

En resumen, el uso de la palabra clave params me permite pasar un array como si fuesen N parámetros, pero no me obliga a ello (puedo seguir pasando el array como un array). Pero recordad: es un truco del compilador 😉

¡Ah, s¡! Y tened presente que:

  1. Tan solo puedo tener un parámetro params en un método…
  2. …Y debe ser el último

Por supuesto, si queréis que los “parámetros variables” puedan ser de cualquier tipo, podéis declarar que vuestro método recibe un params[] object:

class Foo

{

    public void DoBar(int baz, params object[] bazbar) { }

}

class Program

{

    static void Main(string[] args)

    {

        var foo = new Foo();

        foo.DoBar(10, "veinte", 20, new ClaseMia());

    }

}

En este caso, dentro de DoBar, bazbar es un array de objects con dos objetos (una cadena “veinte” y un objeto ClaseMia). Por supuesto que el método DoBar sepa de que tipo son los objetos que están dentro de bazbar es otra historia… 😉

Tercera opción (aunque no reconoceré haberlo dicho): __arglist

Aunque params por si solo ya es suficientemente interesante, vamos a hablar de una de esos pequeños aspectos de C# que son en general, desconocidos… Me refiero a __arglist (sí, con DOS subrayados delante, lo cual ya indica algo). El uso de esa palabra clave (pues es una palabra clave) significa “y a partir de aquí más argumentos”. En efecto, podemos declarar nuestro método DoBar:

class Foo

{

    public void DoBar(int baz, __arglist) { }

}

class Program

{

    static void Main(string[] args)

    {

        var foo = new Foo();

        foo.DoBar(10, __arglist("veinte", 20, 30, "bye"));

    }

Bueno, antes de continuar un par de cosas:

  1. Clarísimamente (como diría Cruyff) __arglist está pensada para que no la usemos. Es decir se trata de una palabra clave no documentada. La verdad es que funciona desde, al menos VS2005 y supongo que funcionará en todas las versiones de C#. Pero insisto: no está documentada, no es “oficial” (de ahí el “no reconoceré haberlo dicho” del título).
  2. Fijaos que debo invocar el método pasándole los parámetros opcionales usando también la palabra clave __arglist.

Ahora nos queda la cuestión final: como accede DoBar a esos parámetros opcionales. Eso no es como params que tenemos un array. No. Aquí si que tenemos un número arbitrario de parámetros de verdad. Pues nada, vamos a echar mano de la estructura ArgIterator:

public void DoBar(int baz, __arglist)

{

    var args = new ArgIterator(__arglist);

    while (args.GetRemainingCount() > 0)

    {

        Console.WriteLine("Param {0} valor {1}",

            Type.GetTypeFromHandle(args.GetNextArgType()).Name,

            TypedReference.ToObject(args.GetNextArg()).ToString());

    }

}

El uso de ArgIterator nos permite iterar sobre la lista de argumentos. Por cada argumento básicamente obtenemos:

  • Su valor, pero a través de un objeto TypedReference que se puede convertir a un object usando el método estático ToObject de la clase TypedReference.
  • Su tipo, pero a través de un objeto RuntimeTypeHandle que se puede convertir a un Type a través del método estático GetTypeFromHandle de la clase Type.

Probablemente estés pensando que no vale la pena usar __arglist, ArgIterator y todo este coñazo de TypedReference y RuntimeTypeHandle y tendrás razón. Por algo la palabra clave __arglist no está documentada. El hecho de que exista tiene que ver más con P/Invoke que no con una necesidad propiamente dicha del lenguaje.

Y bueno… eso es todo! 😛 Espero que os haya sido interesante… 😉

Saludos!

[C# Básico] Paso por referencia

¡Buenas! Este es un nuevo post de la serie C# Básico, que como su propio nombre indica trata sobre aspectos digamos elementales del lenguaje. Cada post es independiente y el orden de publicación no tiene porque ser el de lectura. Los temas los voy sacando de los foros o consultas que se me realizan 🙂

Hoy vamos a tratar un tema que veo que causa mucha confusión: el paso de parámetros por referencia. Como en todos los posts de esta serie lo haremos de forma didáctica y paso a paso.

1. Paso por valor

Para entender que es el paso por referencia, primero es necesario ver que significa el paso por valor. Que salida genera este programa?

static void Main(string[] args)

{

    var inicial = 10;

    Incrementa(inicial);

    Console.WriteLine("Valor DESPUES de incrementar es " + inicial);

}

static void Incrementa(int num)

{

    num = num + 1;

}

El sentido común dice que el programa imprimirá “Valor DESPUES de incrementar es 11”, pero la realidad es otra:

image

¿Como es posible esto? Pues porque la variable incial ha sido pasada por valor. Pasar una variable por valor significa hacer una copia de dicha variable (de ahí el nombre, ya que se pasa el valor y no la variable en sí). De este modo el parámetro num toma el valor de la variable inicial, es decir 10. Pero num es una copia de inicial, así que modificar num, no modifica para nada inicial. Al salir del método, efectivamente num vale 11 pero inicial continua valiendo 10 (además al salir del método la variable num es destruída ya que su alcance es de método).

2. Paso de objetos

Veamos ahora el siguiente código, donde en lugar de pasar un entero, pasamos un objeto de la clase Foo que tiene una propiedad entera:

class Foo

{

    public int Bar { get; set; }

}

 

class Program

{

    static void Main(string[] args)

    {

        var inicial = new Foo();

        inicial.Bar = 10;

        Incrementa(inicial);

        Console.WriteLine("Valor DESPUES de incrementar es " + inicial.Bar);

    }

    static void Incrementa(Foo foo)

    {

        foo.Bar = foo.Bar + 1;

    }

}

¿Cual es la salida del programa ahora? Si nos basamos en lo que vimos en el punto anterior deberíamos responder que va a imprimir “Valor DESPUES de incrementar es 10”, ya que el parámetro foo debería ser una copia de inicial y por lo tanto modificar foo no debe afectar para nada a inicial.

Pero la realidad es otra:

image

Pasar un objeto no crea una copia del objeto. Es por eso que decimos que los objetos no se pasan por valor, se pasan por referencia. Así pues modificar un objeto desde un método modifica el objeto original. No hay manera en C# de pasar una copia entera de un objeto entre métodos (a no ser que se haga manualmente).

Nota: En el código anterior, simplemente modifica “class” por “struct” cuando declaramos Foo. ¿Qué ocurre entonces? Pues que el programa ahora muestra “Valor DESPUES de incrementar es 10”. ¿A que es debido esto? Pues a que las estructuras se pasan por valor (¡es decir se copia su contenido!). Ver el punto (4) para más detalles.

Pero si nos quedamos en este punto obviamos una pregunta muy importante: Efectivamente los objetos se pasan por referencia… ¿pero las propias referencias como se pasan? Pues la respuesta es que las referencias se pasan por valor. Es decir la referencia foo es una copia de la referencia inicial. Pero copiar una referencia no es copiar su contenido (el objeto). Copiar una referencia significa que ahora tenemos dos referencias distintas que apuntan al mismo objeto. Por ello debemos tener muy claro que no es lo mismo modificar el contenido de una referencia (el objeto) que modificar la referencia misma. Si modificamos el contenido (es decir una propiedad del objeto apuntado, en este caso la propiedad Bar), este cambio es compartido ya que ambas referencias apuntan al mismo objeto. Pero si modificamos la referencia este cambio no será compartido:

static void Main(string[] args)

{

    var inicial = new Foo();

    inicial.Bar = 10;

    Incrementa(inicial);

    Console.WriteLine("Valor DESPUES de incrementar es " + inicial.Bar);

}

static void Incrementa(Foo foo)

{

    int valor = foo.Bar;

    foo = new Foo();

    foo.Bar = valor + 1;

}

Este código modifica la referencia foo. No modifica el contenido, modifica la referencia ya que asigna un nuevo objeto a la referencia foo. Al salir del método tenemos:

  1. Una referencia (inicial) que apunta a un objeto
  2. Otra referencia (foo) que apunta a un objeto nuevo
  3. El valor de la propiedad “Bar” del objeto apuntado por inicial es 10.
  4. El valor de la propiedad “Bar” del objeto apuntado por foo es 11.

Al salir del método la referencia foo se pierde, y su contenido (el objeto cuya propiedad Bar vale 11) al no ser apuntado por ninguna otra referencia será destruido por el Garbage Collector. Y efectivamente ahora la salida del programa es:

image

Si entiendes la diferencia entre modificar una referencia y modificar el contenido de una referencia, entonces ya estás listo para el siguiente punto…

3. El paso por referencia

En el punto anterior hemos visto que los objetos se pasan por referencia, pero las propias referencias se pasan por valor. Así que la pregunta obvia es: ¿hay alguna manera de pasar las referencias por referencia?

Y la respuesta es sí: usando la palabra clave ref:

static void Main(string[] args)

{

    var inicial = new Foo();

    inicial.Bar = 10;

    Incrementa(ref inicial);

    Console.WriteLine("Valor DESPUES de incrementar es " + inicial.Bar);

    Console.ReadLine();

}

static void Incrementa(ref Foo foo)

{

    int valor = foo.Bar;

    foo = new Foo();

    foo.Bar = valor + 1;

}

Fíjate que ref debe usarse tanto al declarar el parámetro como al invocar al método. El uso de ref significa que queremos pasar el parámetro por referencia. Si el parámetro es un objeto (como el caso que nos ocupa), ref no significa “pasar el objeto por referencia”, pues eso se hace siempre (como hemos visto en el punto (2)). En este caso ref significa “pasar la referencia por referencia”.

Es por ello que ahora foo y inicial son la misma referencia. Dado que son la misma referencia, forzosamente las dos deben apuntar al mismo objeto. Por ello cuando hacemos foo = new Foo(); estamos modificando la referencia foo haciendo que apunte a otro objeto distinto. Pero si foo y inicial son la misma referencia, al modificar foo modificamos inicial, por lo que ahora al salir del método Incrementa:

  1. La referencia foo apunta a un objeto nuevo cuya propiedad Bar vale 11.
  2. La referencia inicial, dado que es la misma que foo, apunta al mismo objeto.
  3. El antiguo objeto (el que su valor Bar valía 10) al no estar apuntado por ninguna referencia, será destruído por el Garbage Collector.

Por eso, ahora la salida del programa es:

image

4. Paso por referencia de tipos por valor

En terminología de .NET llamamos tipos por valor aquellos tipos que no son pasados por referencia. Así los objetos no son tipos por valor, ya que hemos visto en el punto (2) que se pasan por referencia. Pero p.ej. un int es un tipo por valor, ya que hemos visto en el punto (1) que se pasa por valor.

En general son tipos por valor todos los tipos simples (boolean, int, float,…), los enums, las estructuras (como DateTime). No son tipos por valor los objetos (¡ojo, que eso incluye a string!). Hay una forma sencilla de saber si un tipo es por referencia o por valor: si admite null es por referencia y si no admite tener el valor null es por valor.

Pues bien, ref puede usarse para pasar por referencia un tipo por valor, como p.ej. un int:

static void Main(string[] args)

{

    var inicial = 10;

    Incrementa(ref inicial);

    Console.WriteLine("Valor DESPUES de incrementar es " + inicial);

}

static void Incrementa(ref int valor)

{

    valor = valor + 1;

}

Como ya debes suponer ahora la salida del programa es:

image

En este caso la variable valor no es una copia de la variable inicial. Ambas variables son la misma, por lo que al modificar valor, estamos modificando también inicial. Por ello al salir del método, el valor de inicial sigue siendo 11.

5. Resumen

En resumen, hay cuatro puntos a tener en cuenta:

  1. Los tipos simples, estructuras y enums se pasan por valor (de ahí que digamos que son tipos por valor). Es decir, se pasa una copia de su contenido.
  2. Los objetos se pasan por referencia. Pero la referencia se pasa por valor, es decir el método recibe otra referencia que apunta al mismo objeto.
  3. La palabra clave “ref” permite pasar un parámetro por referencia.
    1. Si este parámetro es un tipo por valor se pasa por referencia, es decir no se pasa una copia sino que se pasa la propia variable.
    2. Si este parámetro es un objeto, lo que se pasa por referencia es la propia referencia.

¡Espero que este post os haya aclarado un poco el tema de paso por valor y paso por referencia! 😉

[Evento] ¡Geek-â-Paloozaaa en Andorra!

Yeah!!!

Nada, cuatro palabras para decir que este sábado 21 de Abril estaré en Andorra, en el primer Geek-â-Paloozaaa compartiendo charlas con otros auténticos monstruos.

Yo voy a hablar sobre async y await, lo que en el fondo me va a dar una excusa perfecta para aburriros a todos sobre el desarrollo de aplicaciones asíncronas en .NET y como ha ido evolucionando el tema a lo largo de las versiones del framework…

El bueno de Lluís, el alma mater de todo este meollo, ha tenido en consideración de colocarme en el último lugar de las charlas, justo cuando el hambre os asole y estéis por tanto despiertos y así obligados a escuchar mi charla!!! 🙂

Luego iremos a comer y por la tarde/noche un Geek-and-Beers en algún bareto de Andorra, donde supongo que podremos ver el clásico entre el Barça y el Madrid (¿eh Lluíiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiis?)… Bueno, si no podemos no pasa nada… mientras haya cervezas y buena compañía (me refiero a geeks, ¡malpensados!).

Si estás cerca de Andorra, pásate por donde se celebra el evento! Te aseguro que valdrá la pena (aunque solo sea para vernos con las camisetas que nos ha preparado el tito Lluís! :P).

Nos vamos a divertir! 😉

Webforms: Forzar postbacks

Jejejee… Sí, aunque no lo parezca a veces hago temillas con Webforms, y es que uno tiene que conocer al enemigo! 😛

Lo que voy a comentar hoy, es como forzar un postback desde un control propio. Una búsqueda en google da varios resultados, pongo un par de ejemplo:

  1. http://tratadooscuro.blogspot.com.es/2009/02/dopostback-ese-gran-desconocido.html
  2. http://programacion.porexpertos.es/provocar-un-postback-desde-javascript-con-aspnet/

Ambos ejemplos dicen lo mismo pero lo cierto es que, en mi opinión, hay una manera ligeramente mejor que hacerlo, pero parece que se desconoce bastante porque buscando en google aparecen menos resultados.

Bueno, si miráis los dos enlaces que he puesto arriba, forzar un postback desde un control propio es tan simple como llamar a __doPostback. Este método lo añade automáticamente Webforms cuando lo necesita.

Si seguimos las instrucciones de cualquiera de los dos enlaces anteriores, si queremos generar un control que sea p.ej. un enlace que al pulsarlo genere un postback vemos que debemos usar un código como este:

[DefaultProperty(«Text»)]

[ToolboxData(«<{0}:MyControl runat=server></{0}:MyControl>»)]

public class MyControl : WebControl

{

    [Bindable(true)]

    [Category(«Appearance»)]

    [DefaultValue(«»)]

    [Localizable(true)]

    public string Text

    {

        get

        {

            String s = (String)ViewState[«Text»];

            return s ?? string.Empty;

        }

 

        set

        {

            ViewState[«Text»] = value;

        }

    }

 

    protected override void RenderContents(HtmlTextWriter output)

    {

        output.AddAttribute(HtmlTextWriterAttribute.Href,

            «javascript:__doPostBack(»,»);»);

        output.RenderBeginTag(HtmlTextWriterTag.A);

        output.Write(Text);

        output.RenderEndTag();

    }

}

Si creamos una Webform vacío y añadimos el control y probamos la página, efectivamente se renderiza el tag <a> que incluye la llamada a __doPostBack. Pero fijaos que este es el código HTML generado por el Webform:

<!DOCTYPE html PUBLIC «-//W3C//DTD XHTML 1.0 Transitional//EN» «http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd»>

 

<html xmlns=»http://www.w3.org/1999/xhtml» >

<head><title>

 

</title></head>

<body>

    <form name=»form1″ method=»post» action=»default.aspx» id=»form1″>

<div>

<input type=»hidden» name=»__VIEWSTATE» id=»__VIEWSTATE» value=»/wEPDwUKLTk1OTYxMjkxMmRkGui/AV2Pv2fTTQJWmL8w2grZiZY=» />

</div>

 

    <div>

 

        <span id=»MyControl1″><a href=»javascript:__doPostBack(»,»);»>Demo</a></span>

 

    </div>

    </form>

</body>

</html>

¿Alguien ve el método __doPostBack? No, ¿verdad? Eso es porque Webforms no lo ha generado, y no lo ha generado porque no sabe que alguien va a usarlo. La verdad es que Webforms solo genera este método cuando sabe que algún control lo requiere. En páginas medio complejas la probabilidad de que algún control requiera postback es tan grande que por eso mucha gente cree que siempre se genera. Pero no es así.

Poner código “a saco” para llamar a __doPostBack funciona en la mayoría de casos pero no es una buena práctica porque estamos usando una característica interna de Webforms. Si en la siguiente release de Webforms Microsoft decide renombrar esta función todo nuestro código se viene abajo.

Vale… entonces si no podemos llamara a __doPostBack a saco, que debemos hacer? Pues usar el método GetPostBackClientHyperlink de la clase ClientScriptManager. Para obtener una instancia de ClientScriptManager se puede usar la propiedad ClientScript del objeto Page:

protected override void RenderContents(HtmlTextWriter output)

{

    output.AddAttribute(HtmlTextWriterAttribute.Href,

        this.Page.ClientScript.GetPostBackClientHyperlink(this, «»));

    output.RenderBeginTag(HtmlTextWriterTag.A);

    output.Write(Text);

    output.RenderEndTag();

}

¡Listos! Ahora si ejecutamos de nuevo el Webform, vemos que el código generado es:

<!DOCTYPE html PUBLIC «-//W3C//DTD XHTML 1.0 Transitional//EN» «http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd»>

 

<html xmlns=»http://www.w3.org/1999/xhtml» >

<head><title>

 

</title></head>

<body>

    <form name=»form1″ method=»post» action=»default.aspx» id=»form1″>

<div>

<input type=»hidden» name=»__VIEWSTATE» id=»__VIEWSTATE» value=»/wEPDwUKLTk1OTYxMjkxMmRkGui/AV2Pv2fTTQJWmL8w2grZiZY=» />

</div>

 

<div>

 

    <input type=»hidden» name=»__EVENTTARGET» id=»__EVENTTARGET» value=»» />

    <input type=»hidden» name=»__EVENTARGUMENT» id=»__EVENTARGUMENT» value=»» />

</div>

    <div>

 

        <span id=»MyControl1″><a href=»javascript:__doPostBack(‘MyControl1’,»)»>Demo</a></span>

 

    </div>

 

<script type=»text/javascript»>

//<![CDATA[

var theForm = document.forms[‘form1’];

if (!theForm) {

    theForm = document.form1;

}

function __doPostBack(eventTarget, eventArgument) {

    if (!theForm.onsubmit || (theForm.onsubmit() != false)) {

        theForm.__EVENTTARGET.value = eventTarget;

        theForm.__EVENTARGUMENT.value = eventArgument;

        theForm.submit();

    }

}

//]]>

</script>

 

</form>

</body>

</html>

Por un lado ahora Webforms nos ha generado el __doPostBack y por otro como podemos ver el el atributo href de nuestro enlace tenemos la llamada a doPostBack correcta (por norma general el primer parámetro es el ID del control que realiza la llamada).

Y ya que sabemos generar PostBacks… ¿como recibirlos?

Hemos visto como generar un PostBack desde nuestro control, ahora… como podemos decirle a Webforms que nuestro control quiere enterarse de los postbacks que él haya efecutado?

Pues para esto basta con implementar la interfaz IPostBackEventHandler que tiene un solo método:

public interface IPostBackEventHandler

{

    void RaisePostBackEvent(string eventArgument);

}

Esta interfaz nos permite hacer algo cuando hay un postback generado por el propio control. Por norma general lo que se hace es lanzar un evento de .NET (p.ej. Click). Vamos a ver un ejemplo muy rápido. Para ello añadimos un evento Click a nuestro control:

public event EventHandler Click;

protected virtual void OnClicked()

{

    var handler = Click;

    if (handler != null) handler(this, EventArgs.Empty);

}

Ahora implementamos la interfaz IPostBackEventHandler y el método RaisePostBackEvent:

public void RaisePostBackEvent(string eventArgument)

{

    OnClicked();

}

Cuando ahora se pulse nuestro enlace se generará un evento Click que podemos capturar desde el Webform, como cualquier otro evento.

Además fijaos que RaisePostBackEvent recibe una cadena (eventArgument). El valor de esta cadena es el valor del segundo parámetro de GetClientPostBackHyperlink que hemos usado para generar la llamada a __doPostBack. De esta manera podemos crear controles que lancen varios eventos en función del valor de eventArgument.

Un saludo!

Catálogo en ASP.NET MVC3 sin usar BBDD

Bueno… este es un post por “encargo”… Hoy he recibido un tweet de @JanoRuiz que decía lo siguiente: Hola, Saludos, una Consulta, Como Hacer Un Catalogo En asp.net mvc3 Sin Usar BD, Hacer Altas, Bajas y Modificaciones.

Bueno, vamos a explorar algunas “formas de hacerlo”… 😀

Vamos a utilizar el siguiente modelo para representar los productos:

  1. public class Producto
  2. {
  3.     public string Nombre { get; set; }
  4.     public int Precio { get; set; }
  5. }

Y el catálogo en sí:

  1. public class Catalogo
  2. {
  3.     private readonly string _nombre;
  4.     private readonly List<Producto> _productos;
  5.  
  6.     public Catalogo()
  7.     {
  8.         _productos = new List<Producto>();
  9.     }
  10.  
  11.     public IEnumerable<Producto> Productos
  12.     {
  13.         get { return _productos; }
  14.     }
  15.  
  16.     public void AnyadirProducto(Producto p)
  17.     {
  18.         _productos.Add(p);
  19.     }
  20. }

Opción 1. Usar la sesión

Bueno, la opción más sencilla es usar la sesión para mantener nuestro catálogo. Para ello directamente creamos el catálogo cuando se crea la sesión, añadiendo el siguiente código en global.asax.cs:

  1. protected  void Session_Start()
  2. {
  3.     Session["catalogo"] = new Catalogo();
  4. }

La sesión se crea en la primera petición que realiza el usuario a nuestra web y está vinculada al usuario (usuario en este contexto = ventana de navegador, por norma general).

Ahora cuando queramos acceder al catálogo:

  1. [HttpPost]
  2. public ActionResult Nuevo(Producto producto)
  3. {
  4.     var catalogo = Session["Catalogo"] as Catalogo;
  5.     catalogo.AnyadirProducto(producto);
  6.     return RedirectToAction("Index");
  7. }

Y listos!

Un par de consideraciones sobre el uso de sesión:

  1. No es persistente. Es decir, cuando el usuario cierre la ventana del navegador y vuelva más tarde a nuestra web, habremos perdido los datos. Si queremos hacer los datos persistentes hay que guardarlos en algún almacenamiento persistente, esto es… una base de datos 😛 (aunque podría ser un fichero en el servidor también).
  2. Se crea incluso si el usuario no está autenticado. Si queremos que tan solo los usuarios autenticados tengan catálogos, podemos crear el catálogo justo cuando autenticamos al usuario (y por supuesto tener protegidos con [Authorize] las acciones correspondientes de los controladores que acceden a la sesión).

Por lo tanto, la sesión nos evita el uso de una BBDD solo mientras el usuario navega por nuestra aplicación, pero si queremos que sea persistente deberemos usar un almacenamiento externo que persista (o sea, una BBDD).

Opcion 2: Serializar los datos en cada petición

De acuerdo, la sesión la podemos usar pero es un recurso digamos… caro. Tiene ciertas implicaciones en escalabilidad y tolerancia a fallos así que quizá no queremos o podemos usarla. Hay alguna manera de mantener el estado como si tuviéramos sesión? Pues sí, serializar los datos de la sesión en cada petición y colocarlas en un campo hidden. ¿Te suena a algo esto? ¿Cómo? ¿Quien ha dicho viewstate? Premio!

Vamos a ver una prueba de concepto de esto. Para empezar necesitamos una clase que dado un objeto nos devuelva su representación en Base64 y vicerversa:

  1. public class ObjectToBase64
  2. {
  3.  
  4.     public static string ToBase64(object source)
  5.     {
  6.         var base64 = string.Empty;
  7.         if (source != null)
  8.         {
  9.             using (var ms = new MemoryStream())
  10.             {
  11.                 var formatter = new BinaryFormatter();
  12.                 formatter.Serialize(ms, source);
  13.                 var buffer = ms.ToArray();
  14.                 base64 = Convert.ToBase64String(buffer);
  15.             }
  16.         }
  17.         return base64;
  18.     }
  19.  
  20.     public static object FromBase64 (string base64)
  21.     {
  22.         if (string.IsNullOrEmpty(base64)) return null;
  23.         var buffer = Convert.FromBase64String(base64);
  24.         using (var ms = new MemoryStream(buffer))
  25.         {
  26.             var formatter = new BinaryFormatter();
  27.             return formatter.Deserialize(ms);
  28.         }
  29.     }
  30. }

La idea ahora es que:

  1. Cada acción tiene un parámetro adicional (p.ej. llamado _viewstate).
  2. Este parámetro debe ser pasado en todas las llamadas, ya sea via GET o via POST.

Así, si p.ej. en una vista tenemos un formulario, podemos hacer:

  1. @if (ViewBag.ViewState != null)
  2. {
  3.     <input type="hidden" name="_viewstate" value="@ViewBag.ViewState"/>
  4. }

Y si generamos un enlace:

@Html.ActionLink("Nuevo", "Nuevo", new {_viewstate = ViewBag.ViewState})

Del mismo modo en el caso de una redirección de un controlador a otro:

return RedirectToAction("Index", new { _viewstate = ObjectToBase64.ToBase64(catalogo) });

Bueno… creo que la idea se entiende, no? 😉

En el fondo lo que estamos haciendo es serializar la “sesión” cada vez y mandarla arriba y abajo. Hacerlo via GET (como en el ActionLink o en RedirectToAction) hace que esta sesión sea visible en la URL:

image

Lo ideal es usar siempre POST (que es lo que hace Webforms). Esta técnica se puede mejorar bastante, pero bueno… como prueba de concepto no está mal. Si alguna vez queréis usar esta técnica echad un vistazo al post del Maestro: http://www.variablenotfound.com/2010/07/un-viewstate-en-aspnet-mvc.html

He de decir que a mi esta técnica no me gusta nada, salvo que no sea para casos muy, muy, muy puntuales. Y por supuesto tiene el mismo “problema” que la sesión: no es persistente.

Y así llegamos a la opción 3…

Opción 3 – LocalStorage

Bueno… en la Opción 1 se trata de mantener el estado en el servidor y la Opción 2 mantiene el estado en el cliente pasándolo a través de campos hidden o en la URL. Ahora toca la tercera opción: mantener el estado en el cliente. No solo eso, sino que además lo haremos persistente. ¿Y eso como? Pues… usaremos un almacenamiento persistente… En este caso el Local Storage.

Local Storage es básicamente un almacén de objetos en el navegador. Llámalo base de datos si quieres, pero ten presente que no hay tablas, ni filas, ni columnas. Se trataría más bien de un diccionario (clave, valor). ¿Y qué guardas? Pues cadenas. Simple y llanamente cadenas.

La idea es la siguiente:

  1. El catálogo se guarda en el local storage del cliente
  2. El cliente añade el producto en el almacén

Todo se realiza en cliente, no hay comunicación en el servidor. Evidentemente en una aplicación real antes de añadir el producto en cliente, este preguntaría al servidor (mediante una llamada ajax) si puede añadirlo. Pero en nuestra prueba de concepto no lo haremos… 😛

La prueba de concepto es muy, sencilla, en la página de Index accedemos al localStorage, lo recorremos y ponemos su contenido en una lista:

  1. <h2>Index</h2>
  2. <script type="text/javascript">
  3.     $(document).ready(function () {
  4.         for (var x = 0; x <= localStorage.length – 1; x++) {
  5.             var key = localStorage.key(x);
  6.             var value = JSON.parse(localStorage.getItem(key));
  7.             $("#lstCatalog").append(
  8.                 $("<li>").append(value.nombre + " " + value.precio)
  9.             );
  10.         }
  11.     });
  12.  
  13.  
  14. </script>
  15.  
  16. <ul id="lstCatalog">
  17.     
  18. </ul>

Fijaos en el uso de JSON.parse para pasar la cadena JSON que guardamos en el localStorage a un objeto javascript. El resto del código se entiende bastante bien, ¿no?

Y la vista de alta? Bueno, pues se trata de un formulario de alta normal, salvo que el botón de enviar no es un botón de submit sino un botón normal. Y luego tenemos el siguiente código js:

  1. <script type="text/javascript">
  2.     $(document).ready(function () {
  3.         $("#btnAdd").click(function () {
  4.             var nombre = $("#Nombre").val();
  5.             var precio = $("#Precio").val();
  6.             localStorage.setItem(nombre, JSON.stringify({ nombre: nombre, precio: precio }));
  7.             location.href = "@Url.Action("Index")";
  8.         });
  9.     });
  10. </script>

(btnAdd es el ID del botón del formulario).

Es simple: accedemos al valor del input “Nombre” y del “Precio”. Construimos un objeto javascript, lo pasamos a cadena con JSON.stringify y lo guardamos en el localStorage. Para la clave usamos el nombre del producto. Es aquí, donde antes de insertar el elemento en el localStorage, nos iriamos al servidor via Ajax para confirmar que podemos añadirlo.

Al final nos volvemos a la página de Index.

Y ya hemos terminado. Como prueba de concepto, no está mal: 5 minutos para tener una lista de productos en el cliente. El servidor ni se ha enterado, este es el código del controlador:

  1. public class LocalStorageController : Controller
  2. {
  3.     public ActionResult Index()
  4.     {
  5.         return View();
  6.     }
  7.  
  8.     public ActionResult Nuevo()
  9.     {
  10.         return View();
  11.     }
  12. }

Por supuesto falta soporte para eliminar un elemento del localStorage, pero eso es trivial usando los métodos clear() (borra todo el localStorage) y removeItem (borra un elemento).

Nota: localStorage es persistente. Es decir, entre ejecución y ejecución de la aplicación (cerrar el browser y abrirlo de nuevo mañana) los datos se mantienen. Viene a ser como una cookie pero con esteroides. Si quieres usar un localStorage “no persistente” (que dure solo mientras la ventana está abierta” existe el sessionStorage. Además recuerda que localStorage, al estar en el cliente, es totalmente hackeable.

En resumen: Realizar un catálogo en MVC sin base de datos en el servidor es posible si queremos que los datos no sean persistentes. Pero si queremos que esos sean persistentes (que el usuario pueda cerrar el navegador, abrirlo y continuar viendo sus datos) debe usarse una BBDD (o algún otro almacenamiento persistente) sí o sí. Hasta antes de HTML5 este almacenamiento persistente tenía que estar en el servidor. Con HTML5 y localStorage puede estar en el cliente.

Nota final:

Además de esas tres opciones se me ocurren al menos DOS más para realizar lo mismo:

  1. Usar cookies (seria la versión pre-html5 de usar el localStorage, pero hay serias limitaciones en el tamaño de los datos a guardar).
  2. Usar indexedDB (sería parecido en filosofía a usar el localStorage).

Algunos links:

  1. WebStorage (localStorage y sessionStorage): http://dev.w3.org/html5/webstorage/
  2. Sesión en ASP.NET MVC (en mi blog): http://geeks.ms/blogs/etomas/archive/2010/06/30/asp-net-mvc-q-amp-a-c-243-mo-usar-la-sesi-243-n.aspx
  3. IndexedDB: http://www.w3.org/TR/IndexedDB/

Espero que el ejercicio os haya sido de interés! 😉

PD: Os dejo un zip con el código de las opciones 2 y 3 en https://skydrive.live.com/redir.aspx?cid=6521c259e9b1bec6&resid=6521C259E9B1BEC6!233&parid=6521C259E9B1BEC6!167&authkey=!AHLp6PeH4Uw1hEg