El punto de inflexión para .NET Client Profile

Recientemente, hemos comenzado la preparación del despliegue de la aplicación de escritorio (basada en .NET Framework 3.5 SP1) que estamos terminando de desarrollar. Como la aplicación debe poder instalarse en equipos con Windows XP SP2 ó SP3 que no tengan .NET Framework instalado de antemano, la instalación de la aplicación debe incluir el despliegue de éste. Y fue en este punto donde nos pareció razonable la idea de utilizar .NET Client Profile, un subconjunto muy streamlined de .NET que reduce en buena medida el tiempo de instalación en los equipos de destino. Pero rápidamente nos dimos cuenta de que eso no iba a ser posible para nuestra aplicación. Después de crear un instalador y ejecutarlo sobre una máquina XP, la aplicación falló al ser lanzada, mostrando la siguiente excepción:



Resulta que nuestra aplicación utiliza una librería de componentes de un conocido fabricante (después de esto, he comprobado que otros paquetes de componentes populares “adolecen” del mismo problema). Este fabricante, en aras de ofrecernos una mayor potencia durante el desarrollo, incorpora en sus clases posibilidades pensadas para ser utilizadas por nosotros durante tiempo de diseño dentro de Visual Studio. Y esas posibilidades se apoyan en la utilización de un ensamblado, System.Design.dll, que no forma parte del Client Profile. Si se “desensambla” System.Design.dll con Reflector o con el propio Visual Studio, quedará claro por qué Microsoft no incluyó este ensamblado en el Client Profile. Este ensamblado concentra recursos para el diseño de aplicaciones y servicios de todo tipo, y depende estáticamente de un mogollón de otros ensamblados (incluyendo System.Web.dll); el propio concepto de Client Profile moriría si todo eso formara parte de él.


Pensando sobre el tema, me parece interesante la cuestión de si se resolverá (y cómo) este problema en el futuro. Si estos fabricantes de componentes quisieran dar soporte al Client Profile sin quitarnos las facilidades de tiempo de diseño de las que ya disponemos, deberían modificar sus librerías para que no tengan una dependencia estática de System.Design.dll, sino que carguen este ensamblado dinámicamente (mediante Reflection) cuando sea necesario (que será solo en tiempo de diseño). Otra alternativa sería ofrecer la posibilidad de generar directamente versiones de las librerías de componentes que no dependan de ese ensamblado, mediante una combinación de directivas de compilación condicional y comandos de generación (recuerde que estas librerías se venden con código fuente). Pero, ¿se tomarán ese trabajo esos fabricantes? .NET Client Profile tiene sentido principalmente para equipos basados en XP, puesto que Windows Vista ya incluye de serie .NET 2.0 y 3.0. ¿No preferirán “hacer la vista gorda” y esperar a que termine el soporte para Windows XP? El tiempo dirá.

"Microsoft .NET Framework 3.5 – WPF" (Preparación para el examen MCTS 70-502)

Recién acabo de leer “Microsoft .NET Framework 3.5 – Windows Presentation Foundation”, de Matthew Stoecker, publicado en julio de este año por Microsoft Press (solo disponible en inglés, por el momento). Como bien se indica en la portada, se trata de un manual destinado a facilitar la preparación para uno de los nuevos exámenes de certificación asociados a .NET Framework 3.5, el relacionado con WPF, y como tal incluye adicionalmente un CD que ofrece, entre otros goodies, un muy renovado “motor de exámenes” que gestiona un banco de más de 300 preguntas similares a las que se presentan en los exámenes. Como es típico en estos libros, cada lección (hay varias por capítulo) termina con un cuestionario (las respuestas están disponibles al final del libro) y sugerencias para tareas prácticas.


El libro consta de los siguientes capítulos:


0: Introduction


1: WPF Application Fundamentals


2: Events, Commands and Settings


3: Building the User Interface


4: Adding and Managing Content


5: Configuring Databinding


6: Converting and Validating Data


7: Styles and Animation


8: Customizing the User Interface


9: Resources, Documents, and Localization


10: Deployment


11: Answers to Questions


