martes, 30 de junio de 2009

ASP.NET MVC y Ajax: fácil no, facilísimo (ii)

Hola a todos! En este post (continuación del post anterior ASP.NET MVC y Ajax) voy a comentaros algunas cosillas más sobre ASP.NET MVC y ajax utilizando jQuery. En este caso vamos a ver como implementar un clásico de Ajax: las combos encadenadas.

Vamos a hacer el manido ejemplo de continentes y paises: seleccionas un continente en la combo y via Ajax se rellena la combo de paises. Vereis que sencillo es crear combos encadenadas con ASP.NET MVC y jQuery.

Lo primero es crear una vista que tenga las dos combos que queramos (yo como siempre modifico la vista inicial (Views/Home/Index.aspx).

El código que he puesto es:

<h1>Ajax y MVC... tan fácil como parece!</h1>
Escoja continente y deje que el poder de Ajax le muestre algunos paises...

<select id="cmbContinente">
<
option value="---" selected="selected">Escoja continente...</option>
<
option value="eur">Europa</option>
<
option value="ame">America</option>
<
option value="asi">Asia</option>
<
option value="afr">Africa</option>
</
select>
<
select id="cmbPaises"></select>

Excelente… ya tenemos las dos combos. Ahora empieza lo interesante: cuando se seleccione un elemento de la combo de continentes vamos  a realizar una petición ajax al controlador, para que nos devuelva que países se corresponden al continente seleccionado…


Como siempre jQuery viene en nuestra ayuda:

<script type="text/javascript">
$(document).ready(function() {
$("#cmbContinente").change(function() {
fillCombo("cmbPaises", $("#cmbContinente").val());
});
});
</script>

Si visteis mi post anterior este código ya no os sonará tan extraño. Si no lo habeis visto, o bien no os acordáis ahí va un rápido resumen: con $(document) obtengo el manejador jQuery al documento actual, y llamo a su método ready. El método ready se ejecuta cuando el documento está “listo” (esto es todo el árbol DOM inicializado), y como parámetro espera una función. En mi caso le paso una función anónima con el código a ejecutar.


Lo que hace la función anónima es muy sencillo. Recordad que en jQuery la expresión $(“#xx”) obtiene un manejador jQuery al elemento DOM cuyo id sea “xx”. Así pues obtengo el manejador jQuery de la combo cuyo id es “cmbContinente” y me suscribo a su evento change. Cuando se usa jQuery generalmente no nos suscribimos a los eventos de los elementos DOM usando la sintaxis clásica onXXX, sinó que usamos las propias funciones de jQuery. En mi caso uso la función change() que se correspondería el evento onChange. La razón de actuar así es que jQuery intenta ofrecer un modelo de eventos unificado entre distintos navegadores. Si veis el código, simplemente se le pasa al método change una función anónima con el código a ejecutar cuando se lance el evento change: una llamada a una función fillCombo  que definiremos luego. A esta fillCombo le pasamos el id de la combo que vamos a actualizar y el valor seleccionado de la combo de continentes. Fijaos que de nuevo usamos jQuery para saber el valor seleccionado de la combo: obtenemos un manejador jQuery a la combo y llamamos al método val().


Bien, vamos ahora a ver el código de la función fillCombo. Esta función coje dos parámetros updateId y value (el id de la combo a rellenar y el valor seleccionado de la primera combo):

<script type="text/javascript">
function
fillCombo(updateId, value) {
$.getJSON("<%= Url.Action("PaisesPorContinente") %>"
+ "/" + value,
function(data) {
$("#"+updateId).empty();
$.each(data, function(i, item) {
$("#"+updateId).append("<option id='"
+ item.IdPais +"'>" + item.Nombre
+ "</option>");
});
});
}
</script>

Veamos que hace exactamente fillCombo:



  • Usa la función getJSON de jQuery. Esta función realiza una llamada a la URL indicada (en mi caso una llamada a la acción PaisesPorContinente), usando AJAX y espera que dicha función devuelva un resultado en formato JSON. Además parsea el resultado JSON y lo convierte en un objeto javascript. Para los que no sepáis que es JSON os diré que es un lenguaje para representar objetos (tal y como puede ser XML) pero mucho más compacto, y además más “compatible” con javascript. Para más información pasaros por json.org.
  • La función getJSON admite dos parámetros: el primero es la URL y el segundo una función a ejecutar cuando se reciba el resultado… Para variar le pasamos una función anónima :)
  • Y la función anónima qué hace?

    • Pues bueno, primero vacía la combo indicada (llama al método empty() que lo que hace es vaciar el árbol DOM de un determinado elemento, por lo que aplicado a un <select> le elimina todas sus <option>).
    • Despues usa el método each de jQuery para iterar sobre la respuesta (asume que lo que se recibe es un array JSON). El método each recibe dos parámetros: el primero es la variable sobre la que iteramos y el segundo la función a ejecutar por cada elemento. En mi caso otra función anónima que usa el método append para añadir una etiqueta <option> a la combo (accediendo a las propiedades IdPais y Nombre de cada elemento del array JSON).

Bueno… hemos terminado la parte de cliente… veamos que debemos hacer en el servidor.


En primer lugar debemos crear la acción PaisesPorContinente en nuestro controlador: Una acción es un método público del controlador, y el nombre del método define el nombre de la acción, aunque esto se puede modificar con el uso del atributo ActionNameAttribute:

[ActionName("PaisesPorContinente")]
public ActionResult GetPaisesPorContinente(string id)
{
var paises = new PaisesModel().GetPaisesPorContinente(id);
return new JsonResult() { Data = paises };
}

Accedemos al modelo y le pedimos que nos devuelva los paises del continente según el id indicado. Luego debemos devolver el resultado de la petición, que ahora no es código html, sinó código JSON. Por suerte la gente del framework MVC sabe que JSON se usa constantemente en AJAX, así que han implementado un serializador para convertir objetos de .NET en objetos JSON. Para devolver un objeto serializado en JSON simplemente devolved un JsonResult con la propiedad Data establecida al objeto que quereis devolver… y listos.


El código de mi modelo es muy simple:

public class Pais
{
public string Continente { get; set; }
public string Nombre { get; set; }
public string IdPais { get; set; }
}

public class PaisesModel
{
private List<Pais> paises;
public PaisesModel()
{
this.paises = new List<Pais>();
this.paises.Add(new Pais()
{
Continente = "eur",
IdPais = "es",
Nombre = "España"
});
// Más y más paises añadidos...
}

public IEnumerable<Pais> GetPaisesPorContinente
(string continente)
{
return this.paises.FindAll
(x => x.Continente == continente);
}
}

Simplemente devuelve una lista de objetos de la clase Pais. Fijaos como la clase Pais tiene las propiedades IdPais y Nombre que usábamos dentro de fillCombo! Y ya hemos terminado! Si quereis ver el proyecto completo, os lo podeis descargar de aquí.


Qué… fácil, no???


Un saludo a todos! ;-)


