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:
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:
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!
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:
- 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[]).
- Obtiene el tipo (System.Type) especificado.
- 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! ;-)
¡Hey! Dos entradas de la serie C# Básico en menos de un mes… ¿Señal de algo? Quien sabe… :P
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:
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:
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:
- Tan solo puedo tener un parámetro params en un método…
- …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:
- 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).
- 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! :P Espero que os haya sido interesante… ;-)
Saludos!
¡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:
¿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:
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:
- Una referencia (inicial) que apunta a un objeto
- Otra referencia (foo) que apunta a un objeto nuevo
- El valor de la propiedad “Bar” del objeto apuntado por inicial es 10.
- 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:

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:
- La referencia foo apunta a un objeto nuevo cuya propiedad Bar vale 11.
- La referencia inicial, dado que es la misma que foo, apunta al mismo objeto.
- 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:

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:

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:
- 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.
- 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.
- La palabra clave “ref” permite pasar un parámetro por referencia.
- 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.
- 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! ;-)
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! ;-)
Jejejee… Sí, aunque no lo parezca a veces hago temillas con Webforms, y es que uno tiene que conocer al enemigo! :P
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:
- http://tratadooscuro.blogspot.com.es/2009/02/dopostback-ese-gran-desconocido.html
- 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,
"BLOCKED SCRIPT__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="BLOCKED SCRIPT__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="BLOCKED SCRIPT__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!
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”… :D
Vamos a utilizar el siguiente modelo para representar los productos:
- public class Producto
- {
- public string Nombre { get; set; }
- public int Precio { get; set; }
- }
Y el catálogo en sí:
- public class Catalogo
- {
- private readonly string _nombre;
- private readonly List<Producto> _productos;
-
- public Catalogo()
- {
- _productos = new List<Producto>();
- }
-
- public IEnumerable<Producto> Productos
- {
- get { return _productos; }
- }
-
- public void AnyadirProducto(Producto p)
- {
- _productos.Add(p);
- }
- }
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:
- protected void Session_Start()
- {
- Session["catalogo"] = new Catalogo();
- }
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:
- [HttpPost]
- public ActionResult Nuevo(Producto producto)
- {
- var catalogo = Session["Catalogo"] as Catalogo;
- catalogo.AnyadirProducto(producto);
- return RedirectToAction("Index");
- }
Y listos!
Un par de consideraciones sobre el uso de sesión:
- 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 :P (aunque podría ser un fichero en el servidor también).
- 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:
- public class ObjectToBase64
- {
-
- public static string ToBase64(object source)
- {
- var base64 = string.Empty;
- if (source != null)
- {
- using (var ms = new MemoryStream())
- {
- var formatter = new BinaryFormatter();
- formatter.Serialize(ms, source);
- var buffer = ms.ToArray();
- base64 = Convert.ToBase64String(buffer);
- }
- }
- return base64;
- }
-
- public static object FromBase64 (string base64)
- {
- if (string.IsNullOrEmpty(base64)) return null;
- var buffer = Convert.FromBase64String(base64);
- using (var ms = new MemoryStream(buffer))
- {
- var formatter = new BinaryFormatter();
- return formatter.Deserialize(ms);
- }
- }
- }
La idea ahora es que:
- Cada acción tiene un parámetro adicional (p.ej. llamado _viewstate).
- 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:
- @if (ViewBag.ViewState != null)
- {
- <input type="hidden" name="_viewstate" value="@ViewBag.ViewState"/>
- }
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:

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:
- El catálogo se guarda en el local storage del cliente
- 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… :P
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:
- <h2>Index</h2>
- <script type="text/javascript">
- $(document).ready(function () {
- for (var x = 0; x <= localStorage.length - 1; x++) {
- var key = localStorage.key(x);
- var value = JSON.parse(localStorage.getItem(key));
- $("#lstCatalog").append(
- $("<li>").append(value.nombre + " " + value.precio)
- );
- }
- });
-
-
- </script>
-
- <ul id="lstCatalog">
-
- </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:
- <script type="text/javascript">
- $(document).ready(function () {
- $("#btnAdd").click(function () {
- var nombre = $("#Nombre").val();
- var precio = $("#Precio").val();
- localStorage.setItem(nombre, JSON.stringify({ nombre: nombre, precio: precio }));
- location.href = "@Url.Action("Index")";
- });
- });
- </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:
- public class LocalStorageController : Controller
- {
- public ActionResult Index()
- {
- return View();
- }
-
- public ActionResult Nuevo()
- {
- return View();
- }
- }
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:
- 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).
- Usar indexedDB (sería parecido en filosofía a usar el localStorage).
Algunos links:
- WebStorage (localStorage y sessionStorage): http://dev.w3.org/html5/webstorage/
- 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
- 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
Buenas :)
Al estilo de muchos blogs que visito habitualmente y que proponen pequeños acertijos en base a un código que tiene un error (muchas veces no aparente, otras más evidente), os propongo hoy uno, que me he encontrado revisando código.
Así que, amigos ¿qué hay de malo en este código?
if (!File.Exists(fname))
{
File.Create(fname);
}
// Hacemos lo que sea...
Sencillo, ¿no? Si el fichero no existe lo creamos y luego hacemos lo que se supone que tengamos que hacer. Parece sencillo, pero hay algo que se nos escapa… ¿Qué puede ser?
Un saludo!
Muy buenas!!! El 14 de Marzo voy a dejar de cambiar pañales durante un par de horas y voy a realizar un webcast para la gente de desarrolloweb hablando sobre ASP.NET MVC 3 (no, no vamos a comentar nada sobre la Beta 4 ya que eso daría para otro webcast).
La intención de este webcast es presentar ASP.NET MVC y está especialmente destinado a todos aquellos que no conozcan nada del framework, incluyendo a todos aquellos que desarrollan para web en tecnologías no microsoft (PHP, Java, Ruby on Rails, …). Vamos a ver el framework, las ideas que hay detrás de él y daremos un repaso rápido a sus capacidades. La duración del webcast será entre una hora – hora y pico y luego… preguntas :)
Uno de los motivos para realizar este webcast es que estoy colaborando con la gente de desarrolloweb en un manual de ASP.NET MVC (que va saliendo por partes) y que en breve presentaremos un curso online sobre ASP.NET MVC de nivel medio (el otro motivo es que me cuesta muy poco ponerme delante del micro a hablar :p)
Así que sin más os dejo con los datos del webcast!
Webcast: Todavía no conoces ASP.NET MVC?
Si no has oído hablar nunca de ASP.NET MVC, o bien lo conoces de oídas pero no le has prestado atención este webcast es para tí. ASP.NET MVC es un soplo de aire fresco para el desarrollo web en tecnologías Microsoft. Tanto si eres un enamorado de ASP.NET clásico como si no lo puedes ni ver en pintura... ¿por qué no le echas una ojeada a ASP.NET MVC? ¡No te arrepentirás!
Fecha y hora: 14 de Marzo de 2012 a las 19:00 (Hora España peninsular)
Url Registro:
https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032507405&Culture=es-ES
Os espero y por supuesto si conocéis a desarrolladores web que no quieran ni oír a hablar de ASP.NET, decidles que se pasen que igual se llevan una sopresita! :)
Bueno… seguimos esta serie explorando ASP.NET WebAPI. En este post vamos a hablar de los formatos de salida. Como ya hemos dicho, de serie ASP.NET WebAPI tiene soporte para XML y para JSON. Pero… como decide el framework si enviar la salida en XML o en JSON?
La cabecera accept
Una de las cabeceras que define HTTP es la cabecera accept. Esta cabecera se usa para que el cliente informe al servidor de los tipos de datos (content type) que acepta. De nuevo un par de pruebas con fiddler nos permiten verlo fácilmente. Este va a ser nuestro controlador:
- public class ValuesController : ApiController
- {
- public IEnumerable<int> GetAll()
- {
- return Enumerable.Range(1, 30);
- }
- }
Y ahora de nuevo usamos fiddler para crear y ver una petición GET a /api/values, como la siguiente:
GET http://worldoffighters.epnuke2.com:55603/api/values HTTP/1.1
User-Agent: Fiddler
Host: worldoffighters.epnuke2.com:55603
La respuesta recibida es:
HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 19 Feb 2012 20:08:20 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
Content-Length: 82
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30]
Bueno, parece pues claro que ante cualquier ausencia de accept, la salida se envía en JSON (content-type: application/json). Añadimos ahora una cabecera accept:

Y generar una petición como la que sigue:
GET http://worldoffighters.epnuke2.com:55603/api/values HTTP/1.1
User-Agent: Fiddler
Host: worldoffighters.epnuke2.com:55603
accept: text/xml
Y esta es la respuesta que recibimos ahora:
HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 19 Feb 2012 20:11:55 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: text/xml; charset=utf-8
Connection: Close
Content-Length: 543
<?xml version="1.0" encoding="utf-8"?><ArrayOfInt xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"><int>1</int><int>2</int><int>3</int><int>4</int><int>5</int><int>6</int><int>7</int><int>8</int><int>9</int><int>10</int><int>11</int><int>12</int><int>13</int><int>14</int><int>15</int><int>16</int><int>17</int><int>18</int><int>19</int><int>20</int><int>21</int><int>22</int><int>23</int><int>24</int><int>25</int><int>26</int><int>27</int><int>28</int><int>29</int><int>30</int></ArrayOfInt>
Bueno, hemos visto el mecanismo que usa el framework para determinar el formato de salida: la cabecera accept (no es nada nuevo, es el standard de HTTP y de hecho ya hablé hace tiempo de como aplicarlo en ASP.NET MVC: http://geeks.ms/blogs/etomas/archive/2010/09/10/asp-net-mvc-formato-de-salida-seg-250-n-content-type.aspx).
Bueno, vamos a ver ahora como crear un tipo de salida nuevo y tenerlo vinculado a un content-type determinado.
Usando MediaTypeFormatter
Para añadir un formato de salida nuevo debemos crear una clase que derive de MediaTypeFormatter:
- public class MediaBinaryFormatter : MediaTypeFormatter
- {
- public MediaBinaryFormatter()
- {
- SupportedMediaTypes.Add(new MediaTypeHeaderValue("application/octet-stream"));
- }
-
- protected override bool CanWriteType(Type type)
- {
- return true;
- }
-
- protected override Task OnWriteToStreamAsync(Type type, object value, System.IO.Stream stream, HttpContentHeaders contentHeaders, FormatterContext formatterContext, System.Net.TransportContext transportContext)
- {
- return Task.Factory.StartNew(() =>
- {
- var formatter = new BinaryFormatter();
- formatter.Serialize(stream, value);
- });
- }
- }
Hay tres aspectos interesantes a recalcar:
- En el constructor de la clase es donde vinculamos este serializador a un content-type específico, en este caso a application/octet-stream
- Indicamos que podemos serializar cualquier tipo .NET. Esto es porque siempre devolvemos true en el método CanWriteType. Pero podríamos devolver true solo para determinados tipos, lo que permite tener serializados personalizados para ciertos tipos de datos ;-)
- Finalmente en el método OnWriteToStreamAsync es donde realizamos la serialización y escritura en el stream de salida. Fijaos que debemos devolver un objeto de la clase Task con el código a ejecutar, ya que ese método será llamado de forma asíncrona por el framework (aunque a nosotros no nos preocupe demasiado). En este caso el código lo que hace es usar el BinaryFormatter de .NET para enviar la serialización en bnario del objeto que reciba. Por supuesto esto es a modo de demostración, ya que es lo más anti-internet que existe: este formato de deserialización es propio de .NET por lo que tan solo un cliente .NET puede entenderlo.
Y listos! Con esto casi hemos terminado… Nos falta simplemente registrar este MediaTypeFormatter en el framework. Y siguiendo la filosofía clásica de ASP.NET MVC la configuración está en una clase estática que podemos inicializar fácilmente desde gloabal.asax. En nuestro caso basta con añadir (p.ej. en el Application_Start) la línea:
- GlobalConfiguration.Configuration.Formatters.Add(new MediaBinaryFormatter());
Y ahora sí que hemos terminado. Si ejecutamos una petición con fiddler poniendo application/octet-stream en la cabecera accept, esta es la respuesta:

Como se puede observar es una serialización binaria de .NET!
En resumen hemos visto como a través del header HTTP accept, el cliente puede especificar que formato de respuesta desea y como podemos añadir MediaTypeFormatters propios para dar soporte a otros tipos de datos que no sean JSON o XML.
Un saludo a todos!
Buenas! Este es el segundo post de la serie que trata sobre ASP.NET Web API una de las grandes novedades que vienen con ASP.NET MVC. El primer post de la serie fue la introducción. Lo que quiero comentar antes que nada es que esta serie la estoy escribiendo no como un tutorial de ASP.NET Web API desde el punto de vista de un experto (porque no lo soy) sino desde el punto de vista de alguien que conoce ASP.NET MVC y está empezando a explorar Web API.
Hoy vamos a tratar un poco más el tema del enrutamiento, y el uso de verbos HTTP propios.
En el post anterior vimos como simplemente llamando a los métodos como el verbo HTTP a usar, el sistema enrutaba las peticiones perfectamente:
- public class ValuesController : ApiController
- {
- // GET /api/values/5
- public string Get(int id)
- {
- return "Hola: " + id.ToString();
- }
- }
El método Get se llama a través dela URL /api/values/xxx y el verbo HTTP GET. Si el método se llamase Post() entonces se accedería a él a través del verbo HTTP POST. Vamos a ver ahora algunas variantes, porque esto se va a poner… interesante :)
- public class ValuesController : ApiController
- {
- public string GetAlgo(int id)
- {
- return "Get Algo: " + id.ToString();
- }
-
- public string GetAlgoDistinto(int id)
- {
- return "Get Algo Distinto: " + id.ToString();
- }
-
- }
Bueno… La primera pregunta es… ¿Y eso funciona? Ahora nuestros métodos no se llaman Get pero empiezan por Get. Será suficiente esto? Veamos:

¡Muy interesante! Se nos queja diciendo que ha encontrado más de un método para invocar dada la URL /Api/Values/2 (y el método GET). Eso significa una cosa: la parte del nombre del método que sucede a Get es ignorada por el framework. Eso puede parecer una tontería, pero no lo es en absoluto, ya que me permite que los nombres de los métodos de mi controlador sean más claros:
- public string GetAll()
- {
- return "Get All";
- }
-
- public string GetById(int id)
- {
- return "Get Algo Distinto: " + id.ToString();
- }
La URL /api/values invocará al método GetAll y la url /api/values/10 invocará al método GetById pasándole el 10.
Bueno… usando esta convención un controlador representa un recurso (p.ej. Values) y las acciones que pueden efectuarse sobre él, a través de los distintos verbos HTTP y parámetros de la URL. Esta es una visión muy REST, de hecho en REST generalmente se usa un patrón de URLs parecidos a:
- /recursos (p.ej. /api/values)
- GET: Obtiene la colección de recursos
- PUT: Sustitye todos la colección de recursos por otra pasada (raro)
- POST: Añade un recurso nuevo a la colección
- DELETE: Elimina todos los recursos
- /recursos/id (p.ej. /api/values/5)
- GET: Obtiene este recurso
- PUT: Crea el recurso con este ID, o si ya existe lo sustituye
- POST: Añade información al elemento con este ID (modiificación)
- DELETE: Elimina el elemento con este ID
Este patrón de URLs REST es el que nos da por defecto el routing de ASP.NET Web API
Pero, por supuesto este patrón puede no ser útil siempre, a nosotros nos puede interesar tener soporte para una URL del tipo /api/values/GreaterThan/10 que nos devuelva solo los valores superiores a 10 (por decir algo). Pues bien, eso podemos conseguirlo usando un mapeo similar al que tendríamos en ASP.NET MVC, usando el route value action. En este caso, este route value se mapea al nombre del método (al igual que los controladores clásicos) y si queremos especificar un verbo HTTP distinto de GET debemos decorar el método con el atributo específico. Además ambos métodos de routing se pueden combinar en una tabla de rutas como esta:
- routes.MapHttpRoute(
- name: "DefaultApi",
- routeTemplate: "api/{controller}/{id}",
- defaults: new { id = RouteParameter.Optional }
- );
- routes.MapHttpRoute(
- name: "DefaultApiRouted",
- routeTemplate: "api/{controller}/{action}/{param}",
- defaults: new { param = RouteParameter.Optional }
- );
Notad como la segunda entrada se parece mucho, pero mucho, a la entrada por defecto de ASP.NET MVC.
En este caso, las URLS (y suponiendo el verbo HTTP GET):
- /api/values –> Invocará al método Get() o GetXXX() (sin parámetros)
- /api/values/x –> Invocará al método Get(id) o GetXXX(id) (con un parámetro)
- /api/values/GreaterThan/x –> Invocará al método GreaterThan(param)
Fijaos que el parámetro se llama distinto (id en la primera ruta y param en la segunda). Eso debe ser así, si usáis el mismo nombre de parámetro en ambas rutas os va a dar un error de ambigüedad en la tabla de rutas.
Verbos propios HTTP
Aunque el protocolo HTTP define un conjunto de verbos estándard (tales como GET, POST, PUT, DELETE) no hay nada que impida crearse verbos propios. En el protocolo HTTP el verbo usado no es nada más que la primera palabra que se envía en la petición. P.ej. esa es una petición GET:
GET http://www.fiddler2.com/fiddler2/updatecheck.asp?isBeta=False HTTP/1.1
User-Agent: Fiddler/2.3.9.0 (.NET 2.0.50727.5448; Microsoft Windows NT 6.1.7601 Service Pack 1; AMD64)
Pragma: no-cache
Accept-Language: es-ES
Referer: http://fiddler2.com/client/2.3.9.0
Host: www.fiddler2.com
Connection: Close
De todos esos datos que envía el navegador si nos fijamos en la primera línea vemos que empieza por GET: eso es el verbo HTTP. Nada nos impide usar un verbo propio (otra cosa es que el servidor que está al otro lado lo entienda claro).
Quizá te preguntes por la necesidad de definir verbos nuevos HTTP. Bueno, imagina una situación como la siguiente: estás creando un juego, que expone una interfaz REST para ser consumida desde cualquier sitio. Sigue suponiendo que en dicho juego se puede atacar casillas. Una forma de hacerlo usando los verbos estándard sería una petición PUT a /Attacks/id (donde ID fuese un ID del nuevo ataque, p.ej.). Es una visión muy CRUD: atacar una casilla es crear un nuevo ataque y insertarlo. Pero otra visión podría ser una petición a /Tile/id_casilla con el verbo HTTP Attack. En esta otra visión el recurso al que accedemos es la casilla y la acción que realizamos sobre ella es atacarla. Esta puede ser una razón por la que queremos crearnos nuestros propios verbos HTTP.
Ahora la duda es si podemos gestionar peticiones que lleguen con un verbo HTTP propio usando ASP.NET Web API. La primera prueba es fácil: consiste en crear un método con un nombre determinado, p.ej. Attack y crear una petición usando este verbo HTTP. Así que creamos el siguiente método en nuestro controlador:
- public string Attack()
- {
- return "Verbo attack!";
- }
Y luego creamos una petición a api/values usando el verbo Attack. Para usar un verbo HTTP inventado lo más rápido es usar fiddler. Abrimos fiddler, le damos a la pestaña “Composer” (en versiones anteriores era “Request Builder”) y ponemos los datos:

El host que veis es simplemente un alias (definido en hosts) para localhost, porque fiddler no soporta localhost.
Y el resultado que nos muestra el propio fiddler es este:
HTTP/1.1 405 Method Not Allowed
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 19 Feb 2012 09:23:13 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
"The requested resource does not support http method 'ATTACK'."
Bueno… en cierto modo es lo que nos podíamos esperar… Hubiese sido demasiado bonito que funcionase así :)
Si queremos soportar verbos HTTP propios lo que tenemos que hacer es decorar el método apropiado con AcceptVerbs y pasar el nombre del verbo HTTP que queremos:
- [AcceptVerbs("Attack")]
- public string PerformAttack()
- {
- return "Verbo attack!";
- }
Pasamos a AcceptVerbs el verbo HTTP a soportar y luego el nombre del método ya es indiferente. Si ahora repetimos la petición con fiddler, en lugar de un error 405 obtenemos un 200 (petición correcta):
HTTP/1.1 200 OK
Server: ASP.NET Development Server/10.0.0.0
Date: Sun, 19 Feb 2012 09:27:54 GMT
X-AspNet-Version: 4.0.30319
Cache-Control: no-cache
Pragma: no-cache
Expires: -1
Content-Type: application/json; charset=utf-8
Connection: Close
Content-Length: 15
"Verbo attack!"
Recuerda que los verbos HTTP propios no pueden ser usados por lo general desde un navegador (ni usando javascript). Así que si los usas debes ofrecer una alternativa con verbos HTTP estándard (y si quieres ser totalmente compatible con cualquier navegador limitándote a GET y POST) si tu API debe ser invocable desde un navegador. Algunas APIs REST ofrecen mecanismos built-in para esto, como el uso de X-Http-Method-Override (usado en las APIs de Google) o de X-Http-Method (usado en OData) pero ninguno de los dos está directamente soportado en ASP.NET Web API.
Bueno… espero que os haya sido interesante. En siguientes posts seguiremos explorando ASP.NET Web API que nos quedan varias cosas intersantes para ver!!
Un saludo a todos!
Bueno… ayer se animó un poco el cotarro con la salida de la beta de ASP.NET MVC4. Y ayer mismo, el Maestro realizó un post fenómenal explicando un poco todas las novedades del framework. Echadle un vistazo al post, porque es realmente espectacular (para variar :p).
De todas las novedades que aparecen en esta versión 4, yo quiero centrarme en la llamada ASP.NET Web API. A pesar de su nombre no se trata de una API nueva. En concreto se trata de un conjunto de herramientas para permitirnos a nosotros, la creación de APIs basadas en REST. Quizá os preguntaréis: Ah, ¿pero es que ahora no se podía?
Pues la verdad es que… sí. Hasta ahora si queríamos crear una API REST teníamos dos opciones. La primera era usar ASP.NET MVC y crear una aplicación cuyos controladores en lugar de devolver vistas con contenido HTML devolvieran datos en JSON o XML. La verdad es que ASP.NET MVC con su sistema de rutas y el fácil soporte para los distintos verbos http es una herramienta ideal para crear servicios REST. Y ¿la otra opción? La otra opción se llama WCF. La verdad es que al principio WCF no estaba muy bien preparada para la creación de servicios REST (nació mucho más orientada a servicios tipo RPC como servicios Web SOAP), pero con el tiempo se le fueron poniendo añadidos que culminaron con la salida de WCF Web API (http://wcf.codeplex.com/wikipage?title=WCF%20HTTP) que simplificaba al máximo la creación de servicios REST usando WCF.
Así que, ¿si ya tenemos dos opciones para la creación de servicios REST para qué añadir otra? Pues muy sencillo: para integrar a las dos anteriores. ASP.NET Web Api, recoge lo mejor de WCF Web Api y lo integra dentro de ASP.NET MVC y de esta manera obtenemos un sistema muy sencillo y potente para la creación de servicios REST. Un sistema que de fábrica nos ofrece:
- Negociación de contenido automática: Que permite que el servicio devuelva los datos en el formato que prefiera el cliente (p. ej. XML o JSON), sin que nosotros nos hayamos de preocupar al respecto.
- Soporte para consultas realizadas usando ODATA
- Hosting autocontenido, es decir, podemos hospedar nuestra API REST usando IIS (sin ningún problema) pero también dentro de un ejecutable propio!
Además obtenemos todos los beneficios de ASP.NET MVC: Model binding, rutas, action filters,… Y es que estamos desarrollando una aplicación ASP.NET MVC!
Creación del proyecto
Para crear un proyecto de Web API es muy sencillo, con la Beta de ASP.NET MVC 4 instalada, os aparecerá un template de proyecto nuevo llamado “ASP.NET Web API”, que os creará el esqueleto del proyecto base:

Como podéis ver en la figura no se diferencia en nada de un proyecto ASP.NET MVC normal… Veamos el código que nos ha generado VS para el controlador ValuesController (lo genera automáticamente):
- public class ValuesController : ApiController
- {
- // GET /api/values
- public IEnumerable<string> Get()
- {
- return new string[] { "value1", "value2" };
- }
-
- // GET /api/values/5
- public string Get(int id)
- {
- return "value";
- }
-
- // POST /api/values
- public void Post(string value)
- {
- }
-
- // PUT /api/values/5
- public void Put(int id, string value)
- {
- }
-
- // DELETE /api/values/5
- public void Delete(int id)
- {
- }
- }
Vale, hay tres puntos a destacar:
- El controlador no deriva de Controller, deriva de ApiController
- Las acciones no devuelven ActionResult, en su lugar devuelven datos tal cual. Fijaos que en ningún sitio indicamos si estos datos serán enviados en JSON, XML u otro formato. El cliente recibirá los datos automáticamente en el formato que haya pedido.
- El nombre de los métodos es el nombre del verbo HTTP que se usa para acceder a ellos. Es decir en lugar de decorar un método con [HttpPost] para indicar que debe accederse a él usando POST, llamamos a este método Post.
Si miramos la tabla de rutas veremos que el código generado por VS es el siguiente:
- routes.MapHttpRoute(
- name: "DefaultApi",
- routeTemplate: "api/{controller}/{id}",
- defaults: new { id = RouteParameter.Optional}
- );
-
- routes.MapRoute(
- name: "Default",
- url: "{controller}/{action}/{id}",
- defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
- );
Vemos dos entradas, la primera es la que corresponde a nuestra API REST (mapeará todas las URLS tipo /api/{controlador}). La segunda entrada es una entrada estándard de ASP.NET MVC porque podemos mezclar una API REST con controladores estándard que devuelvan vistas (o lo que sea).
Si quieremos soportar llamadas del tipo /api/{controlador}/id/texto entonces debemos modificar la tabla de rutas:
- routes.MapHttpRoute(
- name: "DefaultApi",
- routeTemplate: "api/{controller}/{id}/{value}",
- defaults: new { id = RouteParameter.Optional, value = RouteParameter.Optional }
- );
Y ahora podemos añadir un método tal como el siguiente a nuestro controlador (ValuesController):
- public string Get(int id, string value)
- {
- return value + " -> " + id;
- }
Y si llamamos a la url http://localhost:55603/api/values/3/hola obtenemos:

Fijaos como nos ha llamado a nuestro método y nos ha devuelto los datos en formato XML, de forma automática.
Los que hayáis desarrollado en ASP.NET MVC os dais cuenta de una cosa? Eso funciona! Fijaos que tenemos en nuestro controlador:
- // GET /api/values
- public IEnumerable<string> Get()
- {
- return new string[] { "value1", "value2" };
- }
-
- // GET /api/values/5
- public string Get(int id)
- {
- return "Hola: " + id.ToString();
- }
-
- public string Get(int id, string value)
- {
- return value + " -> " + id;
- }
¿Os acordáis lo que pasaba cuando en un controlador clásico teníamos algo como lo siguiente?
- public ActionResult Index()
- {
- return View();
- }
-
- public ActionResult Index(int id)
- {
- return View();
- }
¡Exacto! Pasaba (y sigue pasando en los controladores “normales”) esto:

En cambio en los controladores para APIs no ocurre esto: tenemos tres métodos (Get, Get(int) y Get(int, string)) y el sistema llamará correctamente a uno u otro en función de si en la URL le pasamos 0,1 ó 2 parámetros!
Bueno… Lo dejamos aquí en este post. En los siguientes iremos investigando más sobre todas las características de Web API: Iremos viendo como usar ODATA, distintos formatos de salida, autenticación y no sé… lo que se me vaya ocurriendo, sinceramente!
Un abrazo!
Bueno… empieza el 2012: el último año de nuestra existencia si los maias no andaban errados (unos tíos que hacían pirámides hace miles de años no pueden equivocarse demasiado). Pero bueno… hasta que no llegue el apocalípsis, ahí estaremos! ¡Al pie del cañón!
De todos modos, para empezar nada más que un post ligerito, sobre C# Básico, ya sabéis esta serie de posts donde vamos explorando cosillas, sin ningún orden en particular, básicas del lenguaje. Sí, ya sé que el ritmo de posts de la serie es abrumador (sobre un post por año) pero bueno… como dicen los ingleses: less give an stone.
Hoy vamos a hablar sobre los eventos, uno de los elementos de C# (realmente es de .NET en general pero aquí nos ceñiremos a C#) que más confusión causa, debido a que por un lado son una mezcla de convenciones, por otro VS nos autogenera código que nos puede hacer pensar que todo es más fácil, por otro porque usarlos es trivial pero entender lo que hay debajo de ellos, pues no lo es tanto… Ah sí, y por último a mucha gente le cuesta entender como funcionan porque están basados en delegates y el propio concepto de delegate no es trivial (¡aunque ya hemos hablado en esta serie de delegates!).
1. ¿Qué es un evento?
Bueno… antes que nada digamos que un evento es un mecanismo estándard de .NET. He dicho de .NET, no de winforms, o de WPF o de Silverlight. Repito: de .NET. Eso significa que cualquier clase puede lanzar eventos o suscribirse a ellas. No es necesario tener una aplicación gráfica para tener eventos, una triste aplicación de consola puede tener eventos. Esa es una diferencia de .NET con otros frameworks o plataformas: Los eventos no son parte de un framework en concreto, son parte integral de la plataforma.
En el mundo de .NET un evento es un mecanismo que tiene un objeto de una clase cualquiera de notificar algo a un conjunto de objetos cualesquiera. El cualesquiera del final es importante: si yo, como objeto, expongo un evento cualquier objeto de cualquier clase puede si desea suscribirse a este evento sin que yo tenga que hacer nada en especial. Eso los hace tremendamente útiles porque el desarrollador de la clase que lanza el evento no está obligado a conocer absolutamente nada sobre las posibles clases que se suscribirán a este evento.
2. ¿Como se usa un evento?
Antes de responder a esta cuestión respondamos a otra: ¿qué nos interesa decirle a quien se suscriba al evento? Por ejemplo si estamos creando una clase que informe de que se ha creado un fichero nuevo en disco, nos puede interesar pasar el nombre o la ruta del fichero. Si estamos creando una lista que informa cuando el usuario selecciona un elemento, nos puede interesar pasar el índice del elemento seleccionado, o el propio elemento.
Imaginemos el siguiente código (de una aplicación de línea de comandos).
static void Main(string[] args)
{
var igualada = new Ciudad("Igualada");
var castefa = new Ciudad("Castelldefels");
var bandolero = new Persona("José Miguel", igualada);
Console.ReadLine();
bandolero.Emigrar(castefa);
}
Tenemos al bandolero, que estaba en la ciudad de Igualada y debido a una incomprensible decisión decide emigrar a Castelldefels. Ahora imaginad que tenemos una clase Censo que quiere recibir notificaciones cuando el censo de una Ciudad en concreto se ha modificado. Esto implica que la clase Ciudad debe informar cuando un habitante causa baja o alta en ella. Es en estos casos (cuando un objeto quiere informar a otro) que usamos un evento.
El uso de un evento se compone de dos partes:
- Quien lanza el evento (en nuestro caso la clase Ciudad)
- Quien recibe el evento (en nuestro caso la clase Censo)
2.1 Suscribirse a un evento
El que recibe el evento, debe suscribirse y para ello debe informar de que método quiere que sea llamado cuando se lance el evento. Es decir, debe proporcionar al evento un delegate con el método. El código puede ser algo parecido a:
public class Censo
{
private readonly Ciudad _ciudad;
public Censo(Ciudad c)
{
_ciudad = c;
_ciudad.HabitanteDadoDeAlta += new CiudadEventHandler(Ciudad_HabitanteDadoDeAlta);
}
private void Ciudad_HabitanteDadoDeAlta(Persona persona)
{
Console.WriteLine("{0} vive ahora en {1}", persona.Nombre, _ciudad.Nombre);
}
}
En la segunda línea del constructor la clase Censo se está suscribiendo al evento HabitandeDadoDeAlta que tiene la clase Ciudad. Y el parámetro es un delegate de tipo CiudadEventHandler que “apunta” al método Ciudad_HabitanteDadoDeAlta. Aquí dos preguntas:
- ¿Como sabe la clase Censo que el método Ciudad_HabitanteDadoDeAlta debe recibir un parámetro Persona? Pues por el delegate CiudadEventHandler. Este delegate está definido de la siguiente manera:
delegate void CiudadEventHandler (Persona persona);
- ¿Como sabemos que el evento HabitanteDadoDeAlta usa el delegate CiudadEventHandler? Pues eso se indica en la declaración del evento, que veremos ahora.
Bien, hemos visto como suscribirnos a un evento. Eso implica declarar la función gestora (que será llamada cuando se lance el evento) con los parámetros que defina el delegate usado por el evento. Y también usar el operador += para asignar una instancia del delegate (apuntando a nuestra función gestora) al evento.
2.2 Declarar un evento
Veamos ahora, como declarar un evento. Lo que es la declaración en sí, es muy, muy, muy, simple:
public event CiudadEventHandler HabitanteDadoDeAlta;
Esta línea (en la clase Ciudad) declara un evento, llamado HabitanteDadoDeAlta y que usa el delegate CiudadEventHandler. Lo declaramos público porque todo el mundo pueda acceder al evento, y así suscribirse a él (usando el operador +=).
2.3 Lanzar un evento
Una vez hemos declarado un evento, el siguiente paso es lanzarlo, cuando sea necesario. El código para lanzar un evento es extremadamente simple. En nuestro caso haríamos (en la clase Ciudad):
HabitanteDadoDeAlta(persona);
Fijaos que usamos el evento como si fuese un método. Dado que el delegate define que hay un parámetro de tipo Persona, eso es lo que le pasamos. ¡Y listos! Con esto todos los que estén suscritos al evento, lo recibirán (y ejecutarán cada uno su función gestora, que recibirá los parámetros que nosotros hemos pasado). Las funciones gestoras se ejecutan una tras otra síncronamente.
Bueno… listos del todo no :) La verdad es que este código fallaría si nadie está suscrito al evento HabitanteDadoDeAlta, ya que entonces este vale null y recibiríamos una NullReferenceException. Lo que tenemos que hacer es comprobar antes que el evento no es null (para mayor comodidad he creado una función privada de la clase Ciudad que lanza el evento):
private void OnHabitanteDadoDeAlta(Persona persona)
{
if (HabitanteDadoDeAlta != null)
{
HabitanteDadoDeAlta(persona);
}
}
Este es el código típico que os encontraréis en la mayoría de sitios de ejemplos para lanzar un evento.
Nota: Debo decir que este código NO es 100% correcto. Contiene un sutil error que puede hacer que en según que casos (siempre relacionados con concurrencia) se reciba una NullReferenceException. En los foros de MSDN expliqué con bastante más detalle el tema y pongo el código correcto que debe usarse: http://social.msdn.microsoft.com/Forums/es/vcses/thread/d28cf2c7-b024-4c1a-93aa-8dc5005ff713. También el maestro entre maestros escribió hace tiempo al respecto en su blog.
¡Listos! Con esto ya lo tienes todo! Ya sabes como declarar eventos, lanzarlos y suscribirte a ellos. Veamos ahora algunos detalles más…
3. ¿Pero… las funciones gestoras no deben recibir siempre dos parámetros?
Es mala costumbre de algunos que enseñan eventos en winforms (o incluso en WPF y Silverlight) decir algo parecido a: “Las funciones gestoras de eventos siempre deben recibir dos parámetros. El primero es un object, que contiene el lanzador del evento, y el segundo contiene los datos del evento y es un objeto de una clase derivada de EventHandler”.
Bueno… eso no es más que una convención. Una convención no es nada más que una regla que todos acordamos seguir, para hacernos la vida más fácil. Pero para nada es una obligación del sistema o de la plataforma. Es cierto que en winforms, WPF y Silverlight los eventos siguen esta convención. Por esto estarás harto de ver código parecido a:
private void btnModificar_Click(object sender, EventArgs e)
{
//...
}
Como digo, todos los eventos que están declarados en winforms, en Silverlight y en WPF siguen esa convención (en este ejemplo concreto el evento Click está definido usando el delegate EventHandler que ya viene con .NET) y no está de más decir que tus propios eventos deberían seguirla (aunque si no lo hacen funcionará todo igual).
Por lo tanto: no, las funciones no deben siempre incluir esos dos parámetros, pero si desarrollas en winforms, WPF o Silverlight, deberías hacerlo para seguir la convención marcada.
4. El código para suscribirme a los eventos es muy farragoso…
Sí, es cierto, es una queja común. Por eso en VS2005 creo que fue, lo simplificaron, así que si quieres puedes hacer:
_ciudad.HabitanteDadoDeAlta += Ciudad_HabitanteDadoDeAlta;
Fíjate que es mucho más sencillo: te olvidas de hacer el new del delegate, pasas simplemente el nombre de la función gestora y listos. En este caso el compilador de C# infiere y crea el delegate por tí.
Y sí, crees que tener que crear una función para gestionar un evento, es un rollo y usas VS2008 o superior, puedes usar lambdas:
_ciudad.HabitanteDadoDeAlta += (p) => { Console.WriteLine("{0} vive ahora en {1}", p.Nombre, _ciudad.Nombre); };
Este mecanismo es el más compacto de todos, ya que NO estás obligado a declarar la función gestora, en su lugar pasas la lambda expression que la implementa. Fíjate que el compilador es capaz, incluso de inferir que el parámetro p es de tipo Persona (porque así obliga el delegate del evento). Si no te sientes cómodo con la notación de lambdas, piensa que NO estás obligado a usarla!
Por supuesto el mecanismo de usar lambdas está recomendado cuando el código de las funciones gestoras es muy reducido, por visibilidad quieres tenerlo junto a la suscripción o bien te interesa usar closures.
Actualmente Visual Studio, incluso el 2010 compilando contra el Framework 4.0, sigue usando el primer código que hemos visto cuando debe generarte código para suscribirte a un evento… En MS tienen muy claro que lo que funciona no debe tocarse, o bien el tío que hizo esta parte de VS se ha ido y no saben como modificarla (si en MS trabajan como muchas empresas de España, probablemente será eso :P).
5. Desuscripción de eventos
Sí amigo… cuando dejes de estar interesado en recibir más notificaciones de eventos lanzados por un objeto debes desuscribirte del evento. Si no lo haces se seguirá ejecutando la función gestora que especificaste incluso cuando no te interesa para nada.
¿Cuando debes desuscribirte de un evento? Pues muy fácil, hay solo dos reglas:
- Cuando deja de interesarte recibir el evento (obvio)
- Cuando estés suscrito a un evento que lance un objeto que está fuera de tu ámbito de vida y tu vayas a “morir”. Por decir que está fuera de tu ámbito de vida, me refiero a que si es posible que el otro objeto siga existiendo después de que tu “mueras”.
El punto 2 mucha gente lo pasa por alto, y termina generando memory leaks. Sí, sí… también se pueden generar memory leaks en .NET.
Un ejemplo: en Winforms es normal que el formulario (Form) se suscriba a los eventos de los controles que él contiene. En este caso no es necesario que el formulario se desuscriba, ya que cuando se destruya el objeto Form, todos los controles que este contiene son destruídos también. Pero, si este mismo formulario está suscrito a un evento que lanza un objeto que puede vivir después de que el Form haya sido eliminado, entonces el formulario debe desuscribirse de este evento, cuando vaya a ser eliminado. Esto se suele hacer en el método Dispose().
El código para desuscribirse a un evento es trivial (se usa el operador –=):
_ciudad.HabitanteDadoDeAlta -= Ciudad_HabitanteDadoDeAlta;
No es posible desuscribirse a un evento al cual se haya suscrito a través de una expresión lambda, ¡o sea que mucho ojo con esto!
Y bueno… creo que ya está bien por hoy. Como siempre en los posts de esta serie de C# Básico, hemos empezado por lo elemental y hemos introducido algunas pinceladas más avanzadas. No sufras si no lo entiendes todo a la primera, hay una receta muy fácil: ¡practica y pregunta!
¡Un saludo y feliz 2012!
Muy buenas!
Coged a alguien que no conozca mucho ASP.NET y preguntadle que relación tienen las siguientes clases entre ellas:
- HttpRequest
- HttpRequestBase
- HttpRequestWrapper
La respuesta más probable será que HttpRequestBase es la clase base, de la cual deriva HttpRequest y que HttpRequestWrapper es… bueno, por el nombre no queda muy claro: es un wrapper de algo pero de qué?
Pues no. Nada más lejos de la realidad. Aunque el nombre sugiera lo contrario HttpRequestBase no es la clase base de HttpRequest, de hecho ambas clases no tienen relación alguna entre ellas, pero en Redmond no tuvieron un buen día al escoger el nombre… Aunque al menos debe reconocerse que HttpRequestBase sí es clase base de alguien… ¡concretamente de HttpRequestWrapper!
Dado que no hay mucha gente que conozca esas dos últimas clases, hagamos pues una pequeña presentación.
HttpRequestBase es una clase cuyo interfaz público (es decir sus métodos y propiedades) son idénticos a los de HttpRequest. ¿Que HttpRequest tiene una propiedad llamada Cookies? HttpRequestBase la tiene también… y del mismo tipo. Y así con todas las propiedades y todos los métodos.
¿Y por qué han decidido crear semejante… cosa?
Pues para corregir una carencia que tenía el Framework: permitir abstraernos de HttpRequest de forma fácil: HttpRequestBase tiene la misma interfaz pública que HttpRequest pero no está vinculada a ASP.NET. No requiere un pipeline web ejecutándose, ni nada de nada: es un simple contenedor de datos.
Por supuesto ASP.NET está construido alrededor de HttpRequest, lo cual sigue dificultando mucho los tests unitarios… ¿así que entonces? Bueno ASP.NET arrastra toda una historia y no es fácil (ni sensato) romper con todo, así que lo máximo que podemos exigir es que lo más nuevo sí se haga bien. Y lo más nuevo es ASP.NET MVC. En efecto, ASP.NET MVC está construido alrededor de HttpRequestBase y no de HttpRequest.
P.ej. se puede acceder a la request desde un controlador:
var queryString = ControllerContext.HttpContext.Request.QueryString;
Pero si observáis con detalle veréis que la propiedad Request no es de tipo HttpRequest si no de tipo HttpRequestBase:
¿Y como podemos usar esto en nuestros tests unitarios?
Imaginad un controlador que tenga el siguiente código:
public ActionResult About()
{
var queryString = ControllerContext.HttpContext.Request.QueryString;
var modelo = new FooModel();
modelo.Nombre = ControllerContext.HttpContext.Request.QueryString["p1"];
return View(modelo);
}
El controlador recoge el valor del parámetro “p1” de la querystring y establece la propiedad Nombre del viewmodel con este valor (Nota: ¡Este código es solo para demostrar lo que se comenta en este post, en ASP.NET MVC hay maneras mejores de hacer esto!)
Ahora vamos a ver como sería un test unitario que probase este método. Para ello… y aquí es donde entra en juego HttpRequestBase: nos permite crear un mock o un fake de ella:
public class HttpRequestFake : HttpRequestBase
{
public override NameValueCollection QueryString
{
get
{
var values = new NameValueCollection();
values.Add("p1", "v1");
return values;
}
}
}
Ya tenemos nuestro fake de HttpRequestBase para que devuelva una querystring fijada por nosotros (donde el parámetro p1 valga v1).
Ahora nos toca hacer un par de fakes más: de HttpContextBase (para que devuelva nuestro objeto Request) y de ControllerContext para que nos devuelva el fake de HttpContextBase:
public class HttpContextFake : HttpContextBase
{
public override HttpRequestBase Request
{
get
{
return new HttpRequestFake();
}
}
}
public class ControllerContextFake : ControllerContext
{
public ControllerContextFake()
{
HttpContext = new HttpContextFake();
}
public override HttpContextBase HttpContext { get; set; }
}
Y finalmente ya podemos tener nuestro test unitario:
[TestMethod]
public void TestMethod1()
{
var controller = new HomeController();
controller.ControllerContext = new ControllerContextFake();
var result = controller.About() as ViewResult;
Assert.AreEqual("v1", (result.ViewData.Model as FooModel).Nombre);
}
Y listos! Con esto comprobamos que el controlador hace lo que se supone que debe hacer (asignar el valor del parámetro p1 de la querystring a la propiedad Nombre del viewmodel).
Gracias al hecho de que HttpRequestBase y HttpContextBase NO están ligadas a ningún pipeline de ASP.NET y de que ASP.NET MVC ha sido construído en torno a ellas y no a las reales HttpRequest y HttpContext nos es mucho más fácil la realización de tests unitarios.
Espero que os haya sido de interés! ;-)
Un saludo!
Ah sí… y HttpRequestWrapper? Pues HttpRequestWrapper no es nada más que una clase que deriva de HttpRequestBase y que sirve para… convertir un objeto HttpRequest en un HttpRequestBase… ya, probablemente un nombre mejor para ella hubiese sido HttpRequestAdapter. ;-)
Muy, muy, muy molesto… ASP.NET MVC3 corriendo sobre un servidor web configurado en español (cultura es-ES).
Con la tabla de rutas estándar, cuatro acciones como las siguientes
- [HttpPost]
- public ActionResult Index(DoubleModel model)
- {
- ViewBag.Valor = model.Valor;
-
- return View("Resultado");
- }
-
- [HttpPost]
- public ActionResult IndexSoloDouble(double? valor)
- {
- ViewBag.Valor = valor;
-
- return View("Resultado");
- }
-
-
- public ActionResult IndexRuta(double? id)
- {
- ViewBag.Valor = id;
- return View("Resultado");
- }
-
- public ActionResult IndexGet(double? valor)
- {
- ViewBag.Valor = valor;
- return View("Resultado");
-
- }
La clase DoubleMode simplemente tiene una propiedad double llamada Valor.
- La primera acción recibe un double via POST dentro de un modelo
- La segunda recibe un double via POST sólo
- La tercera acción recibe un double como parámetro de ruta
- La cuarta acción recibe un double via querystring
Pues bien:
- La primera acción acepta 12,10 pero no 12.10 (usa la cultura del servidor web)
- La segunda acción acepta 12,10 pero no 12.10 (usa la cultura del servidor web)
- La tercera acción acepta 12.10 pero no 12,10 (usa la cultura ¿invariante?)
- La cuarta acción acepta 12.10 pero no 12, 10 (como la tercera, parece usar la invariante).
Aquí tenéis el código de la vista usada para enviar los datos:
- @using MvcApplication1.Models
- @model DoubleModel
-
- @{
- ViewBag.Title = "Index";
- }
-
- <h2>Index</h2>
-
- @{
- Html.EnableClientValidation(false);
- }
-
- @using (Html.BeginForm()) {
- <div>
- Introduce el valor:
- <input type="hidden" name="Valor" value="12,10" />
- </div>
- <input type="submit" value="enviar 12,10 via POST" />
- }
-
- @using (Html.BeginForm()) {
- <div>
- <input type="hidden" name="Valor" value="12.10" />
- </div>
- <input type="submit" value="enviar 12.10 via POST" />
- }
-
-
- @using (Html.BeginForm("IndexSoloDouble","Home"))
- {
- <div>
- <input type="hidden" name="Valor" value="12,10" />
- </div>
- <input type="submit" value="enviar 12,10 via POST (solo double)" />
- }
-
-
- @using (Html.BeginForm("IndexSoloDouble", "Home"))
- {
- <div>
- <input type="hidden" name="Valor" value="12.10" />
- </div>
- <input type="submit" value="enviar 12.10 via POST (solo double)" />
- }
-
-
-
- @Html.ActionLink("Enviar 12,10 via GET", "IndexGet", new { valor = "12,10" }); | @Html.ActionLink("Enviar 12.10 via GET", "IndexGet", new { valor = "12.10" });
- <br/>
- @Html.ActionLink("Enviar 12,10 en ruta", "IndexRuta", new { id = "12,10" }); | @Html.ActionLink("Enviar 12.10 en ruta", "IndexRuta", new { id = "12.10" });
El problema parece estar en los ValueProviders de URL (RouteDataValueProvider y QueryStringVaueProvider), que parecen ignorar la cultura, mientras que el ValueProvider de POST (FormValueProvider) la respeta.
Lo dicho… muy molesto :(
Saludos!
Editado 16:43. PD: Iván Loire (@ivanloire) me comenta que este comportamiento es por diseño: los locales NO se aplican en los parámetros de URL para mantener las URLs canónicas. Si usáramos los locales del usuario podríamos tener URLs distintas que sirviesen el mismo contenido, y URLs válidas para un usuario podrían no serlo para otro. Es por ello que en MVC han optado por usar la cultura invariante en los ValueProviders de la URL. Dejo dos enlaces que me ha pasado Iván a dos posts donde se explica esto: http://bit.ly/tIZX64 y http://bit.ly/vCgbmf
Muy buenas! Para ser sinceros esta es una pregunta que me he hecho siempre y, creo yo, que se han hecho muchas personas que vienen de C++. ¿Debería tener C# referencias const? El hecho es que hasta ayer no había encontrado una explicación razonada y de alguien de peso (quien mejor que Eric Lippert, cuyo blog es lectura obligada) del porque C# no las incluye. Al final del post hay el enlace al post de stackoverflow en el que Eric explica sus razones por las que C# no tiene referencias const.
En este post voy a intentar explicar que son las referencias const (en C++) porque a Eric Lippert no le convencen (ojo que no queda claro con una sola lectura de lo que él dice, tuve que leérmelo un par o tres de veces junto con los comentarios, porque Eric es de los que cuando dice algo cualquier palabra cuenta), además de algunas opciones que hay actualmente en C# para simularlas.
El objetivo del post es captar vuestra opinión: es decir, creéis que estarían bien? O que sobran totalmente? O que tal y como están en C++ no, pero de otra forma podrían ser interesantes?
Dado que voy a exponer varias opiniones al respecto este post será largo… Pero espero que os resulte de interés ;-)
Referencias const en C++
Antes que nada aclaremos a que nos referimos por referencias const. Porque hay dos posibilidades:
- La referencia es constante, es decir, una vez inicializada su valor NO puede ser modificado (no puede apuntar a otro objeto). En C# eso sería una variable readonly.
- La referencia NO es constante pero a través de esa referencia NO puede modificarse el objeto apuntado.
En C++ ambas posibilidades existen, así que NO es lo mismo:
que
En el primer caso tenemos una referencia que es constante (básicamente lo que en C# conocemos como readonly, con la salvedad que pueden ser inicializadas en cualquier momento), mientras que en el segundo tenemos una referencia a través de la cual no podemos modificar el objeto. A esas referencias son a las que nos referimos con el nombre de “Referencias const”.
En C++ una clase debe declarar que métodos NO modifican los datos de la clase:
class Foo
{
int value;
public:
int GetValue (void) const;
void SetValue(int nv);
};
La clase Foo tiene dos métodos, GetValue y SetValue. Y el método GetValue está declarado como “const” para indicar que no modifica ningún miembro de la clase.
Así, el siguiente código NO compilará:
void FooConst1 (const Foo& foo)
{
foo.SetValue(10);
}
El compilador nos avisará que estamos intentando modificar un objeto Foo a través de una referencia const (el error real es un mensaje raro de que no puede convertir this pero quiere decir eso :p).
Usar una referencia const convierte el objeto en inmutable? No. Usar una referencia const evita que pueda ser modificado dicho objeto a través de esa referencia. Única y exclusivamente. Mira ese código y piensa cual es el valor de i después de ejecutar la función Bar:
void Bar(const int& v1, int& v2)
{
v2 = v1+1;
}
int _tmain(int argc, _TCHAR* argv[])
{
int i=10;
Bar(i, i);
}
El valor de i después de ejecutar Bar es de 11. Porque el valor de i se puede modificar a través de v2 que es una referencia tradicional (por supuesto intentar v1=v2+1 dentro de Bar sí que da error).
Ventajas de tener referencias const en el lenguaje
Como desarrollador el tener referencias const te permite:
- Tener la seguridad de que un método no va a modificar nada de tu clase. Si un método espera una referencia const sabes que el método no va a poder modificar el objeto que reciba.
- Devolver desde un método un objeto que no va a poder ser modificado por quien ha llamado el método. P.ej. en lugar de devolver una ReadOnlyCollection<T> (una clase que es una auténtica aberración desde el punto de vista de OOP) se podría devolver const List<T>.
La razón (de Eric Lippert) por la cual no existen en C#
En resumidas cuentas porque tal y como están implementadas en C++ no sirven. Él tiene dos objeciones al respecto, una muy pragmática y la otra mucho más profunda y filosófica.
- El casting, bien al estilo C, bien const_cast de C++, elimina la seguridad que podrían ofrecer las referencias const. Dicho de otro modo, la posibilidad de obtener una referencia tradicional a partir de una referencia const, convierten a éstas totalmente en inútiles (en lo que refiere a asegurar que a través de dicha referencia nadie podrá modificar el objeto). Imaginad que yo devuelvo una const List<T> con datos de mi objeto. Si quien obtiene la referencia puede a partir de esa referencia const, obtener una referencia tradicional (y por lo tanto añadir o eliminar elementos de la lista), yo pierdo la seguridad que tenía al devolver la referencia const. No puedo asumir que los contenidos del objeto no van a ser modificados. A eso se refiere Eric cuando dice que const is broken.
- La segunda razón es mucho más fuerte… Para Eric una referencia const sería realmente útil si convirtiese el objeto apuntado en inmutable. Lo deja muy claro con el siguiente comentario: If I have a reliably constant queue then I should be able to say "if (!q.Empty()) { M(); x = q.First(); }" and regardless of what M() does, the queue is still empty when it returns. The same way that if I say "const int y = 123; ... if (y > 0) { M(); " and after M() returns, y is still greater than zero because it is constant.
Es decir, del mismo modo que const int i=10; significa que la variable i nunca podrá ser modificada bajo ningún concepto, del mismo modo const Queue q debería significar que el objeto q es inmutable. No puede modificarse. Eso plantea problemas muy serios en cuanto a como inicializamos estos objetos, o como convertimos un objeto mutable en inmutable, etc, etc. Pero quedaros con la clave: el objeto pasa a ser inmutable. No hay manera de echar esto atrás. No es una referencia const a un objeto, es una referencia a un objeto constante. Esa segunda razón es muy fuerte y va mucho más allá de lo que una referencia const de C++ pretende (que el objeto no sea modificable a través de esa referencia).
Olvidemos pues esa segunda razón, a pesar de que para Eric tiene un peso fundamental, y volvamos a la primera: la posibilidad de hacer casting y de obtener una referencia normal a partir de una referencia const. La solución parece rápida y trivial: se prohíben esos castings. Y punto.
Y como pasa casi siempre, nada es tan trivial:
- Que hacemos con reflection? Si alguien usa reflection teoricamente podría llamara a un método que modificase el objeto apuntado por la referencia (aquí sale a colación de nuevo el segundo argumento de Eric: dado que el objeto no es inmutable, sino que es la referencia la que está marcada como const). Es eso importante? Bueno, depende… actualmente con reflection podemos llamar a métodos privados de una clase, lo que (sin negar su utilidad) puede llegar a representar una violación todavía mayor.
- Que hacemos con object? O dicho de otro modo… una referencia const Foo puede ser pasada a un método que acepte Object? Seguramente sí, responderéis todos, ya que los Foo son Objects con independencia de que sean mutables o no. Pero ahora imaginad una jerarquía de clases:
class Foo
{
public int PropFoo { get; set; }
}
class Bar : Foo
{
public string PropBar { get; set; }
}
Si un método recibe un objeto Foo, le puedo pasar un const Bar? Los objetos Bar son todos ellos objetos Foo, pero los const Bar lo son? Tiene sentido que lo sean? Tiene sentido que no lo sean? O los objetos const Bar sólo pueden ser const Foo? Dicho de otro modo: tenemos UNA sola jerarquía de objetos que empieza por Object, o tenemos DOS (una que empieza por Object y la otra por const Object)? Si admitimos un sola jerarquía, entonces estaremos de acuerdo en que yo puedo tener un const Bar y ese ser modificado (al menos la propiedad PropFoo) a través de un método que espere un Foo.
Por supuesto hay versiones mixtas, como tener una sola jerarquía, pero sólo admitir llamadas a métodos que no muten el objeto. De esa manera una referencia const Bar podría ser pasada a un método Foo y eso sólo compilaría si el método no hace nada que pueda mutar el objeto (p.ej. estaría bien llamar al getter de la propiedad pero no al setter). Pero eso complica mucho (pero mucho, eh?) el tema y es que de hecho con esta visión estamos volviendo al segundo punto que comentaba Eric (no es la referencia lo que está marcada como const, es el objeto que está marcado como inmutable). Y de todos modos para mi esa opción es inviable el hecho de que no hay nada que diga a priori a quien llama el método si esa llamada es correcta o no: que el código compilase dependería de lo que hiciera internamente un método X (que puede estar en otro assembly ya compilado) y no de sus parámetros, ni nada que yo pueda ver externamente. Lo único que podría hacer es intentar pasar mi referencia const Bar a un método que acepta un Foo y… ver si compila! Surrealista.
¿Son necesarias las referencias const tal y como están en C++?
Dicho de forma rápida y corta: No. Eso no significa que no sean útiles. Simplemente que hay otros métodos para hacer lo mismo. ¿Y en que consisten esos métodos? Pues básicamente en declararse una versión solo lectura de mi clase. La mejor manera de hacer esto es a través de una interfaz:
interface IReadOnlyFoo
{
int PropFoo { get; }
}
class Foo : IReadOnlyFoo
{
public int PropFoo { get; set; }
}
Si quiero devolver una referencia a un objeto Foo que no pueda modificarse devuelvo un objeto Foo a través de una referencia de tipo IReadOnlyFoo. Y listos! Si un método quiere aceptar como parámetro un objeto de tipo Foo pero NO quiere modificarlo puede aceptar un parámetro de tipo IReadOnlyFoo. Así el equivalente de const Foo es IReadOnlyFoo.
Por lo tanto la misma semántica que obtenemos a través de las referencias const, las obtenemos con este mecanismo. Cierto: es más tedioso y nos obliga a hacerlo por cada clase de la cual querramos tener una versión de sólo lectura.
Métodos puros
Una de las ventajas de la implementación de referencias const en C++ es que obliga al creador de la clase a declarar que métodos son susceptibles de ser llamados a través de una referencia const. A estos métodos se los conoce en C++ como métodos const. El compilador comprueba efectivamente que un método declarado como const lo sea:
class Foo
{
int value;
public:
int GetValue (void) const;
void SetValue(int nv);
};
int Foo::GetValue(void) const
{
value--;
return value;
}
Este código no compila, porque el método GetValue intenta modificar value a pesar de ser declarado como método const.
Debajo de los métodos const subyace un concepto realmente potente e interesante: los métodos puros. Dicho rápidamente, un método puro (no confundir con un método virtual puro de C++ que no tiene nada que ver) es aquel que siempre devuelve el mismo valor si se le pasan los mismos parámetros y además durante su ejecución no se observa ningún efecto colateral (en el caso de una clase significa que no modifica el estado de dicha clase). Ojo, que los métodos const y los métodos puros NO son exactamente lo mismo:
- Un método const puede ser impuro si devuelve datos aleatorios o dependientes de algo externo no controlable (p.ej. un fichero). Así p.ej. la propiedad Now de DateTime no es pura porque su valor no es predecible (cada día cambia!).
- Un método const NO puede modificar ningún miembro de la clase. Pero NO todos los miembros de la clase conforman su estado. Puede haber miembros privados que sean susceptibles de ser modificados pero que no formen parte del estado de la clase. Un método puro podría modificar esos miembros y un método const no (aunque este segundo punto puede solventarse con el uso de mutable en C++).
¿Y porque son interesantes los métodos puros? Bueno… tenerlos identificados permite ciertas optimizaciones (se puede memoizar (así sin erre) la llamada) y también habilita estos métodos para ser usados en comprobaciones de precondiciones, postcondiciones e invariantes. Por definición la comprobación de precondiciones, postcondiciones e invariantes deben ser métodos puros (dado que deben ser predecibles y no modificar el estado del objeto). Sin métodos puros el diseño por contratos no es posible (de forma segura y validada por el compilador)…
¿Hola? Si todavía estás aquí… muchas gracias por llegar hasta el final del post. Recuérdame que te pague una cervecita cuando nos veamos por ahí ;-)
He creado una encuesta para que podáis expresar vuestra opinión al respecto de todo lo comentado y por supuesto, tenéis los comentarios para explayaros a gusto! ;-)
Enlace a la encuesta: http://www.easypolls.net/poll.html?p=4eb26acb011eb0e44d6335ab
Enlace al post de stackoverflow: http://stackoverflow.com/questions/3263001/why-const-parameters-are-not-allowed-in-c-sharp
Un saludo! :)
¡Hola! Un compañero me ha preguntado si era posible enlazar una propiedad (de tipo int) a un control slider de jQuery UI. La verdad es que sí que es posible y vamos a ver en este post una posible solución que de hecho es extrapolable a otras situaciones parecidas que podáis tener.
Templated helpers al rescate
En ASP.NET MVC2 introdujeron el concepto de templated helpers un mecanismo para construir la interfaz de usuario a partir del tipo de datos del modelo. Simplificando un poco, si colocamos en la carpeta DisplayTemplates y EditorTemplates una vista parcial ASP.NET MVC usará esta vista automáticamente cada vez que se use el método Html.DisplayFor o Html.EditorFor respectivamente.
Si tenemos un Modelo de tipo X que tiene una propiedad, llamémosle Foo, cuyo tipo sea BarType, si hacemos:
- @Html.EditorFor(x=>x.Foo)
ASP.NET MVC buscará la vista EditorTemplates/BarType (el nombre de la vista es el tipo de la propiedad usada en EditorFor).
Como esta regla del nombre de tipo puede ser demasiado genérica, también es posible usar el atributo [UIHint] indicando el nombre del template (la vista parcial) a usar para editar o mostrar los datos:
- public class FooModel
- {
- [UIHint("FooEdit")]
- public BarType Foo { get; set; }
- }
Ahora la llamada a Html.EditorFor, buscará la vista llamada FooEdit en EditorTamplates.
Vamos pues a crear el template para editar y visualizar una propiedad de tipo int usando el slider de jQuery UI.
Para ello creamos una vista parcial en Views/Shared/EditorTemplates y le damos el nombre que queramos, en mi caso slider.cshtml. Para crear un slider basta con tener un <div> y luego llamar al método slider() (doy por supuesto que jQuery UI se ha descargado y está referenciada). Así que empezaremos con este código:
- <script type="text/javascript">
- $(document).ready(function () {
- $("#slider").slider();
- });
- </script>
- <div id="slider">
- </div>
Con esto creamos el slider pero dado que estamos en modo edición, necesitamos alguna manera para guardar el valor que el usuario seleccione. Una forma rápida de hacerlo es tener un hidden que mantenga en todo momento el valor que el usuario seleccione en el slider. Y aquí nos surge la primera duda: que valor ha de tener el atributo name para que luego ASP.NET MVC sea capaz de reconocerlo y enlazarlo a la propiedad del viewmodel? El problema es que el valor del atributo name depende del nombre de la propiedad que estamos enlazando así que hemos de recuperar este nombre… Por suerte podemos saber este nombre, usando la propiedad HtmlFieldPrefix de la propiedad TemplateInfo del ViewData. Es decir, podemos generar el campo hidden así:
- <input type="hidden" name="@ViewData.TemplateInfo.HtmlFieldPrefix"/>
Finalmente sólo nos queda suscrbirnos al evento change del slider y actualizar el campo hidden. Para ello deberemos añadir un id (he usado slider_hidden) al campo hidden y usar el siguiente código para crear el slider:
- <script type="text/javascript">
- $(document).ready(function () {
- var options = {
- change: function (event, ui) {
- $("#slider_hidden").val(ui.value);
- }
- };
- $("#slider").slider(options);
- });
- </script>
Con eso ya podemos crear un ViewModel con una propiedad int, decorada con [UIHint(“silder”)] y observar como aparece el slider para editar la propiedad. P.ej. dado el siguiente ViewModel:
- public class Ratio
- {
- public string Texto { get; set; }
- [UIHint("slider")]
- public int Rating2 { get; set; }
- }
Una vista para editar un objeto Ratio sería tan sencilla como:
- @using MvcSliderBinding.Models
- @model Ratio
- @{
- ViewBag.Title = "title";
- }
- @using (Html.BeginForm())
- {
- @Html.LabelFor(x => x.Texto)
- @Html.EditorFor(x => x.Texto)
- <br />
- @Html.LabelFor(x => x.Rating)
- @Html.EditorFor(x => x.Rating)
-
- <input type="submit" />
- }
Al usar @Html.EditorFor(x=>x.Rating), al ser Rating una propiedad decorada con UIHint(“slider”) se va a usar el editor template que hemos creado antes.
Ya tenemos enlazado una propiedad con el slider de jQuery! Ahora vamos a pulir detalles…
Preparándolo para que pueda haber más de un slider
El template de edición que hemos creado NO admite ser repetido en una misma vista, ya que usa ids estáticos para el <div> que será el slider y el hidden que contiene el valor. Si tuviéramos un viewmodel que tiene dos propiedades y quisiéramos usar dos sliders no nos funcionaría bien. Para solucionarlo nos basta con asegurar que los IDs son siempre distintos. Hay varias maneras, una es usar como id un prefijo y el valor que nos da HtmlFieldPrefix, ya que ese se supone único. Otro es usar un GUID, como se muestra a continuación:
- @model Nullable<int>
- @{
- var suffix = Guid.NewGuid().ToString();
- }
- <script type="text/javascript">
- $(document).ready(function () {
- var options = {
- value: @(Model.HasValue ? Model.Value : min),
- change: function (event, ui) {
- $("#@suffix").val(ui.value);
- }
- };
- $("#slider_@(suffix)").slider(options);
- });
- </script>
- <div id="slider_@(suffix)">
- </div>
Guardamos en suffix el GUID creado y lo añadimos al hidden y al div. Un detalle más que aprovecho para enseñaros es establecer el valor inicial del slider al valor que tenga la propiedad (que está en Model). Pero, si estamos creando el objeto el valor de Model será null. Es por ello que debo declarar que el modelo de la vista es de tipo Nullable<int>, en lugar de int, para poder aceptar esos valores nulos.
Accediendo a la información del viewmodel
Una cosa muy interesante al usar template helpers, es que nuestro template helper puede acceder a información del viewmodel que define la propiedad. Es decir, dentro del template helper, yo puedo saber cual es la propiedad que estoy editando (en mi caso es Rating) y tengo información sobre la clase que define dicha propiedad (en mi caso Ratio). A esos datos se puede acceder a través de ViewData.ModelMetadata:

Podeis usar las propiedades de ViewData.ModelMetadata para realizar ciertas tareas, como analizar el ViewModel en busca de atributos que os puedan ayudar a definir como renderizar el template… lo que se os ocurra.
Un ejemplo de esto, imaginad que tenemos una propiedad decorada con el atributo [Range], para indicar que acepta un rango de valores. Pues desde aquí podríais consultar dicho atributo Range (teneis acceso al Type del ViewModel y sabeis el nombre de la propiedad) y configurar el slider para que sólo acepte entradas en este rango.
Es lo que he hecho yo, salvo que en lugar de usar reflection para acceder al atributo Range, lo que he hecho es obtener los validadores que hay asociados a la propiedad y generar código acorde a ellos. Para ello uso el método GetVaidators() de ModelMetadata:
- @model Nullable<int>
- @{
- var suffix = Guid.NewGuid().ToString();
-
- bool hasRange = false;
- var rangeVal = ViewData.ModelMetadata.GetValidators(ViewContext.Controller.ControllerContext).OfType<RangeAttributeAdapter>().FirstOrDefault();
- ModelClientValidationRangeRule rule = null;
- var min = 0;
- var max = -1;
- if (rangeVal != null)
- {
- rule = rangeVal.GetClientValidationRules().OfType<ModelClientValidationRangeRule>().FirstOrDefault();
- if (rule != null)
- {
- min = Convert.ToInt32(rule.ValidationParameters["min"]);
- max = Convert.ToInt32(rule.ValidationParameters["max"]);
- }
- }
-
- }
Llamo a GetValidatos y busco el validador de tipo RangeAttributeAdapter. Eso es lo mismo que buscar si existe un atributo [Range], salvo que es más genérico (aunque no entraremos en detalles, simplemente comentar que DataAnnotations es una manera de añadir validadores, pero pueden haber más). Si existe el validador de rango, obtengo su configuración, en concreto sus valores mínimo y máximo, y me los guardo. Con esto ahora tengo información para generar un slider que sólo acepte valores en este rango:
- <script type="text/javascript">
- $(document).ready(function () {
- var options = {
- @if (rule!=null)
- {
- @:min: @min,
- @:max: @max,
- }
- value: @(Model.HasValue ? Model.Value : min),
- change: function (event, ui) {
- $("#@suffix").val(ui.value);
- }
- };
- $("#slider_@(suffix)").slider(options);
- });
Listos! Ahora tenemos un editor que además respeta el validador de rango que tenga la propiedad.
Y así podríamos ir perfilando este template de edición con todo lo que necesitáramos para adaptarlo a nuestras necesidades… ¡Simple, sencillo y potentísimo!
Os dejo un proyecto VS2010 con la implementación del template de edición y uno de visualización para que podáis verlo en acción: https://skydrive.live.com/?cid=6521c259e9b1bec6&sc=documents&uc=1&id=6521C259E9B1BEC6%21167#
Espero que os haya sido útil! Un saludo!
NOTA: Este post está basado en la versión Developers Preview de Windows 8, que salió en Septiembre del 2011. Versiones posteriores pueden dejar (y con suerte dejarán) este artículo obsoleto.
Un post cortito: Si desarrollas aplicaciones Metro para Windows 8 usando C# y XAML no uses ObservableCollection<T>. Simple y llanamente no funciona.
En su lugar debe usarse IObservableVector<T> interfaz de la cual podéis encontrar una implementación aquí: http://code.msdn.microsoft.com/Data-Binding-7b1d67b5/sourcecode?fileId=44725&pathId=1428387049. Esa implementación proporciona además un método ToObservableVector para convertir una INotifyCollectionChanged (es decir una ObservableCollection<T>) en un IObservableVector<T>.
Relacionado con el tema: ojo con implementar INotifyPropertyChanged. En concreto, ojo con cual implementas pues resulta que ahora hay dos! Por un lado está el INotifyPropertyChanged de toda la vida (System.ComponentModel.INotifyPropertyChanged) y por otro uno nuevo que es el que usa WinRT: Windows.UI.Xaml.Data.INotifyPropertyChanged. Ese último es el que tenéis que utilizar en vuestros ViewModels.
Desconozco el porque de estos cambios (no usar INotifyCollectionChanged ni el INotifyPropertyChanged de toda la vida), aunque supongo que tienen que ver en no usar colecciones ni eventos propios de .NET y tenerlo todo controlado dentro del API de WinRT. También supongo que en siguientes versiones de Windows 8 eso se arreglará.
Así que si no queréis, como yo, perder un buen rato preguntándoos porque no se actualiza una ListBox… ya sabéis! ;-)
Un saludo!
PD: Algunos enlaces que he encontrado buscando acerca de esto:
- ObservableCollection no funciona: http://social.msdn.microsoft.com/Forums/en-AU/winappswithcsharp/thread/054913c2-6ad4-4b54-a349-c7ae846d4f8e
- Selecciona el INotifyPropertyChanged correcto en WinRT: http://blog.galasoft.ch/archive/2011/09/25/quick-tip-select-the-correct-inotifypropertychanged-in-windows-8.aspx –> Según menciona aquí esto parece que ya está corregido y que la nueva versión de Win8 ya no tendrá ese error.
Muy buenas!
Cada vez más nos encontramos con aplicaciones web que funcionan “en una sola página”, es decir que se carga la página inicial y luego todas las nuevas peticiones son via AJAX. Esas aplicaciones funcionan perfectamente hasta que el usuario le daba a atràs o a F5 para refrescar la página: en este momento se pierde el estado de la navegación.
Hasta ahora no había una manera estándard y sencilla para lidiar con esto, pero HTML5 ya está aquí y incluye una nueva API de historial que nos va ayudar con estos casos. Aunque hay más, voy a centrarme en este post en dos elementos de dicha API:
- El evento popstate. Este evento de window se lanza cuando se navega a una dirección del historial. Bueno, sí, la definición es un poco ambigua pero básicamente traducido es que se lanza cuando se pulsa el botón de atrás en el navegador.
- El método pushState del objeto history. Este método permite meter una entrada en el historial. Esa entrada consta de un objeto con datos arbitrarios y una url. Cuando llamemos a pushState la barra de direcciones se modificará para mostrar la nueva url, pero el navegador no navegará hacia allí. En su lugar habrá añadido una entrada ficticia en el historial con los datos que nosotros le hayamos indicado. Cuando se pulse atrás en el navegador, se lanzará el evento popstate y en él podremos recuperar esos datos y simular lo que sea que tenga que simularse para darle la sensación al usuario de que el botón de atrás funciona.
Sí, parece un poco lioso, pero en este post veremos como realizar una aplicación ASP.NET MVC que:
- Muestre una lista de productos y enlaces a los detalles
- Al pulsar en un detalle se muestre una página del producto
- Todo el refresco sea via ajax
- Al pulsar F5 el usuario se queda donde está. Es decir, si estaba viendo los detalles del producto 2 continuará viéndolos.
- El funcionamiento de back y forward será el esperado.
- En la barra de direcciones se mostrará la URL real que se está visitando, aunque esta se haya cargado via ajax.
En resumen, nuestra aplicación se va a comportar exactamente como se espera de una aplicación que no es ajax salvo que… usará ajax con todas las ventajas (menos refresco y mayor velocidad) que conlleva. Y lo mejor… no nos costará mucho hacerlo. ¡Viva HTML5!
1. La estrategia general
Tenemos que definir la estrategia general: por un lado mientras naveguemos por la aplicación todo serán peticiones ajax, pero si el usuario le da a F5, entonces ya no tendremos una petición ajax. En su lugar tendremos una petición estándard que tendremos que gestionar. Por lo tanto nuestros controladores deberán estar capacitados para servir la misma información les venga la petición por Ajax, o les venga de forma tradicional.
Para solventar esto, nos basta con meter todo el contenido en una vista parcial y devolver la vista parcial cuando la petición sea via Ajax. Cuando la petición sea tradicional entonces devolveremos una vista normal que lo único que hará será renderizar la vista parcial.
P.ej. Este es el código de la vista Home/Index.cshtml:
@{
ViewBag.Title = "Indice";
}
@{Html.RenderPartial("Index_partial");}
Simplemente renderizar Index_partial que es donde habrá el contenido. Luego nos basta un método en el controlador como el siguiente:
public ActionResult Index()
{
return Request.IsAjaxRequest() ?
(ActionResult)PartialView("Index_partial") :
(ActionResult)View();
}
Esa estrategia la repetiremos en todas las acciones de los controladores.
Bien, vayamos ahora a por las vistas. Este es el código de la vista Index_partial:
<div id="source">
<h2>Index</h2>
<a href="@Url.Action("List", "Products")" data-ajax="true" >Ver productos</a>
<br />
</div>
Tan sólo un enlace, con el atributo data-ajax=”true” y un div llamado source. El atributo data-ajax a true es importante porque es el que usaremos para interceptar este enlace y cargarlo via ajax. Por otro lado el div source también es importante porque es donde vamos a poner el contenido que nos venga de la petición ajax: es decir machacaremos todo nuestro contenido con el que nos venga de la petición ajax.
Con eso, ya tenemos la estrategia montada: Vamos a tener una vista “normal” y una parcial por cada acción y las vistas parciales serán las que realmente muestren el contenido.
2. Interceptar las llamadas a los links
Para esto vamos a valernos del atributo data-ajax que hemos creado antes. Este atributo es un atributo que me he inventado yo. En HTML5 te puedes inventar los atributos que te da la gana, siempre que empiecen por data-. Es una forma de estandarizar lo que antes hacíamos con clases CSS o bien inventándonos atributos. Pero a diferencia de usar una clase CSS (que usa algo pensado para aspecto para comportamiento) o inventarse un atributo cualquiera (que hace que el código deje de ser HTML válido) usar un atributo data-* no tiene ninguna contra-indicación. Así funcionan muchas de las características de HTML5: se han cogido muchas cosas que se hacían antes y se ha buscado una manera estándard de hacerlas!
Bueno, al tajo, vamos a crearnos un archivo myscripts.js al que vamos a meter algunas funciones, empezando por lo siguiente:
function bindLinks() {
$("a[data-ajax]").each(function () {
$(this).click(function (evt) {
evt.preventDefault();
ajaxload($(this).attr("href"), true);
});
});
}
La función bindLinks recorre todos los tags <a> que tengan el atributo data-ajax y les asigna una función gestora al evento click. Esa función gestora lo que hace es evitar que actúe el click estándard (por lo que NO navegaremos a través del link) y luego llama a ajaxload que es un método que veremos a continuación pasándole el valor del atributo href del tag <a> pulsado.
Ya tenemos todos los clicks de los enlaces interceptados. Para que esto se ejecute nos basta con asegurar que al cargar la página se llame a bindLinks. Para esto en la página de Layout añadimos:
<script type="text/javascript">
$(document).ready(function () {
bindLinks();
});
</script>
Finalmente la definición de la función ajaxload es casi trivial, ya que lo único que hace es llamar a load() de jQuery para cargar los datos y meterlos dentro del div source que hemos mencionado antes.
function ajaxload(url, add) {
$("#source").load(url, function () {
bindLinks();
});
}
El único detalle es que luego llama de nuevo a bindLinks() para volver a interceptar los clicks de los nuevos tags <a> que hayan aparecido!
3. Simular la modificación de la barra de direcciones
Vale, tenemos un enlace, que nos debería dirigir a una cierta URL, pongamos /Productos/Ver/10 pero en lugar de seguir el enlace, lo estamos cargando via ajax. Si queremos que el F5 funcione correctamente, debemos “modificar” la barra de direcciones, para que aparezca la URL “real”. Esto, que antes no se podía hacer, ahora es posible con la nueva History API de HTML5.
Cada vez que carguemos un enlace via Ajax añadiremos una entrada en el historial. Para ello usaremos el método pushState del objeto history. A este método se le pasan tres parámetros:
- Un objeto de estado. Es un objeto javascript cualquiera.
- Un título
- Una URL
El objeto de estado es una de las partes más importantes: cuando el usuario pulse el botón de atrás vamos a recibir el evento popstate y en este evento tendremos el objeto de estado. En este objeto pues nosotros podemos poner toda aquella información necesaria para que luego, en el evento popstate, podamos “deshacer” los cambios y darle la ilusión al usuario de que el botón de atrás funciona bien.
La URL que pasemos es la URL que se mostrará en la barra de direcciones. Pero sólo se mostrará, el navegador no navegará hacia allí. De nuevo es para engañar al usuario y hacerle creer que realmente ha ido a otra URL cuando en realidad simplemente hemos modificado el contenido de la página usando javascript (ajax).
Para modificar la barra de direcciones nos basta un pequeño añadido a la función ajaxload que teníamos antes:
function ajaxload(url, add) {
history.pushState({ uri: url }, '', url);
$("#source").load(url, function () {
bindLinks();
});
}
Cada vez que cargamos una vista via Ajax, añadimos una entrada en el historial. El primer parámetro es el objeto de estado. En este caso simplemente colocamos la URL “a la que navegamos” (simplemente porque no necesitaremos nada más). El tercer parámetro es la URL que mostrará el navegador en la barra de direcciones. Pero recordad: el navegador NO irá a esa URL (nosotros estamos cargando su contenido por ajax).
4. Soporte para back
Al modificar la barra de direcciones hemos dado soporte a F5. Porque al pulsar F5 el navegador refrescará la última entrada del historial que ahora contiene la URL que hemos puesto antes. Y recordad que nosotros antes hemos preparado los controladores para responder por Ajax y para responder a peticiones normales: por lo tanto en este punto tenemos una aplicación que se refresca totalmente por ajax… pero con soporte para F5. ¿No os parece genial?
Pues ahora vamos a añadir el soporte para el botón de atrás. Para ello nos vamos a basar en el evento popstate que se lanza cuando el navegador debe navegar a otra entrada del historial (básicamente cuando se pulsa el botón de back). En este evento recibimos el objeto de estado de la posición de historial que se está eliminando.
Vamos a modificar la página de Layout para añadir un manejador al evento popstate:
<script type="text/javascript">
$(document).ready(function () {
$(window).bind('popstate', function (evt) {
doPopstate(evt.originalEvent.state);
});
bindLinks();
});
</script>
Usamos el método bind() de jQuery para enlazar un manejador al evento popstate del objeto window y llamar a doPopstate, pasándole la propiedad state del evento (que es el objeto de estado).
La función doPopstate es nuestra y tiene el siguiente código:
function doPopstate(data) {
if (data != null) {
ajaxload(data.uri, false);
}
}
Si tenemos objeto de estado (teoricamente debemos tenerlo SIEMPRE, pero Chrome p.ej. al cargar una página por primera vez lanza un popstate sin objeto de estado, mientras que Firefox no lo lanza. Personalmente creo que es comportamiento de Firefox el correcto), nos limitamos a cargar via ajax la URL de este objeto de estado. Dado que están apilados será la URL anterior a la que estamos.
En este punto hemos tenido que añadir un parámetro a ajaxload (el booleano), para evitar que ajaxload nos añada una entrada de historial si estamos yendo hacia atrás. El nuevo código de ajaxload queda así:
function ajaxload(url, add) {
if (add) {
history.pushState({ uri: url }, '', url);
}
$("#source").load(url, function () {
bindLinks();
});
}
Por supuesto también modificamos la llamada a ajaxload dentro del manejador del evento de click de los enlaces en bindLinks(), para pasarle un true. Y con eso… hemos terminado.
Para entender exactamente que pasa he modificado ligeramente ajaxload y doPopstate para que hagan un log de lo que ocurre en un <div> de la página de Layout. Y eso es un poco más o menos lo que ocurre…