En general, me parece un excelente recurso de aprendizaje, que presenta de manera clara y coherente cada uno de los temas, y que probablemente contenga bastante más que lo suficiente para pasar con éxito el examen MCTS 70-502 (esto último pienso comprobarlo próximamente :-). En cualquier caso, recomiendo otros dos excelentes libros, “WPF Unleashed” de Adam Nathan y “Applications = Code + Markup” de Charles Petzold, para complementar la visión que se adquiera con éste.


Si desea obtener una información sumamente completa sobre las nuevas certificaciones para desarrolladores en tecnologías Microsoft, le recomiendo leer un excelente artículo que José Manuel Alarcón escribió hace unos meses para dotNetManía, y que está disponible aquí: Nuevas certificaciones Microsoft.

Observaciones con respecto a un método extensor ForEach<T>

Recientemente, Valeriano Tórtola publicó un excelente post en el que mostraba la utilización de un método extensor para IEnumerable<T> llamado ForEach<T> para aplicar una misma modificación a todos los elementos de una secuencia obtenida como resultado de una consulta de LINQ to DataSets.


El método extensor en cuestión proviene del blog de Glenn Brock, Program Manager en Microsoft, y su código fuente es el siguiente:


   public static class IEnumerableUtils
   {
      public static void ExtForEach<T>(this IEnumerable<T> collection, Action<T> action)
      {
         foreach(T item in collection)
            action(item);
      }
   }


Lo primero que me llamó la atención al ver el método es que no es “encadenable”: siendo void su tipo de retorno, deberá ser usado obligatoriamente al final de la “cadena alimenticia” de una expresión de consulta, como ha hecho Valeriano en su ejemplo. Pero en el fondo, no hay nada malo en esto: otros métodos extensores como Count o Sum tampoco son encadenables. Y en el fondo, el objetivo de este método es hacer algo “procedimental” sobre cada elemento de la secuencia de entrada; para todo lo demás, ya tenemos a MasterCardSelect :-). Observe además que el método no es lazy (de ejecución on demand): se “traga” toda la secuencia de entrada ipso facto. Pero tampoco podría serlo, si no devuelve nada.


He renombrado el método a ExtForEach para evitar colisiones con el método ForEach de las listas genéricas que usaré en los ejemplos. Y éste es el primer posible señalamiento a hacerle al método: su nombre. Se producirá una colisión de nombres al aplicar ForEach directamente (sin ninguna otra llamada por medio) a listas genéricas, que ya tienen un ForEach, y en ella saldrá perdiendo el método extensor, que tiene la menor prioridad. Aunque podría aducirse que ese comportamiento es precisamente el adecuado, ello podría ser fuente de confusión.


Pero lo que creo que menos me gusta de este método extensor es que puede producir la falsa sensación de que puede ser utilizado de manera universal para realizar modificaciones cualesquiera sobre los elementos de una secuencia, sin que el compilador pueda ser de ninguna ayuda al respecto. Más de un principiante será “mordido” por esto:


   List<string> strList = new List<string> { “ana”, “barbara”, “celia”, “diana” };
   strList.ExtForEach( s => { s = s.ToUpper(); });
   // parafraseando a JI, “la lista sigue igual”
   strList.ExtForEach(s => { Console.WriteLine(s); });


Al menos, cuando se intenta esto:


   foreach (string s in strList)
      s = s.ToUpper();


el compilador advierte correctamente de que no se puede asignar a la variable de iteración del bucle.


Tal vez lo que esté yo echando en falta aquí es el modificador const de los métodos en C++ (oh no, Rafa, not again! :-).


En resumen, que si yo estuviera en situación de tomar esa decisión, creo que NO agregaría este método a la librería de .NET. Si este ExtForEach o como se llame siempre va a estar al final de la cadena de consulta y va a recorrer toda la secuencia de entrada, pues que los programadores utilicen la construcción foreach del lenguaje cuando necesiten hacer algo así. El ejemplo de Valeriano quedaría como sigue:


foreach(var EmployeeRow in EmployeesTable.
      Where(
         EmployeeRow => !EmployeeRow.IsNull(“Country”) &&
         EmployeeRow.Field<String>(“Country”) == “UK” &&
         !EmployeeRow.IsNull(“FirstName”) &&
         EmployeeRow.Field<String>(“FirstName”) != String.Empty))
   EmployeeRow.SetField<String>(“Initial”,
      EmployeeRow.Field<String>(“FirstName”).Substring(0, 1));


Se parece bastante, ¿no?