pd: Por cierto, si vais a trabajar con combos y jQuery os recomiendo que le echeis un vistazo al plugin de jQuery para selectboxes, que añade funciones específicas para trabajar con selects (sí, sí… jQuery puede extenderse con plugins que le añaden todavía más funcionalidades!).


pd: Este post es (para variar) un crosspost desde mi blog de geeks.ms!

viernes, 26 de junio de 2009

ASP.NET MVC y Ajax: fácil no… facilísimo :)

Hola a todos amigos! ;-)

El comentario de Gabriel en este post de mi blog (http://geeks.ms/blogs/etomas/archive/2009/04/02/asp-net-mvc-controles-chart-y-ajax.aspx) me ha motivado a escribir la siguiente entrada.

Él preguntaba sobre si los controles Ajax de ASP.NET, como p.ej. UpdatePanel se podían usar bajo el framework MVC. No conozco mucho los controles de la Ajax Library porque personalmente no me interesan demasiado, aunque apuesto que la mayoría usan viewstate así que me imagino que no deben poder usarse bajo MVC…

… por otro lado sobreentiendo que la duda de Gabriel, va un poco más allá y quiere saber como usar Ajax con el framework MVC. Pues para verlo muy someramente aquí va esta entrada.

El soporte para Ajax del framework MVC no es que sea espectacular: las vistas tienen una propiedad Ajax que permite acceder al objeto AjaxHelper, que contiene varios métodos para permitir usar Ajax en el framework MVC. P.ej. si quieres poner un link que al pulsarlo cargue el div cuyo id sea "ajaxpanel” puedes hacer:

<%=Ajax.ActionLink("Pulsa aquí", "view1", new AjaxOptions() 
{ UpdateTargetId="ajaxpanel"}) %>



Al pulsar sobre el enlace, el framework MVC ejecutará la acción “view1” (del controlador actual) y la vista que esta acción devuelva será incrustada dentro del elemento DOM cuyo id sea ajaxpanel.



No está mal, pero tiene dos pegas:




  1. De serie no nos viene nada más: si queremos usar imágenes que sean enlaces Ajax, o botones, o una combo, debemos hacerlo nosotros.


  2. Usa la librería propia de Ajax de MS (MicrosoftAjax.js y MicrosoftMvcAjax.js)… No tengo nada personal en contra de esta librería, pero no veo muy claro a que viene: ¿teniendo jQuery, para que reinventar la rueda?



Yo personalmente uso sólo jQuery para mi soporte Ajax… ¿Por qué? Pues porqué me da todo lo que necesito… y más. Además de que es cross-browser y no sólo ofrece temas para Ajax, sino para muchas otras cosas. Si estás desarrollando web y no usas jQuery cuando empieces te preguntarás como has podido estar sin ella todo este tiempo.



Usar jQuery implica que no usamos las funciones del AjaxHelper, pero bueno no importa demasiado: rehacerlas usando jQuery no cuesta nada… ah! Y además jQuery ya viene con el framework MVC (en la carpeta /scripts).



En esta demo, veremos como hacer en un momento una vista con dos botones: al pulsar cada uno de ellos se cargará via Ajax una vista y se incrustará dentro de un div.



1. Inicio: Modificación de la vista master



Creamos una nueva aplicación ASP.NET MVC. Ello nos creará la aplicación inicial, con varias vistas (login, home, registro) para empezar a trabajar. Vamos a modificar directamente la vista inicial (Views/Home/Index.aspx).



Lo primero a hacer es incluir la carga de jQuery en nuestra vista. En mi caso modifico la vista master, para que jQuery esté incluida “de serie” en todas mis vistas… yo la uso a mansalva, y creedme: vais a terminar haciendo lo mismo :)