- Lanzamos la aplicación
- Pulsamos “Ver productos”
- Pulsamos “Detalle del producto 2”
- Pulsamos “Inicio”
Fijaos como en todo este tiempo el reloj de “Tiempo Actual” es siempre el mismo (estamos cargando via Ajax) y como la barra de direcciones va cambiando. También podéis ver el log en la parte inferior. Continuemos…




- Pulsamos “Ver productos”
- Pulsamos “Detalles del producto 1”
- Pulsamos BACK –> Fijaos en este punto en la aparición del evento popState y como recuperamos el evento del historial anterior.
- Pulsamos BACK de nuevo –> Otro evento popState y recuperamos la posición anterior.
De nuevo en todo este tiempo el reloj de “Tiempo actual” se ha movido: todo son refrescos Ajax. Sigamos…


- Pulsamos “Ver Productos”
- Pulsamos F5 –> En este punto se lanza una petición “real”. Podeis ver como el reloj de “Tiempo actual” se ha modificado y el log ha desaparecido (normal se ha cargado toda la Layout de nuevo). Pero nos hemos quedado en la página donde estábamos (el listado de productos).
Bueno… os dejo el proyecto en VS2010 para que juguéis con él e investiguéis un poco como funciona el History API de HTML5.
Importante: El proyecto lo he probado con:
- Firefox 7.0 y funciona correctamente
- Chrome 15.0.874.83 y funciona correctamente
- Internet Explorer 9 y no funciona (no tiene soporte para History API)
- Opera 11.5 y funciona correctamente
Editado: Edito para informar que @wasat me ha comentado que Internet Explorer 10 soporta también History API. Para más info: http://msdn.microsoft.com/en-us/ie/hh272905.aspx#_HTML5History Gracias Jose por la información!!!!
Os dejo el enlace con el proyecto para que veais como está hecho y podais jugar con él: https://skydrive.live.com/?cid=6521c259e9b1bec6&sc=documents&uc=1&id=6521C259E9B1BEC6%21167#
Nota: También os dejo el enlace de History.js, que es un plugin de jQuery para soportar History API de forma consistente en todos los navegadores y que es mejor usar antes que meterse a hacerlo a mano: http://plugins.jquery.com/project/history-js No he mencionado el plugin antes porque el objetivo del post no era mostraros el plugin sino la nueva History API de HTML5.
Un saludo!!!!
Buenas! En este post vamos a ver como usar Recaptcha en ASP.NET MVC. Pero, antes que nada permitidme una aclaración: Si estás buscando integrar rápidamente Recaptcha en tu proyecto que sepas que puedes usar MvcRecaptcha o también el helper que viene en MVC3. Pero vamos a ver como hacerlo desde cero. ¿Por que? Pues simplemente porque me parece un buen ejemplo didáctico. Pero insisto: ya hay soluciones hechas, eso es sólo para ver como podríamos hacerlo desde cero
Añadir el captcha en una vista es sumamente sencillo: basta con incluir un tag <script> y dejar que él haga todo. También se puede crear usando javascript (lo que es útil si se quiere crear el captcha sólo si se cumplen ciertas condiciones en tiempo de ejecución), pero no vamos a verlo aquí (todos los detalles están en http://code.google.com/intl/ca/apis/recaptcha/docs/display.html en el apartado de “Ajax API”).
Para añadir recaptcha en nuestra página basta simplemente con añadir el siguiente código script:
<script type="text/javascript"
src="http://www.google.com/recaptcha/api/challenge?k=CLAVE_PUBLICA">
</script>
Este tag <script> renderizará el captcha en la posición donde se incluya.
Vamos a crearnos un helper que nos genere este tag script. El código es trivial:
public static class RecaptchaExtensions
{
public static IHtmlString Recaptcha(this HtmlHelper @this)
{
return Recaptcha(@this, "RecaptchaPublicKey");
}
public static IHtmlString Recaptcha(this HtmlHelper @this, string publicKeyId)
{
var publicKey = ConfigurationManager.AppSettings[publicKeyId];
return DoRecaptcha(@this, publicKey);
}
private static IHtmlString DoRecaptcha(this HtmlHelper @this, string publicKey)
{
var tagBuilder = new TagBuilder("script");
tagBuilder.Attributes.Add("type", "text/javascript");
tagBuilder.Attributes.Add("src", string.Concat("http://www.google.com/recaptcha/api/challenge?k=", publicKey));
return MvcHtmlString.Create(tagBuilder.ToString(TagRenderMode.Normal));
}
}
El método que realmente realiza el trabajo es el método privado DoRecaptcha, que usa un objeto TagBuilder para construir el tag <script>. Fijaos en que el valor de retorno de las funciones del helper es IHtmlString.
La función Recaptcha del helper recibe un parámetro que es el nombre del <appSetting> donde hay la clave pública de Recaptcha (hay una versión sin parámetreos que usa el <appSetting> cuya clave sea RecaptchaPublicKey.
Usar el helper es muy sencillo:
<div>
Necesitamos asegurarnos que eres humano. Actualmente sólo aceptamos
<i>Humanos estándar</i>:
@Html.Recaptcha()
</div>
Perfecto! Estamos listos para lo realmente interesante: Comprobar que el resultado que entra el usuario es válido.
Para ello, si consultamos la página donde se describe el proceso de verificación veremos que necesitamos 4 valores:
- La IP del cliente
- La clave privada de Recaptcha
- Dos valores adicionales, llamados challenge y response que nos envía recaptcha (son campos añadidos al formulario). Los nombres de los dos campos son recaptcha_challenge_field y recaptcha_response_field.
Bueno, para validar que el usuario ha dado de alta el captcha, lo podríamos hacer de muchas maneras, pero yo he escogido un filtro de acción. Eso me va a permitir decorar la acción del controlador de la siguiente manera:
[HttpPost]
[Recaptcha(Name="Captcha")]
public ActionResult Register(RegisterModel model)
{
//...
}
Si la validación con Recaptcha es errónea el flitro dejará un error en ModelState con la clave indicada en el parámetro Name (aquí el mensaje es fijo, pero por supuesto podría ser variable). El filtro lo configuraremos para que se ejecute antes de la acción, por lo que, dentro del método Register podremos usar ModelState.IsValid para preguntar si todo está correcto (incluyendo el captcha).
El uso de un filtro de acción es interesante porque elimina toda esa lógica de comprobación de la acción del controlador.
Bueno, si revisamos de nuevo la documentación de Recaptcha, vemos que debemos usar los 4 valores mencionados anteriormente y realizar un POST a la dirección http://www.google.com/recaptcha/api/verify. La respuesta de este POST nos indicará si la validación ha sido correcta (la primera línea valdrá true) o ha sido incorrecta (valdrá false). ¡Y ya está!
Para crear el filtro, derivamos de la clase ActionFilterAttribute y redefinimos el método OnActionExecuting, para que se ejecute justo ANTES de la acción del controlador:
public class RecaptchaAttribute : ActionFilterAttribute
{
public string Name { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
var request = filterContext.RequestContext.HttpContext.Request;
var challenge = request.Form["recaptcha_challenge_field"];
var response = request.Form["recaptcha_response_field"];
const string postUrl = "http://www.google.com/recaptcha/api/verify";
var result = PerformPost(request.UserHostAddress, challenge, response, postUrl);
if (!result)
{
filterContext.Controller.ViewData.ModelState.AddModelError
(Name ?? string.Empty, "Recaptcha incorrecto");
}
}
}
Este es el código básico: Recogemos los dos campos recaptcha_challenge_field y recaptcha_response_field, realizamos el POST y si el resultado NO es correcto añadimos un error usando el método AddModelError de ModelState.
El método PerformPost sería tal y como sigue:
private bool PerformPost(string remoteip, string challenge, string response, string postUrl)
{
var request = WebRequest.Create(postUrl);
request.Method = "POST";
request.ContentType = "application/x-www-form-urlencoded";
var stream = request.GetRequestStream();
var privateKey = ConfigurationManager.AppSettings["RecaptchaPrivateKey"];
using (var sw = new StreamWriter(stream))
{
const string data = "privatekey={0}&remoteip={1}&challenge={2}&response={3}";
sw.Write(data, privateKey, remoteip, challenge, response);
}
var recaptchaResponse = request.GetResponse();
string recaptchaData = null;
var recaptchaStream = recaptchaResponse.GetResponseStream();
if (recaptchaStream != null)
{
using (var sr = new StreamReader(recaptchaStream))
{
recaptchaData = sr.ReadToEnd();
}
return ParseResponse(recaptchaData);
}
else return false;
}
Usamos la clase WebRequest para realizar una petición POST con los campos indicados. Fijaos en la definición de la variable data que contiene las variables en el formato típico de post: nombre=valor&nombre=valor&… Luego simplemente volcamos esa variable en el stream de la request del objeto WebRequest.
Finalmente recogemos la respuesta, la guardamos toda en una cadena y la parseamos con el método ParseResponse que es tal y como sigue:
private static bool ParseResponse(string recaptchaData)
{
var reader = new StringReader(recaptchaData);
var first = reader.ReadLine();
var result = false;
if (first != null)
{
first = first.ToLowerInvariant();
bool.TryParse(first, out result);
}
return result;
}
Más simple imposible: leemos la primera línea y miramos si es true o false. Esa primera línea nos indica si ha ido bien o mal la validación del captcha.
Y listos! Por supuesto en la vista podemos usar Html.ValidationMessage para añadir el mensaje de error en caso de que la validación del captcha sea incorrecta:
@Html.ValidationMessage("Captcha")
El lugar donde coloquemos este llamada a Htm.ValidationMessage es donde aparecerá el mensaje de error en caso de que la validación del captcha sea incorrecta. Por supuesto el parámetro de ValidationMessage es la misma cadena que el valor del atributo Name del ActionFilter (en mi caso Captcha).
Nos falta ver el código de la acción del controlador, pero no tiene ningún secreto:
[HttpPost]
[Recaptcha(Name="Captcha")]
public ActionResult Register(RegisterModel model)
{
if (ModelState.IsValid)
{
// Creamos el usuario y lo autenticamos
}
// Si llegamos aquí hay algun error (puede ser el captcha
// puede ser cualquier otro).
return View(model);
}
Una prueba rápida nos permite ver que efectivamente si el usuario falla el captcha aparece el mensaje de error:

Y eso es todo!
En este post hemos visto como usar un ActionFilter para integrar la validación de Recaptcha en nuestro site de forma sencilla y fácil.
Insisto en lo que os he dicho al principio: hay soluciones ya hechas para integrar Recaptcha, pero a veces está bien ver las cosas desde cero, saber como funcionan e intentar ver como afrontarlas, no? Porque si siempre nos lo dan todo masticado… que gracia tiene?
Un saludo! :D
Más artículos
Página siguiente >