La vista master está en Views/Shared/Site.Master. Si la abrís vereis que tiene un poco de código. Podeis obviarlo, simplemente añadid una etiqueta <script> dentro del <head>:



<head runat="server">
<
title><asp:ContentPlaceHolder ID="TitleContent" runat="server" />
</
title>
<
link href="../../Content/Site.css"
rel="stylesheet" type="text/css" />
<%-- Incluimos jQuery --%>
<script type="text/javascript" src="../../Scripts/jquery-1.3.2.js">
</
script>

</
head>



(En este caso incluyo jquery-1.3.2.js que es la versión que viene junto con el framework MVC).



2. Modificación de la vista inicial



Como comenté en el punto anterior vamos a trabajar modificando directamente la vista inicial (Views/Home/Index.aspx). Para ello vamos a añadir simplemente dos botones y un <div> vacío que será nuestro contenedor ajax:



<%@ Page Language="C#" MasterPageFile="~/Views/Shared/Site.Master" 
Inherits="System.Web.Mvc.ViewPage" %>

<asp:Content ID="indexTitle" ContentPlaceHolderID="TitleContent"
runat="server">
Home Page
</asp:Content>
<
asp:Content ID="indexContent" ContentPlaceHolderID="MainContent" runat="server">
<h2>Pulsa los botones para refrescar el div usando ajax</h2>
<
input type="button" id="view1" value="Vista 1" />
<
input type="button" id="view2" value="Vista 2"/>
<%-- Este es el div que vamos a modificar via ajax --%>
<div id="ajaxpanel" style="border-color:red; border-style:solid;
border-width:thin"></div>
</
asp:Content>



He puesto un border rojo al div para que vea (sí, reconozco que el diseño no es mi fuerte :p).



2.1 Crear las llamadas Ajax al pulsar los botones



Ahora vamos a añadir código javascript para que cuando se pulsen los botones se hagan las llamadas ajax… Si ya estás corriendo a meter un onClick en cada <input> tranquilo: bienvenido al mundo de jQuery :)



Este es el código javascript que puedes colocar en tu página (justo antes del tag <h2>, dentro del asp:Content IndexContent):



<script type="text/javascript">
$(document).ready(function() {
$("#view1").click(function() {
$("#ajaxpanel").load("<%=Url.Action("View1") %>");
});
$("#view2").click(function() {
$("#ajaxpanel").load("<%= Url.Action("View2") %>");
});
});
</script>



Vamos a comentar rápidamente este código… aunque es muy simple muestra dos conceptos clave de jQuery: los selectores y la potencia de sus funciones incorporadas.



El $ es el simbolo “jQuery” por excelencia. $(document) me devuelve un manejador al documento actual. La función ready() espera como parámetro otra función que se ejecutará cuando el documento esté cargado y todos los objetos DOM existan.



En mi caso le paso a la función ready una función anónima que hace lo que a mi me interesa que se haga cuando el documento esté cargado: crear funciones gestoras de los click de los botones para que se llame via Ajax a otras URLs.



El código $("#view1") es un selector: los selectores son una de las claves de jQuery, ya que permiten obtener un manejador jQuery a uno o varios objetos DOM para realizar tareas con ellos. Aquí estoy usando uno de los más simples, el # que devuelve un manejador al objeto DOM cuyo ID sea la cadena que hay después de #. Así $(“#view1”) me devuelve un manejador al objeto DOM cuyo id sea “view1”, que en mi caso es el primer boton.



Una vez tengo un manejador de jQuery puedo hacer barbaridad de cosas con él (que afectarán al objeto (u objetos) DOM a los que apunte dicho manejador). En mi caso llamo a la función click que espera como parámetro otra función. La función click lo que hace es ejecutar la función que se pase como parámetro cuando se lance el evento click del elemento DOM subyacente… Y que le paso como parámetro a la función click? Pues otra función anónima con el código a ejecutar cuando se lance el evento. ¿Y qué codigo es este? Pues usar un selector para obtener un manejador al objeto cuyo ID sea “ajaxpanel” (que es el <div>) y llamar el método load. El método load usa Ajax para cargar la URL especificada y incrustar el resultado dentro del DOM del manejador usado. Es decir, en nuestro caso dentro del div.



Ya casi estamos…



3. Crear las acciones en el controlador



En ASP.NET MVC las URLs se mapean a acciones de los controladores, no a archivos físicos .aspx. Si os fijais en el codigo de la vista se ha usado <%= Url.Action(“xxx”) %> para pasar la URL al método load de jQuery. Esta llamada a URL.Action obtiene la URL de la acción indicada del controlador actual. En mi caso he llamado a las acciones “View1” y “View2”.



Dado que estamos en una vista gestionada por el controlador Home, debemos crear las acciones en el controlador HomeController. La forma más simple de crear una acción es definir un método público en dicho controlador. Así, pues definimos los métodos View1 y View2 en HomeController (que está dentro de la carpeta Controllers):



public ActionResult View1()
{
return PartialView("View1");
}
public ActionResult View2()
{
return PartialView("View2");
}



En este caso las dos acciones son bastante tontas: el controlador no hace nada salvo retornar dos vistas, llamadas View1 y View2. El método PartialView se usa cuando lo que se va a devolver es una vista parcial, esto es una vista que no es un HTML completo sino una parte.



Solo nos queda una pequeña cosa… crear las dos vistas!



4. Crear las dos vistas parciales



Vamos a crear las dos vistas que necesitamos. Dado que estamos creando vistas del controlador Home, debemos crearlas dentro de Views/Home. Para ello hacemos click con el botón derecho en la carpeta Home en el Solution Explorer y seleccionamos la opción Add->View. Esto nos despliega el cuadro de nueva vista de ASP.NET MVC.



image



Damos nombre a la vista (View1) y marcamos la checkbox de “Create a partial view”. Con ello nos va a crear un archivo .ascx, en lugar de un .aspx (que sería una página completa).



Yo he añadido el siguiente código a mi vista View1:



<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl" %>
<h1>Esta es la vista1</h1>



Ya veis… poca cosa, no?



Repetid el proceso para crear la View2 y… ya habeis terminado.



Ejecutad el proyecto y os aparecerá la página principal, con los dos botones. Pulsad uno u otro y observad como el div se rellena via Ajax con el código de cada vista.



En la última imagen os dejo una captura con el firebug donde se ve la última petición ajax:



image



Esto ha sido una muy breve introducción… pero espero que os haya servido para que os entre el gusanillo de ASP.NET MVC!!! ;-)



Saludos!



pd: Sí… és otro crosspost desde mi blog en geeks.ms

jueves, 18 de junio de 2009

Combo para seleccionar colores (Winforms + C#)

Hola... qué tal?

Imagina que en algún proyecto que estés haciendo, quieres ofrecer una combo para seleccionar colores. De acuerdo, ya se que hay otros métodos para hacer que el usuario seleccione un color, como usar el ColorDialog, pero a lo mejor te interesa que el usuario sólo pueda escoger colores de una lista predeterminada...

Por suerte en .NET hacer que una combo dibuje sus elementos como nosotros queremos, es realmente simple... ¿quieres tener una combo como esta?

image

Pues, ya verás que fácil... apenas cinco líneas de código, y será tuya...

Paso 1: Añadir la combo al formulario

Añade una ComboBox normal a tu proyecto, y establece las propiedades DropDownStyle a DropDownList (para que el usuario no pueda teclear valores nuevos) y DrawMode a OwnerDrawFixed.

La propiedad DrawMode es la clave: Puede tener tres valores, que son "Normal" (el sistema operativo se encarga de dibujar la combo), "OwnerDrawFixed" (nosotros nos encargamos de dibujar cada elemento) y "OwnerDrawVariable" (nosotros nos encargamos de dibujar cada elemento y además cada elemento puede tener un tamaño distinto).

Paso 2: Código para dibujar cada elemento

Crea una función gestora para el evento DrawItem: la combo lanza este evento cada vez que debe dibujar un elemento. La función gestora recibe un objeto DrawItemEventArgs que contiene toda la información que necesitamos para poder "dibujar" el elemento: de que elemento se trata, el contexto gráfico a usar y las coordenadas que ocupa el rectángulo del elemento dentro del contexto gráfico de la combo...

Las primeras líneas de la función gestora serían las siguientes:

ComboBox cmb = sender as ComboBox;
if (cmb == null) return;
if (e.Index < 0) return;
if (!(cmb.Items[e.Index] is Color)) return;
Color color = (Color)cmb.Items[e.Index];



Aquí simplemente estamos asegurándonos de que el elemento que vamos a dibujar sea un objeto Color. Para ello accedemos a la propiedad Index del objeto DrawItemEventArgs que recibimos que nos indica el índice del elemento que estamos dibujando.



Ahora solo nos queda dibujar el elemento: un rectángulo con los bordes negros y rellenado del color indicado, junto al nombre del color:



Brush brush = new SolidBrush(color);
e.Graphics.DrawRectangle(
Pens.Black,
new Rectangle(e.Bounds.Left + 2, e.Bounds.Top + 2, 19,
e.Bounds.Size.Height - 4));
e.Graphics.FillRectangle(brush,
new Rectangle(e.Bounds.Left + 3, e.Bounds.Top + 3, 18,
e.Bounds.Size.Height - 5));
e.Graphics.DrawString(color.Name, cmb.Font,
Brushes.Black, e.Bounds.Left + 25, e.Bounds.Top + 2);
brush.Dispose();



El primer DrawRectangle dibuja el borde del rectángulo, el FillRectangle pinta su interior y el DrawString escribe el nombre del color. Finalmente recordad que debemos hacer dispose de cualquier objeto GDI+ que usemos.



Como se puede observar, apenas cinco líneas de codigo GDI+.



Paso 3: Perfeccionando el tema...



La combo ya es 100% funcional, pero si la usais vereis que "no resalta" en azul el elemento seleccionado, tal y como hace por defecto la combobox... Evidentemente no lo hace porque no hemos puesto código para ello, pero tranquilos: son apenas tres líneas de código.



El propio objeto DrawItemEventArgs tiene todo lo que necesitamos para poder hacerlo:




  1. El método DrawBackground() que dibuja el fondo del elemento actual: en blanco en azul en función de si tiene el cursor del ratón encima o no.


  2. La propiedad ForeColor que nos devuelve el color con el que dibujar el elemento actual (por defecto es negro si no tiene el cursor del ratón encima o blanco en caso contrario).


  3. La propiedad BackColor que nos devuelve el color que se usa en el método DrawBackground.



El código final para dibujar nuestra combo es el siguiente:



private void cmbColor_DrawItem(object sender, 
DrawItemEventArgs e)
{
ComboBox cmb = sender as ComboBox;
if (cmb == null) return;
if (e.Index < 0) return;
if (!(cmb.Items[e.Index] is Color)) return;
Color color = (Color)cmb.Items[e.Index];
// Dibujamos el fondo
e.DrawBackground();
// Creamos los objetos GDI+
Brush brush = new SolidBrush(color);
Pen forePen = new Pen(e.ForeColor);
Brush foreBrush = new SolidBrush(e.ForeColor);
// Dibujamos el borde del rectángulo
e.Graphics.DrawRectangle(
forePen,
new Rectangle(e.Bounds.Left + 2, e.Bounds.Top + 2, 19,
e.Bounds.Size.Height - 4));
// Rellenamos el rectángulo con el Color seleccionado
// en la combo
e.Graphics.FillRectangle(brush,
new Rectangle(e.Bounds.Left + 3, e.Bounds.Top + 3, 18,
e.Bounds.Size.Height - 5));
// Dibujamos el nombre del color
e.Graphics.DrawString(color.Name, cmb.Font,
foreBrush, e.Bounds.Left + 25, e.Bounds.Top + 2);
// Eliminamos objetos GDI+
brush.Dispose();
forePen.Dispose();
foreBrush.Dispose();
}



Y listos!! Nuestra combo ya está lista para usar. Podeis meterle algunos colores para probar:



cmbColor1.Items.Add(Color.Black);
cmbColor1.Items.Add(Color.Blue);
cmbColor1.Items.Add(Color.Red);
cmbColor1.Items.Add(Color.White);
cmbColor1.Items.Add(Color.Pink);
cmbColor1.Items.Add(Color.Green);



Y este es el resultado final:



image



¿No está nada mal, eh?



Saludos a todos!



pd: Pues sí... esto es otro crosspost desde mi blog de geeks.ms!

viernes, 12 de junio de 2009

C# y sobrecarga de métodos genéricos… un detallito

A veces hay aspectos de C# que no pensamos hasta que nos encontramos con ellos… A mi me pasó con un código parecido a este:

class Program
{
static void Main(string[] args)
{
Baz baz = new BazDerived();
new Foo().Bar(baz);
Console.ReadLine();
}
}
class Foo
{
public void Bar<T>(T t)
{
Console.WriteLine("Bar<T> typeof(T) = " + typeof(T).Name);
}
public void Bar(object o)
{
Console.WriteLine("Bar o.GetType() = " + o.GetType().Name);
}
}
class Baz { }
class BazDerived : Baz { }

La pregunta seria: ¿cual es la salida por pantalla de este programa?


Pues… esta es la respuesta (para los que querais pensar un poco la he puesto en negro sobre negro… seleccionad el texto para verlo):



Bar<T> typeof(T) = Baz


La verdad es que tiene toda su lógica… lo que personalmente no me gusta nada es que este código compile sin generar ningún warning. ¿Que opinais vosotros?


Saludos!!!


pd: Otro crosspost de mi blog en geeks.ms… para variar ;-)

miércoles, 3 de junio de 2009

XmlSerializer, colecciones y auto-propiedades…

Que XmlSerializer es una clase curiosa es evidente, hay multitud de maneras de controlar la serialización de un objeto y varios trucos más o menos ocultos (os recomiendo el blog de jmservera que tiene algunos posts interesantes)…

… Lo que quiero comentaros ahora es un caso que me encontré el otro día (valeeee… ayer), en concreto con las auto-propiedades que se incorporaron en C# 3.0.

En la msdn se dice que XmlSerializer es capaz de serializar propiedades ICollection o IEnumerable que sean read-only (de hecho la regla CA2227 del análisis estático se basa en esto). Pues bien, esto es cierto si entendemos como read-only que no haya setter… ni tan siquiera privado.

Es decir, esto NO funciona:

class Program
{
static void Main(string[] args)
{
XmlSerializer xml = new XmlSerializer(typeof(Foo));
using (FileStream fs = new FileStream(@"C:\temp\foo.xml",
FileMode.Open, FileAccess.ReadWrite, FileShare.None))
{
xml.Serialize(fs,new Foo());
fs.Close();
}

}
}
public class Foo
{
public Foo()
{
this.Data = new List<Bar>();
this.Data.Add (new Bar());
this.Data.Add (new Bar());
}

public List<Bar> Data { get; private set; }
int OtherData;
}
public class Bar {}

Si intentamos serializar (o deserializar da igual) recibimos una System.InvalidOperationException (con un mensaje "No se puede generar una clase temporal (result=1). error CS0200: No se puede asignar la propiedad o el indizador 'ConsoleApplication6.Foo.Data' (es de sólo lectura)".


Si transformamos la clase Foo al estilo de C# 2.0 sin usar auto-propiedades, el código funciona, siempre que no pongamos el setter privado…


… como mínimo curioso, no?


Es evidente que cuando se hizo XmlSerializer nadie pensó en un setter de propiedad privado (ya que entonces lo usual era no poner setters privados en propiedades read-only), pero con la aparición de las auto-propiedades en C# 3.0 estos son cada vez más frecuentes… así que igual habría que actualizar XmlSerializer, porque que me obliguen a usar el estilo de C# 2.0 para poder serializar las propiedades de colección no es que me guste especialmente.


Y quizá, ya puestos a pedir, C# debería incorporar auto-propiedades read-only sin necesidad de poner el setter privado y que tuviesen la misma semántica que las variables readonly: sólo podrían ser inicializadas en el constructor. Esto permitiría también que el CLR realizara determinadas optimizaciones…


Saludos!! ;-)


Como siempre esto es un crosspost de mi blog en geeks.ms ;-)