El Calendario que le estaba faltando a WPF
Transcripción
El Calendario que le estaba faltando a WPF
www.dotnetmania.com nº 45 febrero 2008 6,50 € Visual Basic • C# • ASP.NET • ADO.NET • SQL Server • Windows System dotNetManía dedicada a los profesionales de la plataforma .NET El Calendario que le estaba faltando a WPF El Marco de trabajo de entidades de ADO.NET v3.5 (II) • VS 2005 Team Edition para profesionales de bases de datos (y II) • Manejadores de eventos en SharePoint 2007 • Componentes de uso general. Acceso a datos • Una isla para Visual Basic en dotNetManía Comunidad.NET AndorraDotNet TodotNet@QA Cachés globales y Silverlight Laboratorio.NET Visual Guard .NET entrevista Don Syme Investigador Microsoft Research editorial dotNetManía Dedicada a los profesionales de la plataforma .NET Vol. III •Número 45 • Febrero 2008 Precio: 6,50 € dotNetManía, año 5to. Redactor jefe Marino Posadas ([email protected]) Editor técnico Octavio Hernández ([email protected]) Redacción Dino Esposito, Guillermo 'Guille' Som, José Manuel Alarcón, Luis Miguel Blanco y Miguel Katrib (Grupo Weboo) Empresas Colaboradoras Alhambra-Eidos Krasis Plain Concepts Raona Solid Quality Learning Además colaboran en este número Daniel Seara, Gustavo Vélez, Iskander Sierra, José Luis Latorre, Mario del Valle, Rubén Garrigós y Unai Zorrilla. Corresponsal para América Latina Pablo Tilotta Ilustraciones Mascota (Clico): Yamil Hernández Portada: Javier Roldán Fotografía Roberto Mariscal Atención al suscriptor Pilar Pérez ([email protected]) Edición, suscripciones y publicidad .netalia c/ Thomas Edison, 4 Oficina 1406 Parque empresarial Rivas Futura 28521 - Rivas Vaciamadrid (Madrid) www.dotnetmania.com Tf. (34) 91 666 74 77 Fax (34) 91 499 13 64 Imprime Gráficas MARTE ISSN 1698-5451 Bienvenido al número 45, de febrero de 2008, de dotNetManía. Con éste comenzamos el quinto volumen, y con él nuestra declaración de intenciones para este año: dotNetManía online. Empecé a editar contenidos en formato digital en 1991 (clippeRmanía); desde ese momento hasta la fecha he participado en varios proyectos de contenidos digitales que no terminaron de cuajar, a pesar de las evidentes ventajas... ¿quizá no era el momento? Ahora sí creo que pueda ser el momento de combinar la documentación impresa con la documentación online, y éste será nuestro principal propósito en los próximos meses. Durante los días 26 y 27 de febrero se celebrará a nivel mundial el lanzamiento de Visual Studio 2008, junto con el de SQL Server 2008 y de Windows Server 2008, en el que será, posiblemente, el evento más importante en la historia de Microsoft. Me consta que en España se están haciendo enormes esfuerzos para conseguir una afluencia de miles de profesionales. La cita será en el Palacio de Congresos de Madrid y el evento se llama Microsoft Tech Days. Y hablando de eventos, este mes informamos de Envision, un evento para diseñadores celebrado en Milán. Que una revista para desarrolladores como es ésta cubra, aunque sea mínimamente, un evento para diseñadores, puede ser síntoma de que algo está cambiando. Que en un evento para diseñadores se hable de interfaces de usuario, aplicaciones móviles, Web 2.0..., lo confirma. Este mes entrevistamos a Don Syme, principal artífice de F#, un lenguaje con capacidades para el cálculo matemático. Don nos demuestra cómo la pasión por lo que uno hace es el mejor argumento comercial. Marino Posadas no deja de hablar maravillas de este lenguaje desde que entrevistó a Don y sin duda convencerá al más pintado. Tenga cuidado con su lectura, no vaya a convencerle también. Si despierta su interés y quiere conocer más sobre éste u otros lenguajes .NET, le recomiendo una visita a http://www.dotnetlanguages.net. El Grupo Weboo vuelve a ocupar la portada con “El calendario que le estaba faltando a WPF”, continuando con la saga relacionada con elementos avanzados de WPF. En este caso, nos explican cómo definir un control personalizado desde cero, usando como ejemplo el gadget Calendario de Windows Vista y mostrando cómo se implementaría en WPF. Interesante artículo el que Gustavo Vélez publica bajo el largo y descriptivo título: “Manejadores de eventos en SharePoint 2007. Los flujos de trabajo no siempre son necesarios para hacer que las cosas funcionen bien”. Con la integración de Workflow Foundation y SharePoint 2007, la utilidad que los manejadores de eventos tenían a la hora de crear flujos de trabajo desaparece en su mayor parte. Sin embargo, Gustavo nos describe las tareas donde usar manejadores de eventos en lugar de flujos de trabajo es más conveniente. Finalmente, Guillermo “Guille” Som se ha comprado una isla, la isla de Visual Basic en dotNetManía, y la ha llamado Isla.VB. Una columna o reducto para los programadores que quieran ver código Visual Basic de la mano del viejo maestro, que dice estar rodeado de “cesharperos” por todas partes. Depósito Legal M-3.075-2004 Paco Marín << dotNetManía Editor Paco Marín ([email protected]) 3 sumario 45 Entrevista a Don Syme 10-12 En el pasado Tech-Ed se hablaba mucho de un nuevo lenguaje de la familia #. Ya son 4 ó 5 (F#, S#, P#, etc.), pero éste en concreto dispone de características para el cálculo matemático que cualquier científico valorará. Su artífice principal es Don Syme, investigador en Microsoft Research Cambridge (Reino Unido), cabeza del equipo de investigación de aspectos avanzados del CLR en su versión 2.0, amable persona y excelente demostrador de las cualidades de “su lenguaje”. El Marco de trabajo de entidades de ADO.NET v3.5 (II) 14-17 En nuestro anterior artículo [1] hicimos una introducción al Marco de entidades de ADO.NET 3.5, en la que se trató de dar especial importancia a la capacidad de éste para crear una capa de abstracción sobre el modelo relacional. Esta abstracción, a la postre, nos permitirá centrarnos en una programación más conceptual, enfocada en el dominio del negocio que deseamos modelar. En esta entrega y las siguientes veremos cómo modelar nuestro dominio de trabajo y cómo mapear el mismo contra el almacén relacional sobre el que estemos trabajando. VS 2005 Team Edition para profesionales de bases de datos (y II) 18-22 Visual Studio 2005 Team Edition for Database Professionals es la herramienta para profesionales de base de datos de la plataforma Visual Studio. Desplegar nuevos entornos de datos, refactorizar objetos, realizar pruebas unitarias de la base de datos... todo se vuelve sencillo con DataDude. El Calendario que le estaba faltando a WPF 24-30 Con este artículo continúa la saga relacionada con elementos avanzados de WPF. En el último de estos trabajos, vimos cómo definir un control personalizado, al presentar un “scroll circular”. Sin embargo, en aquel caso realmente bastó con asociarle las plantillas y estilos adecuados al control básico ScrollBar ya existente en WPF. Manejadores de eventos en SharePoint 2007. Los flujos de trabajo no siempre son necesarios para hacer que las cosas funcionen bien. 32-36 Los manejadores de eventos de SharePoint 2007 son más que una herencia de la versión 2003: en WSS y MOSS 2007 han sido ampliados y enriquecidos considerablemente. Ahora pueden ser utilizados a lo largo y ancho del sistema, y su modelo de objetos ha sido mejorado en diferentes direcciones. Los manejadores de eventos permiten realizar algunas de las tareas que los flujos de trabajo están en capacidad de ejecutar, pero de una forma mucho más sencilla y efectiva, e inclusive pueden anticiparse a eventos, algo que es difícil de realizar con flujos de trabajo. Componentes de uso general.Acceso a datos 38-42 Comenzamos aquí el desarrollo del primer componente de funcionalidad compleja. Dada la importancia que tiene a la hora de desarrollar aplicaciones, comenzaremos con el acceso a datos, explicando según se desarrolla los fundamentos y consideraciones en los que basa, para que se pueda usar, modificar o repensar completamente y que os pueda ser realmente útil. Una isla para Visual Basic en dotNetManía. O cómo hacer las cosas mejor con Visual Basic. 44-48 Con el quinto año de dotNetManía estrenamos una nueva columna en la revista dedicada exclusivamente a los que gustan desarrollar con Visual Basic. No es que antes no hubiera nada para Visual Basic, pero ya iba siendo hora de que tuviésemos nuestro propio rincón. En esta isla tendremos ocasión de ver muchas cosas relacionadas con Visual Basic, particularmente con la versión 9.0, que es la que se incluye en Visual Studio 2008, aunque muchos conceptos serán válidos también para las versiones anteriores. dnm.todotnet.qa Cachés globales y Silverlight 49-51 dnm.laboratorio.net 52-54 Visual Guard .NET dnm.biblioteca.net 55 Silverlight 1.0 Unleashed Silverlight 1.0 dnm.comunidad.net 56-57 AndorraDotNet. Grupo de usuarios .NET de Andorra dnm.desvan 58 <<dotNetManía noticias noticias noticias noticias noticias 6 Microsoft Ibérica presenta Visual Studio 2008 en castellano La compañía presentó la versión en castellano de Visual Studio 2008 y de .NET Framework 3.5, como paso previo al Microsoft Tech Days, el mayor evento de lanzamiento realizado hasta la fecha por Microsoft, en el que además de Visual Studio 2008 se presentarán SQL Server 2008 y Windows Server 2008. Microsoft Ibérica presentó el pasado 15 de enero la versión en castellano de Visual Studio 2008 y de .NET Framework 3.5. Según Enrique Fernández-Laguilhoat, director de Plataforma y Desarrollo de Microsoft Ibérica, “Visual Studio 2008 posibilita una nueva generación de aplicaciones con una experiencia de usuario revolucionaria, ya que proporciona toda la potencia de la plataforma Windows Presentation Foundation, con la productividad del IDE (Integrated Development Environment), y una integración con nuestras herramientas de diseño, Microsoft Expression, que permiten la colaboración entre desarrollador y diseñador. Por su parte, los servicios online –que Microsoft lleva impulsando desde el nacimiento de .NET en 1999– están transformando el mercado TIC. Nuestra compañía ha encontrado en este mundo de servicios a través de Internet otra forma de seguir proporcionando valor al cliente, combinándolos de forma equilibrada con nuestras ofertas líderes de software de sobremesa y servidor. Se trata de ofrecer a nuestros partners nuevas vías de hacer negocio sobre una plataforma que liderará el futuro de Internet. Visual Studio 2008 permite desarrollar este tipo de aplicaciones de una forma productiva, ágil e integrada con tecnologías como LINQ, WCF y WF.” El mercado de desarrollo de software en España Enrique Fernández-Laguilhoat aprovechó para dar cifras del estado actual del mercado de desarrollo en España, además de dar un repaso a todas las novedades de Visual Studio 2008 y un rápido repaso a los avances de los últimos años, desde las tecnologías cliente-servidor, las aplicaciones distribuidas, más tarde servicios Web y finalmente entramos en el concepto de software+servicios en el que se engloba Visual Studio 2008. Actualmente, el mercado del desarrollo de software cuenta con 97.500 desarrolladores profesionales en España (algunas fuentes estiman esta cifra en 130.000), de los cuales 61.500 están repartidos en 8.500 empresas de desarrollo a medida, 7.000 son programadores en 1.200 ISV y 29.000 son desarrolladores corporativos (grandes bancos, organismos oficiales, con desarrolladores en nómina). Según la compañía, el 70% de los desarrolladores ha adoptado la tecnología .NET, y en cifras globales los desarrolladores de .NET y los desarrolladores de Java están prácticamente a la par en estos momentos. La comunidad de desarrolladores en España ha venido creciendo y organizándose, de modo que hasta la fecha existen 20 grupos de usuarios con 4.000 miembros y 39 MVP certificados. Por su parte, MSDN online ha registrado 30.000 inscripciones y en los programas de betatesting y pruebas han participado 11.000 desarrolladores. Además, se han realizado 35.000 y 2.800 descargas de las versiones Express y de prueba de Visual Studio, respectivamente. Microsoft Tech Days: presentación Mundial de Visual Studio 2008,Windows Server 2008 y SQL Server 2008 Los días 26 y 27 de febrero son los designados para el lanzamiento mundial de Visual Studio 2008, SQL Server 2008 y Windows Server 2008 y. El evento en español tendrá lugar en el Palacio Municipal de Congresos de Madrid. Junto con la presentación de estas tres nuevas soluciones, se contactará con la presentación que Steve Ballmer realizará ese mismo día en Estados Unidos. También se contará con la presencia de Rosa Mª García, presidenta de Microsoft Ibérica, además de los responsables del área de Desarrollo y Plataformas de la compañía en nuestro país. Si quiere más información, puede visitar la página www.microsoft.es/lanzamiento. Disponibilidad de Visual Studio 2008 en castellano La versión en castellano de Visual Studio 2008 estará disponible a partir del 1 de febrero para los suscriptores MSDN en España. Más información y descargas en http://www.microsoft.com/spanish/msdn/latam/visualstudio2008. << dnm.directo.noticias Sun Microsystems adquiere MySQL El acuerdo acelera la estrategia de crecimiento de Sun, que toma una relevante posición en un mercado mundial de bases de datos que asciende a más de 15.000 millones de dólares. Sun Microsystems anuncia que ha llegado a un acuerdo definitivo para adquirir MySQL AB, uno de los máximos iconos de la tecnología Open Source y desarrollador de una de las bases de datos sobre código abierto de mayor crecimiento del mundo. La adquisición se llevará a cabo por aproximadamente 1.000 millones de dólares. Una vez se complete la transacción propuesta, MySQL se integrará en las organizaciones de Software, Ventas y Servicios de Sun, y Marten Mickos, hasta ahora CEO de MySQL, pasará a formar parte del equipo directivo de Sun. Mientras tanto, un equipo conjunto formado por representantes de ambas compañías desarrollará los planes de integración basados en las sinergias técnicas, de producto, así como culturales y las mejores prácticas de negocio y desarrollo de ambas compañías. MySQL tiene su sede central en Cupertino, California, y Uppsala, Suecia, y cuenta con 400 empleados en 25 países. La base de datos open source MySQL es la “M” en el acrónimo LAMP –la plataforma de software compuesta por la unión de las tecnologías de sistema operativo Linux, el servidor web Apache, el gestor de base de datos MySQL y los lenguajes de programación PHP/Perl–. Sun está comprometida en mejorar y optimizar las tecnologías LAMP sobre GNU/Linux y Microsoft Windows junto con OpenSolaris y Mac OS/X. Se espera que la operación quede totalmente cerrada a finales del tercer trimestre o principios del cuarto del año fiscal 2008. El código fuente de .NET Framework disponible Microsoft ha liberado el código fuente de las librerías de .NET Framework, permitiendo su depuración con Visual Studio 2008. Está disponible el código de las siguientes librerías: - .NET Base Class Libraries (System, System.CodeDom, System.Collections, System.ComponentModel, System.Diagnostics, System.Drawing, System.Globalization, System.IO, System.Net, System.Reflection, System.Runtime, System.Security, System.Text, System.Threading, etc). - ASP.NET ( System.Web, System.Web. Extensions). - Windows Forms ( System.Windows. Forms). - Windows Presentation Foundation (System.Windows). - ADO.NET y XML (System.Data y System.Xml). En breve, se incluirán nuevas librerías, incluyendo LINQ, WCF y Workflow. Más información en: http://weblogs.asp.net/scottgu. Microsoft presenta Microsoft Dynamics CRM 4.0 La nueva versión de Microsoft Dynamics CRM incorpora más de 200 novedades con una experiencia de usuario muy familiar, ya que está integrada con las herramientas de Microsoft Office. • Una avanzada arquitectura multiempresa (multi-tenant) que soporta varios clientes por servidor. • Nuevas capacidades globales que ofrecen al usuario la posibilidad de elegir entre más de 25 idiomas y múltiples divisas. • Nuevas capacidades de Business Intelligence, con nuevas vistas y asistentes de informes para el usuario final. • Automatización de procesos avanzada gracias a la integración con Microsoft Windows Workflow Foundation. • Nuevas posibilidades de colaboración con Microsoft Office Communications Server 2007, con indicadores de presencia en tiempo real dentro de la propia aplicación CRM. Disponibilidad de la solución Microsoft Dyna mics CRM 4.0 ya está disponible en inglés, alemán, fran cés, holandés, español, danés y finlandés. El resto de idiomas se irán lanzando a lo largo de las próximas semanas. Los clientes actuales con un plan de mantenimiento activo (Software Assurance) pueden disponer de la nueva versión sin cargo adicional. La solución hospedada por Microsoft, “Microsoft Dynamics CRM Live” se ofrece solo en Estados Unidos y Canadá. <<dotNetManía Microsoft ha presentado su nuevo software de gestión de relaciones con clientes, Microsoft Dynamics CRM 4.0, anteriormente conocido como Titan. La nueva versión se ofrece bajo dos nombres de producto: Microsoft Dynamics CRM 4.0 para las implantaciones en local y hospedadas por partners y Microsoft Dynamics CRM Live para la solución hospedada por Microsoft. Al haber sido diseñada con un mismo código base tanto para las implantaciones en local como hospedadas, los clientes podrán elegir el modelo que mejor se adapte a sus necesidades, con la flexibilidad de cambiar de opción en función de la evolución de dichas necesidades. Microsoft Dynamics CRM 4.0 ofrece más de 200 novedades, entre las que se incluyen: 7 eventos <<dotNetManía eventos ENVISION 8 Influencing digital communication El pasado mes de enero se celebró en Milán, Italia, un evento sin precedentes, dedicado a la forma en que diseñamos y comunicamos mediante los nuevos medios digitales. Todo ello sin concretar tecnologías y de una forma general, sin entrar en técnicas ni herramientas, simplemente centrándose en conceptos, y en las nuevas tendencias de diseño y líneas de investigación en todos los ámbitos, desde la creación de logotipos hasta las interfaces de usuario. Este evento fue Envision, http://www. envision-event.com, un curioso (y, a todas luces, exitoso) experimento cuyo planteamiento era una interactividad total entre ponentes y asistentes, con un número bastante reducido de estos últimos. Se estructuró alrededor de la realización de ponencias seguidas de una tanda de preguntas al ponente, estableciendo un diálogo directo con los asistentes. La última sesión correspondió a una distribución de los ponentes entre las mesas en que estaban ubicados los asistentes, lo que provocó unas conversaciones muy interesantes. En Envision pudimos contemplar, desde el punto de vista de varias figuras internacionales del diseño, la evolución de las tecnologías y cómo los cambios sociales afectan a las comunicaciones digitales y a las formas de transmitir mensajes. Tuvimos la suerte de contar con la presencia de figuras como Matt Bagwell, Giles Colborne, August De Los Reyes, Catriona Campbell, Phil Gerrard, Wolfgang Henseler, Frederick Fuchs, Pete Barr-Watson y James Deeley, todos ellos expertos reconocidos internacionalmente en sus áreas. Para más información sobre los ponentes, visite http://www.envision-event.com/agenda/bios.aspx. Durante el evento se pudieron vislumbrar tendencias como el diseño simplista, estilo “KISS –Keep It Simple, Stupid–”; el análisis del impacto emocional de determinados diseños, psicoanalizándolos literalmente; el análisis de lo verdaderamente relevante; la evolución de las interfaces y la nueva moda “multitouch”, adaptando las interfaces a nuevas formas de interactuar mucho más naturales; la definición del diseño como un nuevo lenguaje, y con él, cómo reacciona el ser humano al mismo, evaluando la respuesta emocional de las personas; y lo que se define como emotional designer, que busca un diseño motivado casi exclusivamente por su impacto emocional en las personas. También se expusieron los procesos de diseño que buscan que éste provoque un impacto en el público, causando fuertes experiencias con el resultado de una retención de información mucho más efectiva. Y tuvimos el placer de ver otras líneas de diseño que han aparecido debido a las nuevas tecnologías, como el diseño minimalista, derivado de las nuevas necesidades de interfaces “mínimas” para dispositivos móviles, o cómo afecta el fenómeno Web 2.0 al diseño. Por último, se expuso el roadmap de la nueva línea de productos Microsoft para diseñadores y cómo el gigante informático pretende potenciar todas estas líneas de negocio y facilitar su integración con el mundo, a veces opuesto, del desarrollo. Sin duda, todo apunta a una nueva era, en la que el diseño y todas las aplicaciones del mismo van a ocupar un lugar primordial, haciendo mucho más agradable nuestra forma de percibir e interactuar con la tecnología. Un nuevo diseño que nos sea emocionalmente mucho más agradable y extremadamente natural y fácil de usar. Sin duda, toda una visión de futuro... ¿o debería decir “de presente”? José Luis Latorre entrevista Marino Posadas entrevista a Marino Posadas es director de tecnologías de desarrollo para España y Portugal de Solid Quality Mentors. Puedes leer su blog en http://www. elavefenix.net. Don Syme En el pasado Tech-Ed se hablaba mucho de un nuevo lenguaje de la familia #. Ya son 4 ó 5 (F#, S#, P#, etc.), pero éste en concreto dispone de características para el cálculo matemático que cualquier científico valorará. Su artífice principal es Don Syme, investigador en Microsoft Research Cambridge (Reino Unido), cabeza del equipo de investigación de aspectos avanzados del CLR en su versión 2.0, amable persona y excelente demostrador de las cualidades de “su lenguaje”. Aunque lo hemos comentado en privado, ya es proverbial que el entrevistado se introduzca a sí mismo… Claro, mi nombre es Don Syme, trabajo en Cambridge, y podría decirse que tengo dos áreas de interés: una relacionada con la División de Desarrollo, y otra como investigador. Mi trabajo en los últimos 10 años ha sido el de aportar novedades al paisaje de ofertas que constituye .NET, y que luego son implementadas en lenguajes como C#, Visual Basic, y ahora también en F#. Respecto a mi pasado, bueno, soy Doctor en Informática, y quizás la par- te más conocida de mi obra es algo que muchos desarrolladores utilizan todos los días: los tipos genéricos, que se introdujeron en la versión 2.0 de .NET. De hecho, ese proyecto comenzó hace muchos años, en 1999. Además, ha sido una de las características de la versión 2.0 que, por un lado, era la más solicitada, y por otro, la más ampliamente aceptada y utilizada, cuando ha estado disponible. Los genéricos fueron un trabajo ligado, en sus inicios, a los lenguajes exclusivamente, y luego hicimos un gran esfuerzo para incorporarlos al CLR de forma transparente, en la forma que pueden utilizarse desde la versión 2.0. Sin embargo, respecto a los nuevos cambios en los lenguajes .NET 3.5, hay una variedad de opiniones. La mayoría piensa que son muy interesantes y aportan productividad, pero también hay voces detractoras de algunas novedades como los métodos extensores, que para más de uno rompen con los principios de la programación orientada a objetos. Interesantísimo el tema. Verás, yo mismo no soy un defensor de ese tipo de extensiones, digamos que no es mi favorita. Y supongo que, al final, será una cuestión de buenas prácticas en la forma en que se utilicen. Pero es indudable que podemos encontrar escenarios donde los métodos extensores son totalmente requeridos si queremos dar con una solución sencilla. Y, además, como investigadores estuvimos trabajando en la forma de hacer que estas extensiones no rompieran en ningún momento la extraordinaria arquitectura sobre la que está basada el CLR. Eso era un objetivo primordial, y algunas de las limitaciones impuestas para su uso vienen de esas consideraciones. Es lo mismo que ocurre con LINQ. Creo que son características muy poderosas, muy interesantes de cara al desarrollador, pero que es responsabilidad del usuario usarlas en el escenario correcto y en el sitio correcto. Mi respuesta es no, pero quiero saber la tuya. Y esto viene a tenor de algunos comentarios que leí en tu Web. ¿Programar en un lenguaje seguro es hacer programación segura? Es muy importante la seguridad de tipos, es muy importante que el lenguaje y la plataforma ofrezcan características programables de seguridad, y es muy importante la seguridad que ofrece la ejecución en un entorno como el CLR, con sus características de verificación, que son amplí- simas. Pero –efectivamente– por mucho que haga la plataforma, la programación segura es una decisión de los analistas, planificadores y codificadores. Siempre habrá prácticas incorrectas de programación si el código no tiene en cuenta la seguridad. Como vamos a hablar del lenguaje nuevo dentro de nada, quiero comenzar por una pregunta que –desde los desarrolladores, hasta alumnos míos de post-grado en universidades– me han hecho con respecto a los lenguajes: ¿hacía falta otro lenguaje nuevo, ahora que parece que, en esta materia, está todo inventado? Pues creo que sí, porque –una vez más– depende de la tarea que quieras hacer. Cuando veo a gente trabajar en F# porque quieren abordar cierto tipo de problemas, y los veo avanzar muy rápidamente en la solución, me convenzo de que sí. De que cada tipo de problemática tiene, idealmente, su lenguaje; aquel en el que el problema a resolver se expresa con más sencillez y ofrece un mejor tiempo de respuesta. Una de las contradicciones de la situación es ésa: por un lado, hay una convergencia clara en los lenguajes, y la mayoría de usuarios se decanta por las opciones C# o VB.NET en .NET, e incluso tenemos Java (similar sintácticamente a C#) en otras plataformas, <<dotNetManía << dnm.directo.entrevista 11 <<dotNetManía << dnm.directo.entrevista 12 y por otro, también puedes programar en lenguajes funcionales, cosa que no era posible hace 10 años en la forma en que lo es hoy en día. Y estamos notando que incluso desarrolladores de otras plataformas se están sintiendo atraídos por F#, por lo que pueden aprender a hacer con el lenguaje y porque es una experiencia gratificante observar cómo este lenguaje soluciona algunos problemas clásicos del cálculo, su elegancia, su simplicidad y su potencia. De hecho, para su diseño nos fijamos en características de muchos lenguajes, desde Prolog, pasando por lenguajes funcionales, a muchos otros de tipo más experimental. Incluso en el propio lenguaje SQL, con toda su potencia expresiva. Bueno, pues vamos a hablar de F# como lenguaje… Tenemos un equipo de trabajo en Cambridge, dedicado al desarrollo de este lenguaje, y también contamos con dos personas en Redmond. Y la forma en que yo lo veo tiene mucho que ver con la programación funcional. Es crucial en los tipos genéricos, en LINQ, en los lenguajes funcionales, claro, y también lo es aquí. Debido a las características propias que tiene, F#, junto a otros lenguajes de la plataforma, convierten a .NET en una plataforma multi-paradigmática, donde manteniendo los criterios fundamentales, contamos con una variedad enorme de posibilidades para realizar las tareas. En F#, por ejemplo, podemos abordar la representación gráfica de funciones (tanto en 2D como en 3D) de una manera simplísima y con un rendimiento espectacular. Esto permite eliminar muchas complicaciones propias de lenguajes que no han sido pensados para esto, sino para representar entidades, y conseguir un código sucinto, que te permite empezar a jugar con el lenguaje, obtener inmediatamente resultados de esas pruebas, y acabar, como si acabas un juego, con una aplicación completa que resuelve un problema concreto. Desde el punto de vista de las novedades, por decirlo así, ¿cuál dirías que es la oferta de F# como lenguaje? Supongo que es una combinación de factores muy productivos desde el punto de vista operativo (Don echa mano de su portátil y comienza a hacernos una demo del lenguaje). Como ves, la sintaxis del lenguaje es muy bella, y solo necesita plantear como sentencias los objetivos que se persiguen. Si te das cuenta, estamos obteniendo resultados de forma rapidísima, con muy poco código, que además resulta muy fácil de comprender, precisamente por la misma razón. Y la rapidez es una de las cosas que más llama la atención. La gente no puede creer que operaciones como la que estamos planteando ahora (era un cálculo matricial con matrices de 10000 elementos), puedan dar una respuesta tan inmediata. Y además, F# puede combinarse con otros lenguajes, de forma que F# ofrezca la solución que podamos necesitar desde un punto de vista de cálculo numérico mientras el resto de nuestra aplicación está hecha en C# o VB.NET o lo que nos parezca bien. Podemos disfrutar de las ventajas que ofrece cada lenguaje, sin abandonar ninguno de los pilares sólidos que nos da la plataforma, y haciendo que cada lenguaje se ocupe de aquello que hace más eficazmente. Por ejemplo, hacer lo que estamos haciendo ahora en C++ (estaba representando gráficamente los contenidos de la matriz inversa de una dada, en una superficie tridimensional) no solo nos llevaría muchísimo más tiempo de codificación, sino que no tendríamos la inmediatez de la representación gráfica, y mucho menos este tiempo de respuesta. Además, en todos los procesos de re-cálculo, cuando cambiamos las condiciones originales, como puedes ver (y lo podíamos ver; créame el lector, que no salíamos de nuestro asombro), todo el proceso es casi instantáneo… Desde luego, ya me hubiera gustado tener algo así cuando estudiaba Física en la universidad… De hecho estoy escribiendo un libro –junto a Jon Harrop– titulado precisamente “F# para científicos”, en el que tratamos cómo resolver la mayor parte de los problemas matemáticos relacionados con el cálculo: desde las características que has visto, hasta transformadas de Fourier, implementación de operadores especiales como lagrangianos, hamiltonianos, etc. Pues vamos a recomendar los sitios Web de ambos, si te parece, para los lectores que quieran introducirse en el lenguaje. El oficial de MS-Research (http://research.microsoft.com/fsharp/fsharp.aspx), el tuyo (http://blogs.msdn.com/dsyme) y el de Jon (http://fsharpnews.blogspot.com). Desde el punto de vista bibliográfico, también he visto un libro en la editorial APress… Sí, se llama “Expert F#”, y –aparte de mí– colaboraron en él Adam Granicz y Antonio Cisternino. Creo que es una buena introducción al tema. Y además, ya hay mucha gente haciendo eco de las características del lenguaje en numerosos blogs, y pensamos seguir incorporando gente al equipo a medida que el proyecto siga creciendo. Unai Zorrilla Octavio Hernández plataforma.net El Marco de trabajo de entidades de ADO.NET v3.5 (II) En nuestro anterior artículo [1] hicimos una introducción al Marco de entidades de ADO.NET 3.5, en la que se trató de dar especial importancia a la capacidad de éste para crear una capa de abstracción sobre el modelo relacional. Esta abstracción, a la postre, nos permitirá centrarnos en una programación más conceptual, enfocada en el dominio del negocio que deseamos modelar. En esta entrega y las siguientes veremos cómo modelar nuestro dominio de trabajo y cómo mapear el mismo contra el almacén relacional sobre el que estemos trabajando. Unai Zorrilla es Development Team Leader de Plain Concepts y tutor de campusMVP. MVP desde 2004, colabora activamente con Microsoft en eventos de arquitectura y desarrollo, así como en giras de productos. Autor del libro "Modelando procesos de negocio con Workflow Foundation". Octavio Hernández es Mentoring Team Leader de Plain Concepts, editor técnico de dotNetManía y tutor de campusMVP. Es MVP de C# desde 2004, MCSD y MCT. Autor del libro "C# 3.0 y LINQ". Si juntáramos en una misma sala a un programador de algún lenguaje orientado a objetos y a un DBA para modelar un determinado sistema, el modelo de clases y el modelo relacional generado por cada uno de ellos seguramente distarían muchísimo uno de otro. El programador modelaría el proceso teniendo en cuenta conceptos como la herencia de clases, el polimorfismo, la agregación de tipos y el uso de contratos como las interfaces; además, tendría en cuenta distintas métricas para valorar la calidad del modelo: profundidad de la herencia, complejidad del código, etc. y procuraría aplicar patrones de diseño para resolver problemas ya conocidos. Por otra parte, el DBA crearía su modelo relacional teniendo en cuenta factores muy distintos a los anteriores; algunos de estos factores podrían ser el estudio de las formas normales, la integridad referencial y el uso de índices apropiados. Ambos tendrían argumentos de sobra para defender los modelos creados, y sin lugar a dudas ambos tendrían razón. Esta impedancia entre ambos mundos es la que se lleva mucho tiempo intentando resolver, teniendo como objetivo que ambas partes puedan trabajar sobre sus modelos e intentando que ninguno tenga que realizar demasiadas concesiones al otro. El Marco de entidades de ADO.NET v3.5 nos permitirá crear de una forma relativamente sencilla una capa de abstracción sobre el modelo relacional, posibilitando así el desarrollo de un modelo acorde con un lenguaje orientado a objetos como C# o Visual Basic. En esta entrega y las siguientes veremos cómo, a partir de un modelo relacional, podemos crear un modelo de objetos que haga uso de la herencia, la agregación de tipos y la creación de tipos complejos o entidades. Al igual que en el anterior artículo de esta serie sobre el Marco de entidades, partiremos del modelo relacional FUTBOL2006, que a pesar de ser un modelo sencillo, nos permitirá jugar con Figura 1. Modelo lógico de FUTBOL2006 << dnm.plataforma.net La herramienta visual de diseño Como comentamos la vez anterior, el Marco de entidades se apoya en tres ficheros XML de metadatos: el modelo relacional del almacén se especifica en el fichero SSDL y el modelo conceptual en el fichero CSDL, mientras que el fichero MSL define un mapeado entre los dos modelos. Precisamente estos dos últimos son los que debemos modificar para reflejar nuestros conceptos, y esas modificaciones pueden realizarse de dos maneras: a) Editando manualmente los ficheros, para luego validar su contenido y generar los ficheros de código correspondientes utilizando EDMGen.EXE. Presentamos las posibilidades que ofrece esta herramienta de línea de comandos en nuestra entrega anterior. b) Utilizando las herramientas visuales integradas en Visual Studio 2008 (actualmente parte de Entity Framework Tools CTP). Ésta es la vía que utilizaremos a partir de aquí. Para crear un modelo de datos del Marco de entidades se deberá, sobre el nodo correspondiente al proyecto en el Explorador de soluciones, seleccionar la opción “Add…” | “New item” | “ADO.NET Entity Data Model”. Aparecerá un asistente que nos permitirá elegir la base de datos de origen (también se puede generar un modelo a partir de cero, si se desea). El modelo inicial generado en nuestro caso se muestra en la figura 2. [ Dentro de esta única tabla tendremos una columna, conocida como discriminador, que nos permitirá diferenciar según su valor a qué tipo concreto corresponde cada una de las filas de la tabla. Mediante esta estrategia, las consultas sobre tipos distintos se realizan de una forma óptima, puesto que solamente tendremos que realizar un filtro sobre la columna que actúa Figura 2. Modelo conceptual inicial de FUTBOL2006 como discriminador. La entidad Futbolista del modelo de la figura 2 es un ejemplo Mapeando la herencia en el que puede aplicarse una herencia de tipos por jerarquía, en la que la propiedad Los modelos relacionales no soporPosicion nos permite discriminar los tan de forma directa el concepto de distintos tipos de futbolistas que se pueherencia de tipos. El Marco de entidaden establecer dentro de nuestro modedes, por su parte, prevé tres mecanislo de dominio. En el modelo relacional, mos diferentes para modelar la herenla columna Posicion es de tipo nchar(1) cia, que son: y toma valores como ‘P’ para indicar que • Tabla por jerarquía (Table per Hieel jugador es un portero, ‘D’ para indirarchy, TPH). car que se trata un defensa o ‘M’ para • Tabla por tipo concreto (Table per indicar que la posición del futbolista es Type, TPT). la de mediocentro. Este conocimiento • Tabla por subclase (Table per Subsobre el discriminador nos permite creclass, TPS). En esta entrega y las siguientes veremos cómo modelar las relaciones de herencia utilizando cada uno de estos tres mecanismos. Tabla por jerarquía Esta técnica consiste en mapear toda la jerarquía de tipos dentro de una misma tabla de nuestro modelo relacional. NOTA Cuando se crea un modelo de entidades mediante el asistente, Visual Studio genera un fichero .EDMX que “condensa” en un único esquema los contenidos de los ficheros .SSDL, .CSDL y .MSL. Al generar el proyecto, el contenido de este fichero se “desdobla” en las tres partes correspondientes en el directorio de salida. ] Figura 3. Agregando entidades al modelo conceptual ar una jerarquía de clases en nuestro modelo conceptual. El primer paso a realizar será el de eliminar de la entidad Futbolista la propiedad Posicion, para que posteriormente pueda ser utilizada en las condi- <<dotNetManía muchos de los conceptos antes mencionados. En la figura 1 puede verse parte de este modelo relacional. 15 << dnm.plataforma.net ciones de mapeado de nuestras entidades lógicas al modelo relacional subyacente. De acuerdo con nuestro discriminador, crearemos dentro del modelo conceptual las entidades Portero, Defensa, Mediocentro y Delantero; para ello es suficiente con usar el menú contextual del modelo conceptual, como se muestra en la figura 3. La ventana de creación de entidades nos permitirá indicar si la nueva entidad creada es una herencia de algún tipo de entidad ya existente en el modelo; en nuestro caso, seleccionamos Futbolista como tipo base para esta nueva entidad (figura 4). Figura 6. Modelo de herencia con tabla por jerarquía. Figura 4. Creando la entidad Portero con un tipo base <<dotNetManía Una vez creada la nueva entidad, deberemos establecer qué valor de discriminador permite establecer que un Futbolista es un Portero. La ventana de detalles de mapeado nos permitirá indicar la tabla del modelo relacional a la 16 Figura 7. Diagrama de clases resultante que se asocia esta entidad (por supuesto, la tabla Futbolista), y la condición por la cual se establecerá que una fila Figura 5. Mapeando la entidad Portero. de esta tabla corresponde a una entidad de tipo Portero (figura 5). Si realizamos el mismo proceso para las entidades Defensa, Mediocentro y Delantero, obtendremos un modelo conceptual similar al que se puede observar en la figura 6. Por supuesto, todo este trabajo realizado sobre el diseñador tiene su contrapartida en los ficheros CSDL y MSL. Por un lado, dentro del modelo conceptual tendremos definidas nuevas entidades, como se puede ver en el fragmento XML del listado 1. El fichero de mapeado, por su parte, contendrá nuevos elementos de tipo Condition, << dnm.plataforma.net namespace DNM { using FUTBOL2006Model; class Program { static void Main(string[] args) { FUTBOL2006Entities ctx = new FUTBOL2006Entities( metadata=.\\Futbol2006.csdl|.\\Futbol2006.ssdl|.\\Futbol2006.msl;” + “provider=System.Data.SqlClient;” + “provider connection string=” + “\”Data Source=.;Initial Catalog=FUTBOL2006;Integrated Security=True\”” ); Listado 1. Nuevas entidades en el fichero CSDL // a new kid in town... Portero p = new Portero(); p.Nombre = “PABLO PELAEZ”; p.Sexo = “V”; p.FechaNacimiento = new DateTime(1973, 2, 18); p.Club = ctx.Club.First(c => c.Codigo == “RMA”); p.Pais = ctx.Pais.First(x => x.Codigo == “ES”); // ¡observe que no hay propiedad Posicion! ctx.AddToFutbolista(p); ctx.SaveChanges(true); // persistir los cambios mediante los cuales se especifican los valores que debe tomar la columna discriminador para cada una de las nuevas entidades creadas con tipo base Futbolista (listado 2). Utilizando el modelo obtenido Una vez terminado el trabajo anterior, tendremos nuestro modelo conceptual y la jerarquía de clases tal como nos los muestra el diagrama de clases de la figura 7. Como la semana anterior, no quisiéramos terminar la columna sin mostrar un pequeño ejemplo de código, para que el lector vaya familiarizándose con el estilo de programación que promueve el // porteros de equipos madrileños var q = from c in ctx.Futbolista where c is Portero && c.Club.Ciudad == “MADRID” select c.Nombre; // ejecutar la consulta foreach (var s in q) Console.WriteLine(s); Console.ReadLine(); } } } Listado 3. Programa de ejemplo. <EntityTypeMapping TypeName=”IsTypeOf(FUTBOL2006Model.Portero)”> <MappingFragment StoreEntitySet=”Futbolista”> <ScalarProperty Name=”Id” ColumnName=”Id” /> <Condition ColumnName=”Posicion” Value=”P” /> </MappingFragment> </EntityTypeMapping> <EntityTypeMapping TypeName=”IsTypeOf(FUTBOL2006Model.Defensa)”> <MappingFragment StoreEntitySet=”Futbolista”> <ScalarProperty Name=”Id” ColumnName=”Id” /> <Condition ColumnName=”Posicion” Value=”D” /> </MappingFragment> </EntityTypeMapping> <EntityTypeMapping TypeName=”IsTypeOf(FUTBOL2006Model.Mediocentro)”> <MappingFragment StoreEntitySet=”Futbolista”> <ScalarProperty Name=”Id” ColumnName=”Id” /> <Condition ColumnName=”Posicion” Value=”M” /> </MappingFragment> </EntityTypeMapping> <EntityTypeMapping TypeName=”IsTypeOf(FUTBOL2006Model.Delantero)”> <MappingFragment StoreEntitySet=”Futbolista”> <ScalarProperty Name=”Id” ColumnName=”Id” /> <Condition ColumnName=”Posicion” Value=”L” /> </MappingFragment> </EntityTypeMapping> Listado 2. Condiciones en el fichero C-S Marco de entidades. El listado 3 muestra un programa en el que se crea un Portero y se persiste en la base de datos; a continuación, se ejecuta una consulta para comprobar que la inserción ha sido correcta. El próximo mes continuaremos presentando los mecanismos que ofrece el Marco de entidades para modelar la herencia. Referencias [ 1 ] Hernández, Octavio y Zorrilla, Unai “El Marco de trabajo de entidades de ADO.NET 3.5”, en dotNetManía nº 44, enero de 2007. [ 2 ] Blog del equipo de ADO.NET Entity Framework, en http://blogs. msdn.com/adonet. <<dotNetManía <EntityType Name=”Portero” BaseType= ”FUTBOL2006Model.Futbolista” /> <EntityType Name=”Defensa” BaseType= ”FUTBOL2006Model.Futbolista” /> <EntityType Name=”Mediocentro” BaseType= ”FUTBOL2006Model.Futbolista” /> <EntityType Name=”Delantero” BaseType= ”FUTBOL2006Model.Futbolista” /> 17 plataforma.net Rubén Garrigós VS 2005 Team Edition para profesionales de bases de datos (y II) Visual Studio 2005 Team Edition for Database Professionals es la herramienta para profesionales de base de datos de la plataforma Visual Studio. Desplegar nuevos entornos de datos, refactorizar objetos, realizar pruebas unitarias de la base de datos... todo se vuelve sencillo con DataDude. Introducción Rubén Garrigós es un arquitecto de soluciones Microsoft certificado como MCT, MSCD .NET y MCITP SQL Server 2005. Es experto en eCommerce B2C y B2B y en plataformas de datos sobre SQL Server en Solid Quality Mentors. Junto a Eladio Rincón y Carlos Sacristán escribe en “El rincón del DBA”: http://blogs.solidq.com/ES/ ElRinconDelDBA En nuestro anterior artículo vimos cómo Visual Studio 2005 Team Edition for Database Professionals (DataDude para los amigos) nos proporciona interesantes funcionalidades para hacer nuestro trabajo diario más cómodo. En esta segunda parte vamos a continuar abordando otros requerimientos habituales en nuestro día a día: • “Necesito desplegar la base de datos en un nuevo entorno, a partir de los últimos cambios y con un conjunto de datos listo para utilizar”. • “Hay que renombrar unos objetos de bases de datos y encargarse de todas sus dependencias. No está bien que tengamos columnas telfono, farturas, lineafracturas u otras perlas similares”. • “Necesitamos garantizar que los nuevos cambios realizados en el modelo no produzcan errores en la capa de datos”. • “El sistema en producción contendrá 1 millón de clientes y 10 millones de facturas. Debes comprobar que el rendimiento no se deteriora en exceso”. Despliegue de cambios El despliegue de cambios en los modelos de datos es un proceso que se realiza frecuentemente de forma bastante artesanal. Probablemente tengamos definido nuestro propio procedimiento de despliegue, y siguiéndolo paso a paso nos asegu- ramos que se cumplen ciertas condiciones como: disponer de una copia de seguridad actual de la base de datos, no tener usuarios conectados, etc. Acto seguido suele venir un conjunto de scripts ordenados o un largo script que deberemos lanzar para realizar las modificaciones pertinentes. Finalmente, se suelen realizar algunas comprobaciones básicas y se da el despliegue por finalizado. Este proceso tiene varios inconvenientes y puede acabar siendo demasiado lento para las necesidades actuales, bastante propenso a fallos y con una vuelta atrás complicada. DataDude nos ofrece la generación automática de un script de despliegue unificado que tendrá en cuenta todos los cambios aplicados a los objetos, así como varios scripts de pre-despliegue y post-despliegue que pueden ser personalizados. Por citar algunos, los scripts de predespliegue son responsables de generar o modificar los servidores enlazados y los logins creados, y los de post-despliegue de los cambios en roles y permisos. Adicionalmente podemos añadir nuevos scripts, por ejemplo para actualizar un registro en una tabla de estado. De esta forma indicaríamos a las aplicaciones que estamos realizando una operación de mantenimiento en la base de datos. Utilizando la funcionalidad de despliegue de DataDude, cuando lo realicemos contra un servidor en el que ya teníamos desplegado nuestro modelo de base de datos, se nos advertirá de cualquier situación que pueda producir pérdidas de datos. Para mayor seguridad, tenemos la opción de que se realice una copia de seguridad automáticamente como paso previo al despliegue, evi- << dnm.plataforma.net tegia similar, pero siempre se requería de una gran intervención manual por nuestra parte. Con la nueva herramienta de refactorización de DataDude, al renombrar un objeto se replicará el cambio en todos los objetos afectados. Por ejemplo, para cambiar el nombre de una columna tan solo tendremos que seleccionar la opción “renombrar” en el objeto del esquema deseado, previsualizar los cambios a aplicar y aplicarlos (figuras 2 y 3). Figura 2. Renombrar objetos. Figura 1. Scripts de despliegue tando así la posibilidad de un olvido en este punto. Algunas veces no será viable realizar la copia de seguridad de esta forma, y entonces deberá ser realizada manualmente en los scripts de pre-despliegue para adaptarse a las peculiaridades del sistema destino. Además, si nuestro modelo de recuperación es completo, el proceso de despliegue nos generará un punto de restauración, por lo que ante cualquier problema nos revertirá al estado anterior al despliegue. Finalmente, indicar que este proceso de compilación y despliegue del modelo de datos puede ser automatizado mediante el uso de scripts y MSBuild de forma que éste se realice contra varios servidores, cada día o de forma sincronizada con las compilaciones y despliegues de nuestras aplicaciones. Una nueva funcionalidad que aparece con DataDude es la posibilidad de realizar cambios a los objetos de nuestro modelo (tablas, procedimientos, funciones, vistas…) de forma que las referencias a éstos se actualicen y se transmitan en cascada por el resto del modelo. Hasta ahora, la alternativa habitual del profesional de base de datos pasaba por el uso del socorrido procedimiento almacenado sp_depends o alguna estra- Comparado con el tedioso proceso de tener que realizar esta labor manualmente, deshaciendo los cambios en cada uno de los objetos, la ventaja es evidente. Como medida de seguridad adicional, se registran adicionalmente en ficheros de log los cambios realizados por la refactorización. Previa aplicación del script de despliegue (que será el que reflejará los cambios de nuestro proyecto en la base de datos), debemos tener en cuenta algunas considera- <<dotNetManía Refactorización Figura 3. Actualización automática de los objetos afectados por el cambio 19 << dnm.plataforma.net ciones adicionales. La primera de ellas es que los cambios en tablas por defecto se aplican previo borrado del objeto afectado. Otro problema que nos podemos encontrar es que no se realice el renombrado de forma correcta en scripts personalizados pre-despliegue, post-despliegue, tests unitarios, etc. El motivo en este caso suele ser que los objetos referenciados no utilizan el nombre cualificado de éstos. En estos casos es posible que algunas referencias queden sin actualizar, y por ello una buena práctica consiste en el uso siempre del nombre totalmente cualificado (por ejemplo AdventureWorksLT.SalesLT.ProductDescription en vez de ProductDescription). <<dotNetManía Tests Unitarios 20 Los test unitarios nos proveen de una forma de testear componentes individuales en un sistema. Su naturaleza está destinada a comprobar el funcionamiento correcto o incorrecto de cada componente de forma individual. El valor de contar con una batería de tests que nos permita asegurar el funcionamiento a nivel unitario correcto crecerá con el tiempo, con el aumento de la complejidad del modelo y con la cantidad de cambios que se introduzcan durante las iteraciones del ciclo de desarrollo. Los tests unitarios suelen ir destinados a comprobar el buen funcionamiento de un componente con una funcionalidad muy concreta, como podría ser en nuestro caso un procedimiento almacenado. Uno de los principales problemas con los que nos encontramos a la hora de implementar los tests unitarios de forma manual es el coste de su realización y mantenimiento. La creación de los esqueletos que realicen la llamada a los procedimientos, comprueben los valores devueltos y validen ciertas condiciones requiere algunas veces más esfuerzo que la codificación del propio procedimiento. En este apartado, DataDude nos facilita la generación de forma automática de esqueletos para procedimientos, funciones y triggers y nos soporta directamente la evaluación de las condiciones que se muestran en la tabla 1. Condición Descripción Empty Resultset Comprueba que se devuelve un resultset vacío. Se debe precisar el resultset a evaluar si se devuelven varios como resultado. Execution Time Comprueba que se ejecute la prueba en menos de un umbral de tiempo seleccionado. Esto puede ser útil para detectar problemas de rendimiento tras un cambio o como complemento para cumplir un SLA. Inconclusive Indica que el test aún no está completo, que tenemos pendiente definir cuál es el comportamiento esperado. Not Empty Resultset Comprueba que se devuelve un resultset no vacío. Se debe precisar el resultset a evaluar si se devuelven varios como resultado. Row Count Comprueba que se devuelve un número exacto de registros en el resultset indicado. Scalar Value Comprueba que el valor devuelto en un resultset, en la fila y columna indicada, coincide con un valor dado. También puede configurarse para aceptar o no nulos. Tabla 1. Condiciones soportadas Cuando creemos un nuevo test a nuestro proyecto, se añadirá un nuevo proyecto de tests asociado a la solución y deberemos indicar la conexión principal a la base de datos y, de forma opcional, la privilegiada. Es muy interesante el que se disponga de esta diferenciación, pues nos permitirá comprobar que la ejecución de la prueba se realizará en un contexto de seguridad adecuado y no en el de un administrador de base de datos. La conexión privilegiada se utilizará en los scripts de pre y post-ejecución y nos permitirá, por ejemplo, acceder directamente a aquellas tablas a las que el usuario que ejecuta el procedimiento no debería tener acceso directamente para comprobar que el funcionamiento del procedimiento ha sido el esperado. Un ejemplo de esqueleto autogenerado de prueba para un procedimiento sería el siguiente: — database unit test for dbo.uspLogError DECLARE @RC INT, @ErrorLogID INT SELECT @RC = 0 EXEC @RC = [dbo].[uspLogError] @ErrorLogID OUTPUT SELECT RC=@RC, ErrorLogID=@ErrorLogID En este caso, podría ser suficiente comprobar que el valor devuelto es 0, lo cual indicaría una ejecución correcta. En otros casos deberemos realizar comprobaciones adicionales que podemos codificar en T-SQL y lanzar un error en caso de no superarlas de esta forma: if (@RC=2 and @ErrorLogID>25) RAISERROR( ‘El identificador del error está fuera de rango’, 12, 1 ) Una característica de los tests unitarios bien diseñados es que garantizan su repetibilidad, siendo los resultados idénticos en distintas ejecuciones dado un mismo entorno de prueba. Para conseguirlo, es necesario garantizar que se cumplen ciertas pre-condiciones antes de lanzar un test. Un ejemplo claro para el test de un procedimiento almacenado de inserción de cliente sería comprobar previamente que el cliente que va a insertarse no existe ya. Este test unitario del procedimiento contendrá a su vez los tests o acciones necesarios para garantizar su funcionamiento acorde a lo especificado. En << dnm.plataforma.net Planes de generación de datos de prueba Muy relacionada con la repetibilidad está la preparación de datos de prueba. En muchos casos las pruebas requieren de un contexto más o menos complejo para poder ser lanzadas. Por ejemplo, en el ejemplo comentado anteriormente de creación de un cliente podemos requerir de una tabla de países, otra de códigos postales y otra de tipos de clientes. No resultaría nada conveniente tener que preparar el entorno de datos completo y deshacerlo al finalizar cada prueba unitaria. Un plan de generación de datos pertenece a un proyecto de base de datos, aunque un mismo proyecto puede contener diferentes planes de generación de datos. Un ejemplo típico consiste en tener un plan de generación orientado a las pruebas, otro a las pruebas de rendimiento y otro para despliegues de nuevos entornos. Cuando añadimos un plan de generación nuevo, se nos muestran las tablas que componen nuestro modelo para que indiquemos cuáles queremos que participen en este plan de generación de datos. Para cada tabla podremos configurar el número de filas a generar y si queremos mantener proporcionalidad con alguna tabla relacionada. Si estamos generando datos para una tabla que nos relaciona un cliente con sus direcciones de correo, podemos determinar un ratio de 3:1 para que por cada cliente se generen 3 direcciones asociadas. Esto es especialmente relevante cuando generamos planes de generación de datos para pruebas de carga, pues el rendimiento puede variar extraordinariamente si un sistema debe soportar 100 facturas por cliente o 100.000 facturas por cliente. Una vez hemos determinado la cantidad de registros a generar, debemos indicar cómo vamos a generar dichos datos columna a columna. Las columnas que tengan definido un identificador autoincremental asociado a la clave primaria o sean columnas calculadas aparecerán deshabilitadas, pues se generarán los datos acorde a su definición. Para el resto disponemos de generadores de dos tipos principalmente: gene- radores basados en el tipo del dato y generadores basados en la extracción de datos de una fuente de datos. Entre los primeros nos encontramos generadores de enteros, de números en coma flotante, de booleanos o de cadenas, y todos ellos parametrizables. Algunos de los parámetros son: valor mínimo y máximo, longitud del dato, tipo de distribución para la generación aleatoria, semilla a utilizar en el generador, porcentaje de nulos a generar... De estos parámetros hay que destacar la importancia de la semilla utilizada. Será ésta la que nos permitirá asegurar la repetibilidad del dato generado entre distintas ejecuciones del plan de generación de datos. Entre los segundos contamos con el Data Bound Generator, que nos permite obtener los datos a partir de una consulta. Respecto a los datos devueltos podremos especificar un porcentaje de nulos a generar, así como una semilla. En este caso la semilla no se utilizará para inicializar un generador de datos aleatorio, sino para seleccionar, de entre los registros devueltos por la consulta, cuáles utilizar. Obviamente, para garantizar la repetibilidad, la consulta que utilicemos deberá devolver siempre los mismos datos. Para aquellos casos en los que estos generadores no sean suficientes y necesitemos de generadores específicos, Figura 4. Plan de generación de datos <<dotNetManía este caso necesitaríamos, al menos, otro test que garantizara que cuando el cliente ya existe se devuelva el error esperado. Por otra parte y para garantizar la repetibilidad, deberíamos borrar dicho cliente al finalizar el test para dejar el entorno listo para la próxima ejecución. Para llevar a cabo estas tareas comentadas disponemos de varios scripts asociados a los scripts de prueba. Disponemos de los scripts “pre-test”, “post-test”, “Test initialize” y “Test cleanup”. Los dos primeros se ejecutan antes y después de cada test específico y son específicos para cada test, mientras que los segundos se llaman antes y después de cada uno de los tests de la clase de pruebas. Indicar que finalmente estos tests se implementan con clases del nuevo espacio de nombres Microsoft.VisualStudio.TeamSystem.Data.UnitTesting, con lo que es posible modificar el comportamiento de las clases generadas para adaptarlo a nuestras necesidades más allá de la ejecución de scripts T-SQL. Por ejemplo, podríamos modificar en el servidor de pruebas un fichero XML que se utilice en nuestros procedimientos con OPENROWSET. Esto nos permitiría fijar y comprobar precondiciones que no sean directamente parte del motor relacional. 21 << dnm.plataforma.net contamos con la posibilidad de crear nuestros propios generadores (MyDataGenerator), al igual que hacíamos con los comprobadores de los tests unitarios. Estos generadores es posible obtenerlos por composición de generadores ya existentes, por ejemplo combinando el generador de expresiones regulares con el Data Bound Generator, o bien crearlos desde cero de forma que se ajusten a nuestras necesidades específicas. “DataDude, parece que hay algún problema en la sincronización de datos con una de nuestras sucursales. Necesitaría comparar los datos de facturas y clientes para confirmar que está todo correcto“. “DataDude, hay que renombrar unos objetos de bases de datos y encargarse de todas sus dependencias. No está bien que tengamos clientes con “telfono”, “farturas”, “lineafracturas” u otras perlas similares”. Mediante la herramienta de comparación de datos comprobaremos si es cierta o no la sospecha. Utilizaremos como origen la base de datos de la sucursal y como destino la base de datos central. Utilizando refactorización realizaremos los cambios uno a uno y supervisaremos los objetos a los que afectan. Aplicaremos los cambios y compilaremos el modelo de nuevo para comprobar que no existen errores. A continuación, desplegaremos en nuestro entorno de pruebas y pasaremos las pruebas unitarias para contrastar que en tiempo de ejecución todo sigue funcionando como esperamos. Figura 5. Generadores de datos Otra vida es posible <<dotNetManía Como colofón a estos artículos, vamos a indicar qué funcionalidades podemos utilizar de DataDude para ayudarnos en la resolución de los requerimientos planteados al inicio de los artículos. 22 Indicaremos ignorar los datos que existan solo en destino para centrarnos en aquellos datos que existen en origen pero no en destino. Seleccionaremos las tablas que contengan los datos y obtendremos las diferencias y el script de inserción si lo necesitamos para sincronizar los datos. “DataDude, necesito conocer las diferencias del modelo de datos de desarrollo respecto al modelo de producción. También un script para sincronizar nuestro entorno de desarrollo”. “DataDude, necesito desplegar la base de datos en un nuevo entorno, a partir de los últimos cambios y con un conjunto de datos listo para utilizar”. Utilizando la herramienta de comparación de esquemas podremos obtener de forma rápida las diferencias existentes y el script en caso de ser necesario. Podemos realizar la comparación bien seleccionando directamente la base de datos de producción como origen, o bien un proyecto de Visual Studio que tengamos sincronizado. Como destino seleccionaremos la base de datos de desarrollo del equipo o un proyecto de base de datos sincronizado con ésta. Si existen diferencias, tan solo tendremos que generar el script, revisarlo y entregarlo. El primer paso será obtener la última versión del modelo del SourceSafe o de la herramienta de control de cambios utilizada. Una vez hecho esto, compilaremos el modelo para asegurarnos de que no existen errores en el proyecto. Modificaremos el destino del despliegue, revisaremos el script de despliegue generado y lo ejecutaremos. En estos momentos sería buena idea también pasar la batería de tests unitarios si disponemos de ellos. Para ello ejecutaremos previamente el plan de generación de datos para dejar la base de datos desplegada y probada, lista para su uso. “DataDude, necesitamos garantizar que los nuevos cambios realizados en el modelo no produzcan errores en la capa de datos”. Tras aplicar los cambios en el modelo, lanzaremos nuestras pruebas unitarias previamente diseñadas. Rápidamente comprobaremos si alguna de éstas ha fallado y en dicho caso analizaremos el origen del error. Daremos el ok cuando todas las pruebas unitarias se superen sin errores. “DataDude, el sistema en producción contendrá 1 millón de clientes y 10 millones de facturas. Debemos comprobar que el rendimiento no se deteriora en exceso”. Crearemos un plan de generación de datos especial para la realización de la prueba de carga. Dicho plan generará las tablas con tantos registros como los esperados en producción. A continuación, bien manualmente o bien mediante un test de carga de VS2005, lanzaremos las diferentes pruebas unitarias para comprobar el grado de desempeño y determinaremos si es necesario tomar medidas como la creación de nuevos índices, particionado, reescritura de código T-SQL, etc. plataforma.net Mario del Valle, Iskander Sierra, Miguel Katrib El Calendario que le estaba faltando a WPF Con este artículo continúa la saga relacionada con elementos avanzados de WPF [1-4]. En el último de estos trabajos [4], vimos cómo definir un control personalizado, al presentar un “scroll circular”. Sin embargo, en aquel caso realmente bastó con asociarle las plantillas y estilos adecuados al control básico ScrollBar ya existente en WPF. Miguel Katrib es doctor y profesor jefe de programación del departamento de Ciencia de la Computación de la Universidad de La Habana. Miguel es líder del grupo WEBOO, dedicado a la orientación a objetos y la programación en la Web. Es entusiasta de .NET y redactor de dotNetManía. Mario del Valle e Iskander Sierra son instructores de programación en C# de la cátedra de Programación e Ingeniería de Software del departamento de Ciencia de la Computación de la Universidad de La Habana. Son desarrolladores del grupo WEBOO dedicados a la tecnología .NET. En este trabajo vamos a ver cómo definir un control personalizado pero desde cero, es decir, cuando no basta con retocar uno ya existente. El control a definir es para que haga las veces de un calendario, y puede verse como un control de edición de valores de tipo DateTime, como el que se usa en muchas aplicaciones de Windows pero que lamentablemente no está presente entre los controles básicos que ofrece WPF. La figura 1 nos muestra el gadget Calendario de Windows Vista, al cual pretendemos acercarnos mostrando cómo se pudiera implementar en WPF. Note que un calendario no se limita a presentar los valores enteros que conforman un DateTime, sino que visualiza todos los días del mes de dicha fecha y los distribuye por días de la semana, lo que nos propor- ciona mayor utilidad y funcionalidad en el trabajo con fechas. En la jerarquía de tipos básicos de WPF no hay un tipo Calendar ni un tipo DateTimePicker, como sí los hay en Windows Forms, con los que poder hacer edición o selección de fechas. ¿Dónde encajar un tal Calendar dentro de la jerarquía de WPF? Tómese un tiempo antes de seguir y piense en una respuesta. Un tal calendario podría considerarse como control de rango (un heredero de RangeBase) ya que, al igual que los valores numéricos, los valores DateTime son totalmente ordenables en un rango entre un valor mínimo y un valor máximo. Sin embargo, lamentablemente RangeBase no es un tipo genérico (por ejemplo definido con parámetro restringido a IComparable) y solo está definido en base a tipos numéricos, lo que impediría usarlo con una fecha (DateTime) como valor componente. Heredero de Control Figura 1. Gadget Calendario de Windows Vista Por otra parte, un calendario no puede verse solamente como una figura para visualizar, ni tampoco como un panel o un decorador. Debe definirse como un control ya que debe aportar funcionalidad, pero a su vez se espera que pueda tener diferentes apariencias, acordes con el estilo y tema de la aplicación en que se utilice. Recuerde que los controles en WPF (tipos herederos de Control) << dnm.plataforma.net lizar en el espacio de nombres Wpf CustomControlLibrary1 (como indica el valor del atributo xmlns:local). Figura 2. Creando una biblioteca de controles WPF con VS 2008 Para definir un control personalizado en VS 2008 hay que crear un proyecto con la opción “WPF Custom Control Library” (figura 2). Esto crea por defecto un fichero Custom Control1.cs, que renombraremos por Calendar.cs, en el cual se incluye una clase parcial heredera de Control (a la que llamaremos Calendar). Además, se crea también un fichero generic.xaml correspondiente al tema por defecto de la aplicación. Este código XAML contiene un diccionario de recursos WPF donde aparece un estilo definido por defecto para el nuevo control generado. El listado 1 nos muestra este estilo. Es conveniente detenerse aquí para destacar algunos aspectos sobresalientes de WPF: 1. El uso del atributo especial xmlns:local declarado en el elemento raíz le indica al compilador de XAML que cualquier elemento con un nombre que comience con el prefijo local: (en este caso local:Calendar) se debe loca- en TargetType ( Calendar en este caso). 3. La plantilla del control ( Control Template) asignada a la propiedad Template mediante el Setter es la que definirá la apariencia predeterminada del control. De modo que cambiando directamente el valor de la propiedad Template o utilizando otro estilo para asociar al tipo Calendar, se puede modificar la apariencia completa del calendario. Esta característica de separar la apariencia, a través de plantillas y estilos, de la funcionalidad, es una de las novedades más honorables de WPF en comparación con el resto de las API visuales que le anteceden. 4. La extensión de marcado TemplateBinding permite enlazar propiedades de los elementos contenidos en la plantilla con propiedades del propio calendario, de modo que la apariencia de lo que se visualiza esté asociada a la información del control. <ResourceDictionary xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml” xmlns:local=”clr-namespace:WpfCustomControlLibrary1”> <Style TargetType=”{x:Type local:Calendar}”> <Setter Property=”Template”> <Setter.Value> <ControlTemplate TargetType=”{x:Type local:Calendar}”> <Border Background=”{TemplateBinding Background}” BorderBrush=”{TemplateBinding BorderBrush}” BorderThickness=”{TemplateBinding BorderThickness}”> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> </ResourceDictionary> Listado 1. Estilo predeterminado generado por Visual Studio para los nuevos controles personalizados. 2. La utilización de un estilo (Style) presente en el código de generic.xaml hace que todas las propiedades indicadas en sus correspondientes Setters se apliquen por defecto a todos los objetos creados del tipo indicado Es decir, cuando usted modifique el valor de una propiedad enlazada en un calendario, como por ejemplo Background, el valor asignado automáticamente se asignará también al de su propiedad enlazada, o <<dotNetManía tienen una propiedad Template a través de la cual se le puede cambiar la apariencia al control. 25 << dnm.plataforma.net sea el Background del Border contenido en la plantilla. Estos tipos especiales de enlace hacen la magia de mantener la apariencia del control de mutuo acuerdo con las propiedades del propio control y permitir que esta apariencia pueda cambiar completamente de forma dinámica. Pero claro, esta apariencia predeterminada que nos pone Visual Studio cuando se define un control solo nos muestra un rectángulo con el borde y fondo ligados a una instancia del control, que en el caso de ser ubicado dentro de una ventana, como el caso del listado 2, los toma de la ventana, y el control se visualiza tan anodinamente como se muestra en la figura 3. sobre la que se concibe el calendario. Para ello le colocaremos una propiedad Value de tipo DateTime. ¿Algo más? Posiblemente eventos de teclado y ratón, propiedades para configurar accesibilidad, cultura, etc. Pero sobre estas últimas no hay que preocuparse, ya que son todas propiedades que se heredan de Control. Propiedades de dependencia Figura 3. Apariencia predeterminada de un control personalizado con su plantilla generada por VS. <Window x:Class=”WpfApplication1.Window1” xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation” xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml” xmlns:custom=”clr-namespace:WpfCustomControlLibrary1;assembly=WpfCustomControlLibrary1” Title=”Window1” Height=”300” Width=”300”> <Grid> <custom:Calendar Background=”LightGray” BorderBrush=”DimGray” BorderThickness=”1” VerticalAlignment=”Center” HorizontalAlignment=”Center” Width=”100” Height=”100”/> </Grid> </Window> <<dotNetManía Listado 2. Utilizando el calendario en una ventana 26 Claro que esto no aporta nada a nuestro objetivo de aproximarnos a la apariencia de la figura 1. Para ello hay que agregar algunos elementos nuevos, tanto en el código XAML de la plantilla como en el código C# de la clase que define al tipo Calendar. El quid del asunto está en saber qué ponemos en cada parte. Es importante tener siempre presente que todo lo que sea pura apariencia debiera estar en el código XAML y que en el código C#1 solo se debe poner el código que dé soporte a la funcionalidad deseada para el control (por ejemplo, obtener todos los días del mes para ubicarlos según los días de la semana). Esta separación es lo que facilita expresar que se pueda hacer un cambio de apariencia (incluso usando terceras herramientas, como Expression Blend) sin perturbar la funcionalidad del control. Funcionalidad Cuando usted vaya a diseñar cuál es la funcionalidad que desea tenga un control, es útil que se abstraiga de la imagen visual que se pueda tener, o querer, de éste. Por ejemplo, para operar desde C# con un tal control calendario es indispensable que éste tenga un valor DateTime que indique la fecha El listado 3 muestra la forma clásica de incluir una propiedad Value en la definición de un tipo cualquiera en NET. public class Calendar : Control { ... private DateTime value; public DateTime Value { get { return value; } set { this.value = value; } } } Listado 3. Definición de la propiedad Value al estilo NET Sin embargo, esta forma directa de definir propiedades, a la que estamos acostumbrados en la programación clásica con C#, no es suficiente en este caso para hacer valer toda la capacidad de WPF. Para poder aprovechar muchas de las bondades de WPF, como es “ligar” (binding) una propiedad con otras propiedades (del mismo o de otros controles) o con fuentes de datos, y poder con ello animar el valor de una propiedad, o establecer criterios de apariencia asociados con el valor de la propiedad (mediante triggers), es necesario definir las propiedades como se muestra en el listado 4 . Al definir la propiedad a través de una variable de tipo DependencyProperty (que en el código del listado 4 se registra en un diccionario que incluye los controles personalizados), estamos 1 Aunque usamos C#, la implementación de controles personalizados está disponible para todos los lenguajes .NET. << dnm.plataforma.net public class Calendar : Control{ ... public static readonly DependencyProperty ValueProperty = DependencyProperty.Register(“Value”, typeof(DateTime), typeof(Calendar), new PropertyMetadata(DateTime.Today)); public DateTime Value { get { return (DateTime) GetValue(ValueProperty); } set { SetValue(ValueProperty, value); } } } Listado 4. Definición de un DependencyProperty en WPF [ ] NOTA Sí, es cierto, escribir todo esto le puede resultar algo incómodo, pero la buena noticia es que Visual Studio tiene un snippet (propdp) que le ahorrará unos cuantos golpes de tecla. DateTime.Today). Diversas características se pueden indicar mediante este parámetro, como la forma en que se comportan los enlaces (bindings), las animaciones, o el repintado del control. También es posible interceptar mediante delegados diferentes cambios de estado de la propiedad, como el momento en que se va a asignar un valor o el momento en que este valor ya fue asignado, pero analizar todas estas posibilidades se sale del espacio disponible para este artículo. Tanto Control como la mayoría de los tipos de WPF tienen como clase base común a DependencyObject. Entre otras facilidades, esta clase brinda los métodos GetValue y SetValue, que le permiten a sus clases herederas poder acceder directamente al valor de las propiedades de dependencia en la forma siguiente: DateTime dt = (DateTime) myCalendar.GetValue( Calendar.ValueProperty); myCalendar.SetValue( Calendar.ValueProperty, DateTime.Now); Sin embargo, para facilitar el uso desde código C# y desde XAML de la propiedad de dependencia, en el listado 4 se agregó además la propiedad Value de acuerdo al estilo acostumbrado en .NET. De este modo, se puede entonces hacer: DateTime dt = myCalendar.Value; myCalendar.Value = DateTime.Now; Aunque no lo crea, esto es todo lo que hay que definir dentro del tipo Calendar desde el punto de vista de la funcionalidad de éste como control. El resto queda dentro de la apariencia que se le va a asociar a éste, y es lo que se verá a continuación. Claro, esto no quiere decir que no haya nada más que escribir en código C#; nos falta también definir un “convertidor de tipo”, pero esto se verá más adelante. La apariencia Para que la apariencia se aproxime a la del gadget de Windows Vista (figura 1), hay que sustituir entonces el código XAML que Visual Studio ha generado por defecto (listado 1 y figura 3). Hace falta una plantilla que haga lucir al control como el familiar calendario, inclu- <<dotNetManía indicándole en este caso a WPF, mediante los parámetros del método Register, que todos los objetos de tipo Calendar que se declaren en XAML tendrán una propiedad llamada Value (primer parámetro del método Register en el listado 4), a la cual se le pueden asignar valores de tipo DateTime (segundo parámetro que se le ha pasado al método Register). Para esta variable, como es DependencyProperty, están disponibles los mecanismos de binding, animations y triggers. Aunque estos tres parámetros usados en la llamada a Register son suficientes en la mayoría de los casos, hay otras sobrecargas del método. Por ejemplo, es posible utilizar un cuarto parámetro (como en el listado 4) que permite detallar el comportamiento de la propiedad en ejecución. El tipo PropertyMetadata es uno de los aceptados en este cuarto parámetro, por ejemplo para indicar un valor predeterminado a asumir (en el listado 4 se ha utilizado la fecha actual 27 << dnm.plataforma.net yendo la repartición de todos los días del mes en una tabla cuyas columnas sean los días de la semana (listado 5 y figura 4). <<dotNetManía Figura 4. Apariencia del calendario. 28 En el segmento (1) del listado 5 puede observarse que se usa un Grid como panel para distribuir los elementos del calendario. En esta rejilla se definen dos filas. Una fila es para las letras que indican los días de la semana, los cuales han sido distribuidos uniformemente por un UniformGrid; la otra fila es un ListBox para mostrar los días del mes, distribuidos por semanas, donde cada ítem del ListBox es un UniformGrid con los días de cada semana. Aunque el lector puede pensar que el Grid es el panel ideal para representar una tabla de elementos, su principal limitante para dar la apariencia del calendario es que en un Grid hay que indicar en qué fila y columna se encuentra cada elemento. Esto da mucho trabajo cuando los elementos a ubicar no se pueden indicar explícitamente en el XAML, sino que hay que calcularlos dinámicamente en ejecución, como es el caso de los días del mes, que hay que ubicarlos según el día de la semana (incluyendo los días del mes anterior que forman parte de la primera semana y los días del mes siguiente que forman parte de la última semana). Sin embargo, hay en WPF un panel que sí resulta muy útil para este caso y es el UniformGrid (ver en listado 5 los segmentos (2) y (5)). Éste es un panel que, al igual que el Grid, distribuye sus elementos en filas y columnas, pero la diferencia radica en que para el UniformGrid no es necesario indicar las posiciones de los elementos que él distribuye. El propio UniformGrid, al indicarle la cantidad de columnas, reparte los elementos uniformemente, llenando primero la primera fila, poniendo un elemento en cada columna; luego de finalizar con la última columna de la fila, entonces el siguiente elemento se ubica en la primera columna de la segunda <Style TargetType=”{x:Type this:Calendar}”> <Setter Property=”Template”> <Setter.Value> <ControlTemplate TargetType=”{x:Type local:Calendar}”> <Border Background=”{TemplateBinding Background}” BorderBrush=”{TemplateBinding BorderBrush}” BorderThickness=”{TemplateBinding BorderThickness}”> <Border> <Border.Background> <LinearGradientBrush EndPoint=”1,1”> <GradientStop Color=”#8fff” Offset=”0”/> <GradientStop Color=”#dfff” Offset=”0.4”/> <GradientStop Color=”#dfff” Offset=”0.6”/> <GradientStop Color=”Transparent” Offset=”1”/> </LinearGradientBrush> </Border.Background> (1) <Grid> <Grid.RowDefinitions> <RowDefinition Height=”Auto”/> <RowDefinition Height=”Auto”/> </Grid.RowDefinitions> (2) <UniformGrid Columns=”7” Margin=”0,2”> <TextBlock Text=”D”/> <TextBlock Text=”L”/> <TextBlock Text=”M”/> <TextBlock Text=”M”/> <TextBlock Text=”J”/> <TextBlock Text=”V”/> <TextBlock Text=”S”/> </UniformGrid> <ListBox Grid.Row=”1” BorderThickness=”0” Background=”Transparent” SelectedItem=”{Binding Value, RelativeSource={RelativeSource TemplatedParent}, Mode=TwoWay}” Foreground=”{TemplateBinding Foreground}”> (4) <ListBox.ItemsSource> <Binding Path=”Value” RelativeSource=”{RelativeSource TemplatedParent}”> <Binding.Converter> <local: DateToMonthDatesConverter/> </Binding.Converter> </Binding> </ListBox.ItemsSource> (5) <ListBox.ItemsPanel> <ItemsPanelTemplate> <UniformGrid Columns=”7”/> </ItemsPanelTemplate> </ListBox.ItemsPanel> (6) <ListBox.ItemTemplate> <DataTemplate> <TextBlock Name=”tb” Text=”{Binding Day}” FontSize=”9” HorizontalAlignment=”Right” TextAlignment=”Right”/> (7) <DataTemplate.Triggers> <DataTrigger Binding=”{Binding DayOfWeek}” Value=”Sunday”> <Setter TargetName=”tb” Property=”Foreground” Value=”OrangeRed”/> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> </ListBox.ItemTemplate> </ListBox> </Grid> </Border> </Border> </ControlTemplate> </Setter.Value> </Setter> </Style> Listado 5. Estilo para el calendario. << dnm.plataforma.net <ListBox SelectedItem= ”{TemplateBinding Value}” ...> Esto provocaría que, automáticamente, cuando cambiara la propiedad Value en el calendario la selección del ListBox también cambiara en correspondencia, pero… ¿y a la inversa? TemplateBinding es un enlace que ocurre en un solo sentido, el de cambiar la selección del ListBox al cambiar la propiedad Value. Para lograr que también al cambiar la selección del ListBox se produzca un cambio en la propiedad Value del objeto calendario es necesario indicar el enlace de manera más verbosa (segmento (3) del listado 5). Note que el enlace se especifica mediante el uso de Binding con la propiedad Value del elemento que se obtiene de la fuente relativa TemplatedParent, o sea el elemento al cual se le esté aplicando la plantilla. Hasta aquí se tiene lo mismo que con TemplateBinding. La diferencia radica en que al hacerlo con Binding se puede además especificar la dirección del enlace (en este caso, decir que es en ambos sentidos haciendo Mode=TwoWay). O sea, de este modo al cambiar cualquiera de las propiedades Value, sea la del propio control o la del SelectedItem del ListBox contenido en su plantilla, la otra debe actualizarse con el mismo valor. Pero el lector puede haber notado que está faltando algo por explicar. La propiedad Value en el control lo que tiene es una fecha, pero los ítems con los que se quiere rellenar el ListBox deben ser todos los números de los días del mes de dicha fecha, unidos a los días del mes anterior y del mes posterior que hagan falta para completar la primera y última semana... Convirtiendo una fecha en todas las fechas a poner en el calendario del mes La propiedad ItemsSource con la que se “alimentan” los ítems a visualizar por el ListBox, es de tipo IEnumerable, por tanto mediante esta propiedad se puede “rellenar” el ListBox asociándole una colección física, o asociándole el resulta- do de una iteración (que es lo que haremos en este caso). Observe en el listado 5 (segmento (4)) que se ha puesto sobre esta propiedad un Binding idéntico al que se puso sobre la propiedad SelectedItem (segmento (3)), solo que éste está definido de forma explícita, no se aplica en ambos sentidos, y además tiene indicado un convertidor (es decir, un valor asociado a su propiedad Converter). Este valor que se asocia a la propiedad Converter es el nombre de un método (DateToMonthDatesConverter en este ejemplo) que será el encargado de convertir el valor de la propiedad Value (un DateTime en este ejemplo) en un IEnumerable, que produce en este caso los valores de tipo DateTime correspondientes a todos los días del mes en cuestión, incluyendo los días del mes anterior que completan la primera semana y los días del mes posterior que completan la última semana (ver código C# del listado 6). Para definir estos convertidores hay que implementar los dos métodos Convert y ConvertBack de la interfaz IValueConverter. Estos son los que hacen la conversión en ambos sentidos. En este caso, ConvertBack se ha implemen- public class DateToMonthDatesConverter : System.Windows.Data.IValueConverter { public object Convert(object value, Type targetType, object parameter, CultureInfo culture) { DateTime dt = (DateTime) value; return GetDatesOfTheMonth(new DateTime(dt.Year, dt.Month, 1)); } private IEnumerable<DateTime> GetDatesOfTheMonth(DateTime firstDay){ int dayOfWeek = (int)firstDay.DayOfWeek; // Convirtiendo enumerativo en entero DateTime current = firstDay.AddDays(-dayOfWeek - 1); // Para poder rellenar con los días del mes anterior int daysInMonth = DateTime.DaysInMonth(firstDay.Year, firstDay.Month) + dayOfWeek; for (int j = 0; j < daysInMonth + 7 - (daysInMonth % 7); j++) { current = current.AddDays(1); yield return current; } } public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) { return null; // No interesa conversión en este sentido, la práctica en WPF es devolver null } } Listado 6. Conversión de una fecha en un iterador que nos dé todas las fechas del mes de esa fecha. <<dotNetManía fila y así sucesivamente. Observe en el segmento (2) del listado 5 que se ha utilizado un UniformGrid con 7 columnas para las letras de los días de la semana. Más abajo, segmento (5) del listado 5, se le indica al ListBox que utilice un UniformGrid, también de 7 columnas, para ubicar cada ítem del ListBox, es decir para que vaya ubicando todos los días por columnas de a 7. Una vez que ya el código XAML de la plantilla se encarga de repartir los elementos en su lugar, solo queda finalmente enlazar esto con la fecha representada en la propiedad Value de nuestro Calendar. Se ha escogido como elemento contenedor de los días del mes a un ListBox porque necesitamos usar un control que permita seleccionar un elemento entre varios (ese elemento seleccionado es el día que suele aparecer destacado cuando se muestra e interactúa con un calendario). Para hacer que el ListBox tenga seleccionado el día actual entre el resto de los días del mes, se podía haber utilizado la extensión de marcado TemplateBinding del modo: 29 << dnm.plataforma.net Conclusiones Esta característica de separar la apariencia, a través de plantillas y estilos, de la funcionalidad, es una de las novedades más honorables de WPF en comparación con el resto de las API visuales que le anteceden tado como que devuelve null, ya que no se desea ninguna conversión en ese sentido. Para completar la apariencia del calendario, en el segmento (6) del listado 5 se ha agregado una plantilla de datos que define la apariencia que tendrá cada día del mes. Observe que esta plantilla ha sido asignada a la propiedad ItemTemplate del ListBox, lo cual indica que esta plantilla será aplicada para cada ítem del ListBox. Y observe también que como cada ítem es de tipo DateTime, los enlaces (bindings) de esta plantilla se pueden enlazar directamente con las propiedades de DateTime (Day, DayOfWeek): <<dotNetManía <DataTemplate> <TextBlock Name=”tb” Text=”{Binding Day}” FontSize=”9” HorizontalAlignment=”Right” TextAlignment=”Right”/> <DataTemplate.Triggers> <DataTrigger Binding=”{Binding DayOfWeek}” Value=”Sunday”> <Setter TargetName=”tb” Property=”Foreground” Value=”OrangeRed”/> </DataTrigger> </DataTemplate.Triggers> </DataTemplate> 30 A la plantilla se le ha incorporado entonces un DataTrigger (segmento (7) del listado 5) que se activa cuando la propiedad DayOfWeek de la fecha tome el valor “Sunday”. Cuando esto ocurre, el Trigger indica a su vez a través de un Setter que el color (Foreground) del bloque de texto que muestra al día (de nombre tb) debe ser OrangeRed. De esta manera se logra que los días que correspondan a domin- go aparezcan distinguidos con un color diferente en el calendario (figura 4). Una nota final Aunque la plantilla que define la apariencia del control Calendar se apoya en el uso de convertidores, y éstos a su vez se programan en C#, no se puede considerar que este código forma parte de la funcionalidad del control. De hecho, en este ejemplo se necesita este convertidor debido a la apariencia que se ha escogido para mostrar el calendario (y que quiere mostrar todas las fechas del mes al estilo del gadget de Windows). De haber escogido un simple TextBox para que el usuario escribiese una fecha, o incluso un TextBlock si solo se quisiese visualizar la misma, hubiese sido necesario un convertidor diferente (o puede que no hubiese sido necesario ninguno). De momento estos convertidores vienen a suplir una carencia en el poder expresivo declarativo de XAML, sobre lo cual trataremos en un próximo artículo. Con lo expuesto hasta aquí hemos querido darle una panorámica de cómo se definen algunos de los elementos básicos para construir un control personalizado desde cero. Siempre recuerde que todo comienza por escoger la clase base más apropiada para el control. Luego, es importante abstraerse de su parte visual para definir las propiedades de dependencia y demás miembros que definen la funcionalidad que el control tendrá cuando se opere con él desde código. Y para finalizar, se define en generic.xaml la plantilla por defecto que tendrá el control, utilizando las novedosas bondades gráficas de WPF. Claro que comparado con el gadget Calendario de Windows Vista de la figura 1, este control aún está incompleto. Habría que añadir un espacio para mostrar y seleccionar tanto el mes como el año y completar la funcionalidad esperada de un calendario (en el que se puede pasar de mes y de año). Pero no hay espacio para más. Esperamos que lo mostrado le sirva de impulso al lector para terminar por su cuenta este calendario, continuando en este mundo fascinante que es WPF. Incluso, anímese a animarlo, colóquele un efecto de paso de páginas del almanaque con un efecto 3D como el que se desarrolló en el artículo [3]. Referencias [ 1] Hernández Yamil, Sierra Iskander, Del Valle Mario, Katrib Miguel. Cómo definir nuestros propios paneles personalizados en WPF, dotNetManía No. 35, marzo 2007. [ 2] Del Valle Mario, Sierra Iskander, Hernández Yamil, Katrib Miguel. Entrando en la tercera dimensión, dotNetManía No. 37, mayo 2007. [ 3] Sierra Iskander, Del Valle Mario, Hernández Yamil, Katrib Miguel. Paso de páginas en 3D con WPF, dotNetManía No. 38, junio 2007. [ 4] Del Valle Mario, Sierra Iskander, Hernández Yamil, Katrib Miguel. Controles personalizados en WPF: un scroll circular, dotNetManía No. 40, septiembre 2007. sharepoint Gustavo Vélez Manejadores de eventos en SharePoint 2007 Los flujos de trabajo no siempre son necesarios para hacer que las cosas funcionen bien Los manejadores de eventos de SharePoint 2007 son más que una herencia de la versión 2003: en WSS y MOSS 2007 han sido ampliados y enriquecidos considerablemente.Ahora pueden ser utilizados a lo largo y ancho del sistema, y su modelo de objetos ha sido mejorado en diferentes direcciones. Los manejadores de eventos permiten realizar algunas de las tareas que los flujos de trabajo están en capacidad de ejecutar, pero de una forma mucho más sencilla y efectiva, e inclusive pueden anticiparse a eventos, algo que es difícil de realizar con flujos de trabajo. Qué es un manejador de eventos, y su relación con los flujos de trabajo Gustavo Vélez es ingeniero mecánico y electrónico, especializado en el diseño, desarrollo e implementación de software (MCSD) basado en tecnologías de Microsoft, especialmente SharePoint. Es creador y webmaster de http://www.gavd.net/se rvers, y trabaja como senior developer en Winvision (http://www.winvision.nl) Windows es un sistema operativo que funciona a base de eventos: cada vez que un usuario interactúa con Windows, éste lanza internamente un evento para anunciar que algo ha ocurrido. Un desarrollador puede capturar el evento y crear software que reaccione de una forma específica. SharePoint es un sistema basado en tecnología Windows, y por lo tanto permite capturar eventos (un documento ha sido subido a una librería, un elemento ha sido eliminado de una lista, etc.), permitiendo crear software que tome las acciones apropiadas. El concepto de manejador de evento fue introducido en la versión 2003 de SharePoint y fue utilizado principalmente para crear flujos de trabajo. Con la aparición de Workflow Foundation y su integración con SharePoint 2007, esta función ha desaparecido mayoritariamente; a pesar de esto, el sistema de eventos dentro de SharePoint ha sido ampliado considerablemente y permite realizar tareas que no son posibles de implementar con los flujos de trabajo. Los nuevos eventos permiten controlar prácticamente todas las acciones que ocurren dentro de SharePoint: agregar, modificar o eliminar campos en elementos de listas, librerías, Webs y sitios. Pro- bablemente la mayor diferencia con los flujos de trabajo es que los eventos pueden ser lanzados antes de que la acción se ejecute, lo que no es posible (o difícil de programar) con flujos; además, las modificaciones en la infraestructura de Webs y sitios no son detectadas por Workflow Foundation pero sí por la infraestructura de los manejadores. Y hay una diferencia más: un manejador de eventos puede iniciar un flujo de trabajo, mientras que lo contrario no es posible. Al otro lado de la moneda, los manejadores de eventos tienen muy pocas posibilidades para interactuar con el usuario: son iniciados automáticamente, sin que el usuario pueda hacerlo manualmente, no es posible detenerlos para aceptar configuración de datos, y los desarrolladores deben tomar todas las precauciones necesarias si algo no funciona correctamente, pues no tienen mecanismos de protección como los que tienen los flujos de trabajo. Arquitectura y nuevas posibilidades Los manejadores de eventos de SharePoint 2003 eran aplicables solamente a librerías de documentos, imágenes y formularios, y estaban restringidos a los eventos de proteger/desproteger, copiar, eliminar, insertar y modificar o cambiar el nombre. << dnm.sharepoint Como se puede observar, en los nombres de los eventos hay dos tipos principales: • Eventos sincrónicos, cuyo nombre termina en “ing”. Ocurren antes de que la acción del evento sea implementada, suspendiendo el proceso hasta que el código del manejador ha sido completamente ejecutado. Esto permite modificar la acción, por ejemplo, para cancelar la modificación de un documento antes de que el cambio se realice. • Eventos asincrónicos, cuyo nombre termina en “ed”, que ocurren después de que la acción ha tomado efecto. Esta era la única posibilidad con SharePoint 2003; en 2007, de igual manera, el evento no se ejecuta inmediatamente, sino que puede tener un retraso de hasta algunos segundos. Note que no existen eventos para cuando se agregan o modifican sitios o Webs. También es importante mencio- nar que los valores de las propiedades Before y After de los eventos en librerías siempre mostrarán los valores correctos en las librerías, pero en las listas solamente es preservado el valor After. Programación Los manejadores de eventos para SharePoint 2007 son programados como librerías de clases de código manejado en .NET 2.0 o superior utilizando C# o Visual Basic. El siguiente ejemplo (listado 1) muestra cómo crear un manejador sencillo, que agrega un vínculo en una lista de vínculos cada vez que un documento es añadido a una librería de documentos. El ejemplo es solamente para mostrar la forma de programación, y para utilizarlo como código de producción debería ser ampliado y mejorado. Inicialmente, debemos crear una biblioteca de clases en Visual Studio 2005, utilizando un nombre único (por ejemplo, CreadorVinculos), y asignar un nombre adecuado a la clase creada por defecto (digamos ClaseCreadorVinculos). Asimismo, hay que agregar una referencia a Windows SharePoint Services (Microsoft.SharePoint.dll) y es conveniente agregar la sentencia de importación correspondiente (mediante using en C# o Imports en Visual Basic) en el código. La nueva clase debe heredar de una de las clases para manejadores de eventos; en el caso del ejemplo, de SPItemEventReceiver. Todos los métodos de la clase base pueden ser sobrescritos, incluso más de uno si es necesario. En el ejemplo se sobrescribe el método ItemAdded. El parámetro properties contiene toda la información sobre el elemento que ha lanzado el evento: una tabla hash con los valores de las propiedades antes y después del evento, el contexto de SharePoint, información sobre el usuario (identificador, nombre, login), la lista (identificador, elemento, título), sitio y Web. Como los manejadores de eventos no se ejecutan bajo el contexto de SharePoint o IIS, no es posible utilizar el archivo web.config del sitio para guar- using using using using System; System.Collections.Generic; System.IO; Microsoft.SharePoint; namespace CreadorVinculos { public class ClaseCreadorVinculos : SPItemEventReceiver { public override void ItemAdded( SPItemEventProperties properties) { base.ItemAdded(properties); } } } Listado 1 dar información que pueda ser cambiada fuera del programa, como es deseable para el nombre de la lista de vínculos del ejemplo; pero el archivo machine.config sí se puede utilizar para tal efecto. Este archivo no posee una sección AppSettings por defecto, pero es posible agregar una si es necesario. Por lo tanto, en el fichero machine.config (C:\WINDOWS\Microsoft.NET\Framework\v2 .0.50727\CONFIG\machine.config) añadimos una nueva sección debajo del cierre del último elemento (</configSections>): <appSettings file = “”> <add key = “NombreListaVinculos” value = “NombreLista” /> </appSettings> El valor puede ser leído desde el código de la forma tradicional: base.ItemAdded(properties); string miListaVinculos = System.Configuration. ConfigurationSettings. AppSettings[“NombreListaVinculos”]; Finalmente, utilizando el parámetro properties es posible obtener una referencia a la librería (SPList) en donde se ha lanzado el evento, y crear el <<dotNetManía Además, solamente un manejador de eventos se podía utilizar en cada librería. En SharePoint 2007 (WSS y MOSS), hay cuatro clases que ofrecen eventos: 1. La clase SPEmailEventReceiver, con un evento (EmailReceived). 2. La clase SPItemEventReceiver, con eventos para atrapar inserciones (ItemAdding/ItemAdded), eliminaciones (ItemDeleting/ItemDeleted), cambios (ItemUpdating/ItemUpdated), adición o eliminación de archivos adjuntos ( ItemAttachmentAdding /Added, ItemAttachmentDeleting/Deleted), protección y desprotección (ItemCheckingIn-Out/CheckedIn-Out, ItemUncheckingOut/ UncheckedOut), cambio de sitio ( ItemFileMoving/ Moved) y conversión de archivos (ItemFileConverted). 3. La clase SPListEventReceiver, para sucesos a nivel de campos de listas (FieldAdding/Added, FieldDeleting/ Deleted, FieldUpdating/Updated). 4. La clase SPWebEventReceiver, para eventos en sitios y Webs (SiteDeleting/Deleted, WebDeleting/Deleted, WebMoving/Moved). 33 << dnm.sharepoint vínculo en la lista de eventos. El código completo del método sobrescrito se muestra en el listado 2. el problema de trasladar la información de los errores hacia el exterior. Existen varias formas de hacerlo: principal- public override void ItemAdded(SPItemEventProperties properties) { SPSite miSite = null; SPWeb miWeb = null; base.ItemAdded(properties); try { string miListaVinculos = System.Configuration. ConfigurationSettings.AppSettings[“NombreListaVinculos”]; miSite = new SPSite(properties.SiteId); miWeb = miSite.OpenWeb(); SPList miLista = miWeb.Lists[miListaVinculos]; SPListItem miVinculo = miLista.Items.Add(); miVinculo[“URL”] = properties.WebUrl + “/” + properties.ListItem.Url + “, “ + properties.ListItem.Name; miVinculo.Update(); } catch(Exception ex) { TextWriter miEscritor = new StreamWriter(@”c:\ManejadorEventosError.txt”); miEscritor.WriteLine(ex.ToString()); miEscritor.Close(); } finally { if (miWeb != null) miWeb.Dispose(); if (miSite != null) miSite.Dispose(); } } <<dotNetManía Listado 2 34 En el código, luego de leer el nombre de la lista de vínculos que se ha guardado en el archivo machine.config, se crea una instancia de SPSite usando las propiedades del evento. Luego de crear una instancia de la lista de eventos, se le añade un nuevo elemento y se configura su URL para que apunte al documento recién creado en la librería de documentos. Como se ha indicado al principio, las posibilidades de interacción con el usuario son muy limitadas. El parámetro properties tiene un método ErrorMessage que se puede utilizar solamente en eventos sincrónicos cuando el código cancela la acción iniciada para enviar un mensaje al usuario. Esto crea mente escribir en el Visor de sucesos o en un archivo. En ambos casos, es necesario tener en cuenta que el usuario debe tener suficientes derechos para escribir (el usuario utilizado es el que se ha configurado en el Grupo de aplicaciones de IIS para el sitio de SharePoint). Las líneas de código en el bloque catch del ejemplo graban en un archivo el mensaje de error cuando ocurre algo inesperado. Los ensamblados que contienen manejadores de eventos tienen que ser instalados en el GAC (Global Assembly Cache), por lo que es necesario firmarlos con un nombre seguro. Para ello, en la pantalla de propiedades del proyecto de Visual Studio vamos a “Firma” | “Firmar el ensamblado”, en “Seleccione un archivo de clave de nombre seguro” seleccionamos “Nueva”, definimos un nombre en “Nombre de archivo de clave” (por ejemplo, CreadorVinculos), deseleccionamos “Proteger mi archivo de clave mediante contraseña” y generamos el proyecto. El ensamblado generado puede ser copiado directamente al GAC, o se puede utilizar la herramienta gacutil para ello. Instalación y utilización SharePoint no dispone de pantallas de administración para configurar manejadores de eventos, así que la forma recomendada para instalación es utilizar una solución de SharePoint que copie la DLL en el GAC, junto con una característica que configure la librería o lista. También es posible usar el modelo de objetos de SharePoint para configura el manejador. Una característica para instalar el manejador del ejemplo consta de dos archivos: uno llamado Feature.xml, con la definición de la característica en sí, y otro llamado Elementos.xml para identificar sus componentes. Entonces creamos dos archivos con estos nombres en un nuevo directorio llamado CreadorVinculosCaracteristica bajo C:\ Archivos de programa\Archivos comunes \Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES. El contenido del archivo Feature.xml se presenta en el listado 3, y el de Elementos.xml en el listado 4. En el listado 3, “GUID” debe ser sustituido por un identificador global único, que puede ser obtenido mediante la utilidad GuidGen.exe. <Feature Scope=”Web” Title=”Manejador Eventos CreadorVinculos”” Id=”GUID” xmlns=”http://schemas.microsoft.com/sharepoint/”> <ElementManifests> <ElementManifest Location=”Elementos.xml”/> </ElementManifests> </Feature> Listado 3 << dnm.sharepoint <Elements xmlns=”http://schemas.microsoft.com/sharepoint/”> <Receivers ListTemplateId=”103”> <Receiver> <Name> Manejador Eventos CreadorVinculos </Name> <Type>ItemAdded</Type> <SequenceNumber>10000</SequenceNumber> <Assembly>CreadorVinculos, Version=1.0.0.0, Culture=neutral, PublicKeyToken= 96290f904372ba8a </Assembly> <Class> CreadorVinculos.ClaseCreadorVinculos </Class> <Data></Data> <Filter></Filter> </Receiver> </Receivers> </Elements> Listado 4 En el listado 4, el valor de ListTemplateId es el número asignado a la lista de vínculos por SharePoint, y se puede encontrar en la respectiva definición CAML de la lista (en el archivo C:\Archivos de programa\Archivos comunes\Microsoft Shared\web server extensions\12\TEMPLATE\FEATURES\LinksList\ListTemplates\Links.xml, para la lis- ta del ejemplo). El tipo de evento es el que se ha definido en el código (ItemAdded), y el número de secuencia indica el orden en el que son lanzados los diferentes eventos: de 1.000 a 9.999 están reservados por SharePoint para sus propios eventos, así que es necesario utilizar un valor superior al límite máximo. El elemento Assembly es configurado con los parámetros del ensamblado utilizado: el nombre del ensamblado, su versión y cultura y la clave pública, que se puede encontrar con la herramienta sn.exe de Visual Studio (utilizando el parámetro -T). Finalmente, es necesario indicar el nombre de la clase que se va a utilizar. Luego de crear los dos archivos en el directorio indicado, la característica se puede instalar utilizando la herramienta de administración de SharePoint con la sintaxis: using using using using stsadm -o activatefeature -filename CreadorVinculosCaracteristica\Feature.xml –url http[s]://NombreServidor/NombreSitio Como el modelo de objetos de SharePoint dispone de todas las clases y métodos necesarios para configurar un manejador de eventos, la segunda forma para instalarlos es creando un pequeño programa. Además, manejadores para Webs o sitios tienen que ser instalados de esta manera, pues no es posible hacerlo con características. La aplicación de consola que se presenta en el listado 5 configura el manejador del ejemplo en una lista ( NombreDeLista ), después de que la DLL haya sido instalada manualmente en la GAC. System; System.Collections.Generic; System.Text; Microsoft.SharePoint; namespace InstallTasksCleaner { class Program { static void Main(string[] args) { SPSite miSitio = new SPSite(“http[s]://NombreServidor”); SPWeb miWeb = miSitio.OpenWeb(); SPList miLista = miWeb.Lists[“NombreDeLista”]; SPEventReceiverDefinitionCollection misReceptoresEventos = miLista.EventReceivers; SPEventReceiverDefinition miEvento = misReceptoresEventos.Add(); miEvento.Assembly = “CreadorVinculos, Version=1.0.0.0, Culture=neutral, PublicKeyToken= 96290f904372ba8a “; miEvento.Name = “Manejador Eventos CreadorVinculos”; miEvento.Type = SPEventReceiverType.ItemAdded; miEvento.SequenceNumber = 10000; miEvento.Class = “CreadorVinculos.ClaseCreadorVinculos”; miEvento.Update(); foreach (SPEventReceiverDefinition miDef in misReceptoresEventos) { Console.WriteLine(miDef.Name); } stsadm -o installfeature -filename CreadorVinculosCaracteristica\Feature.xml } } } Listado 5 <<dotNetManía Y se puede activar utilizando la pantalla de administración del sitio correspondiente, o por medio de la misma herramienta con la sintaxis: 35 << dnm.sharepoint <<dotNetManía Figura 1 36 Cada lista o librería dispone de una colección de receptores de eventos (SPEventReceiverDefinitionCollection), a la que se le puede añadir uno del tipo SPEventReceiverDefinition, y luego configurar sus propiedades. Note la similitud entre estas líneas de código y las utilizadas en la característica; ambos procesos utilizan el mismo código básico en la máquina de SharePoint. Luego de instalado, un usuario no puede ver cómo funciona el manejador de eventos, sino que solamente puede notar sus efectos (si es que son visibles). La figura 1 muestra la librería en la que se ha configura el manejador y el efecto en la lista de vínculos luego de agregar un documento. La configuración de la lista de vínculos por medio de machine.config es una forma fácil y rápida de cambiar la lista cuando sea necesario. Hay que tener en cuenta que el código del ejemplo es muy sencillo, y que tanto la librería de documentos como la lista de vínculos tienen que estar en el mismo sitio; pero modificarlo para hacerlo funcionar de cualquier modo deseado es fácil, y el modelo de objetos de SharePoint provee todas las herramientas para programar lo que sea necesario. Cuando se utilizan manejadores de eventos, es muy importante tener en cuente el impacto del código sobre la estabilidad y rendimiento de los servidores de SharePoint. Por ejemplo, crear un manejador que recorra listas con grandes cantidades de elementos es relativamente fácil de programar, pero si el evento que lo lanza ocurre frecuentemente, se verá disminuir el rendimiento del sistema hasta agotar los recursos de los servidores. Cuando sea necesario utilizar un escenario de este tipo, probablemente sea mejor crear algún mecanismo de almacenamiento en caché, o hacer que el proceso se mantenga en estado de latencia hasta que el sistema tenga menos carga de procesamiento. Conclusiones Los manejadores de eventos han existido en SharePoint desde hace ya algún tiempo, y en la última versión se han ampliado no solamente en su campo de acción, sino también en las posibilidades que ofrecen a los desarrolladores y en el alcance de su utilización. Los manejadores pueden sustituir en algunos casos a flujos de trabajo, pero debido a sus reducidas capacidades de interacción con los usuarios, su ámbito de aplicación se dirige más hacia la automatización de tareas rutinarias dentro de SharePoint. Las posibilidades de utilización son prácticamente ilimitadas, y su facilidad de programación e instalación los convierten en una de las mejores herramientas a disposición de los desarrolladores de SharePoint. plataforma.net Daniel Seara Componentes de uso general Acceso a datos Comenzamos aquí el desarrollo del primer componente de funcionalidad compleja. Dada la importancia que tiene a la hora de desarrollar aplicaciones, comenzaremos con el acceso a datos, explicando según se desarrolla los fundamentos y consideraciones en los que basa, para que se pueda usar, modificar o repensar completamente y que os pueda ser realmente útil. [ Daniel Seara es mentor de Solid Quality Mentors y director del área de desarrollo .NET. Es MVP desde 2003 y ponente habitual de INETA y Microsoft. NOTA No os ilusionéis. No hay modo que esto pueda desarrollarse en una sola nota. Serán, espero, dos. ] Si vamos a comenzar a hablar de componentes genéricos, es casi obligado empezar por el servicio de acceso a datos. Nada tan repetido, tan modificado, tan “versionado”… ni tan solicitado. Y aun cuando existen muchos ejemplos completos en Internet acerca de mecanismos y componentes de acceso a datos, realmente es probable que ninguno cumpla exactamente las necesidades de un caso particular. Hasta la propia Microsoft, al comenzar con su grupo PAG, comenzó sus publicaciones precisamente con un servicio de acceso a datos, que fue denominado DAAB (Data Access Application Block). DAAB forma parte hoy de Enterprise Library, que podrán encontrar navegando un poco por los sitios de MSDN. No voy a caer en comentar que para mí está mal hecho, porque posiblemente me tiraría tierra encima, ya que fui uno de los que aportó para la idea y el diseño, pero sí decir que yo lo hubiese hecho algo distinto. Como en estas publicaciones pretendo no solo “dar las cosas casi listas para usar”, sino también llevaros por los caminos del aprendizaje, voy a montar otro, que se parece bastante al que solemos usar. ¿Qué necesitamos? Hagamos una lista de lo que nos hace falta para operar adecuadamente con datos: • Conectarnos al motor. –Administrar las conexiones. • Obtener datos del mismo. –Un valor único. –Un conjunto de valores de una sola fila. –Un conjunto de filas. • Actualizar información. –Insertar. –Actualizar. –Eliminar. –Ejecutar procesos. • Operar dentro de transacciones. Y otra lista de lo que deberíamos tener en cuenta para tener un componente que realmente sea efectivo y completamente funcional: • Que disponga adecuadamente de los recursos. • Que optimice en rendimiento y escalabilidad. • Que informe de sus acciones de ser necesario, para permitirnos evaluar la funcionalidad de una aplicación y detectar sus cuellos de botella. • Que pueda operar con distintas versiones de bases de datos. << dnm.plataforma.net Consideraciones de diseño Volvamos a ODBC No, no. No voy a usar ODBC. Quiero que volvamos a ODBC para considerar cómo funciona e implementar algo similar. ODBC es la primera implementación de “buenas intenciones”: permitir a desarrolladores escribir aplicaciones que interactúen con distintos sistemas propietarios. ODBC no es más que un conjunto de estándares para que las empresas de bases de datos implementen ciertas funcionalidades a las cuales ODBC pueda acceder, y por el otro lado, un conjunto de llamadas estándar, para que los programadores “hablen” con las bases de datos siempre de la misma manera que normalmente llamamos API. O sea, manteniendo una interfaz común, desentendernos de cómo se implementa internamente; como un conector USB. Pero hagámoslo en .NET Este tipo de implementaciones en .NET se basan en dos características de la plataforma: pueden ser interfaces o pueden ser clases que obligatoriamente deban heredarse, comúnmente denominadas abstractas (MustInherit o abstract, dependiendo del lenguaje de programación). Public MustInherit Class BaseDataServer Protected Sub New() End Sub Protected ReadOnly Property Connection() As _ System.Data.Common.DbConnection Get End Get End Property Public Property Transaction() As _ System.Transactions.Transaction Get End Get Set(ByVal value As System.Transactions.Transaction) End Set End Property Public Function Execute(ByVal procedureName As String,_ ByVal ParamArray parameters As Object()) As Integer End Function Public Function GetValue(ByVal procedureName As String,_ ByVal ParamArray parameters As Object()) As Object End Function Public Function GetValues(ByVal procedureName As String,_ ByVal ParamArray parameters As Object()) As Object() End Function Public Function GetTable(ByVal procedureName As String,_ ByVal ParamArray parameters As Object()) _ As System.Data.DataTable End Function Public Function GetReader(ByVal procedureName As String,_ ByVal ParamArray parameters As Object()) As System.Data.Common.DbDataReader End Function Public Sub Fill(ByVal procedureName As String, _ ByRef destination As IEnumerable, _ ByVal ParamArray parameters As Object()) End Sub Public Function ExecuteSQL() As Integer End Function Public Function GetReaderFromSQL(ByVal sql As String)_ As System.Data.Common.DbDataReader End Function Public Function GetValuesFromSQL(ByVal sql As String)_ As Object() End Function Public Function GetValueFromSQL(ByVal sql As String)_ As Object End Function Public Sub FillFormSQL(ByVal sql As String,_ ByRef destination As IEnumerable) End Sub Public Function GetTableFromSQL(ByVal sql As String)_ As System.Data.DataTable End Function End Class Listado 1. Miembros de la clase base abstracta. <<dotNetManía Claro, llegados a este punto, comienzan las discusiones acerca de la versatilidad de este tipo de componentes, la “generalización” del código, la funcionalidad contra varias plataformas versus la especificidad de operar aprovechando las mejores características de un motor de base de datos específico, etc. Este tipo de consideraciones me persiguen desde la época de DAO (estoy hablando de la época en que Visual Basic tenía 7 disquetes ☺) o RDO o ADO (que todavía era huérfano, hasta que lo adoptó .NET). Bajo esas consideraciones, mucha gente comenzó grandes desarrollos con ODBC basados en la premisa “funciona con cualquier motor”. Lo cual no estaba del todo mal, pero se me hace que es “nivelar hacia abajo”… además de que eso no es tan cierto. Pero, por otra parte, me recuerdo colaborando hace años con una empresa que necesitaba codificar un sistema para que funcionase con al menos dos de las grandes bases de datos, pero una no retornaba registros si se ejecutaban procedimientos almacenados, lo cual alteraba en gran medida la funcionalidad de la aplicación. Con esto quiero decir que generalizar es bueno, pero sin exagerar, y que a veces, el resultado no sirve en todos los casos. 39 << dnm.plataforma.net De esta manera, se puede definir toda la interfaz de utilización (la API), a ser llamada desde las aplicaciones que usen este componente; pero en cada caso la implementación será específica por cada clase concreta. Diseñemos Básicamente, lo que necesitamos en nuestra clase abstracta se muestra en el diagrama de la figura 1. Y el detalle de las firmas de cada miembro se presenta en el listado 1. Algunos comentarios Figura 1. Clase base abstracta • Estoy dejando métodos para ejecutar sentencias SQL simplemente porque siempre alguien se queja que no los tiene. Lo lógico, seguro y óptimo en cuanto a ejecución es que todo se realice usando procedimientos almacenados. Tanto es así, que en la versión “real” dichos métodos están decorados con el atributo Obsolete, como se muestra: <Obsolete(“Sería mejor que uses un procedimiento almacenado”)>_ Public Function ExecuteSQL() As Integer End Function • Las propiedades Connection y Transaction son accesibles solo por esta clase base y aquellas que la hereden; de esta forma, nadie externamente accede “en directo” a la base de datos. Protected mConnectionString As String Ahora, dependiendo de qué motor de bases de datos se trate, podremos utilizar diferentes objetos Connection (SqlConnection, OracleConnection, OleDbConnection, etc.). Entonces nuestra clase abstracta manipulará la conexión como System.Data.Common.DbConnection, y le encargará a quienes hereden de ella que se responsabilicen de la creación de la instancia. Eso se logra con un método que tenga definición diferida (o sea, que deba definirse en las clases que heredan de ésta). Dicho método se declara con MustOverride (listado 2). Protected MustOverride Function InternalCreateConnection() As _ System.Data.Common.DbConnection Private mConnection As System.Data.Common.DbConnection = Nothing Protected ReadOnly Property Connection() As _ System.Data.Common.DbConnection Get If mConnection Is Nothing Then mConnection = internalCreateConnection() End If Return mConnection End Get End Property Listado 2 Algo similar, pero sin depender de las clases herederas, haremos con la transacción del listado 3. Private mTransaction As System.Transactions.Transaction Public ReadOnly Property Transaction() As _ System.Transactions.Transaction Get If mTransaction Is Nothing Then mTransaction = New System.Transactions.CommittableTransaction End If Return mTransaction End Get End Property Codificando lo abstracto Comencemos pues a codificar lo que implementa la clase base. <<dotNetManía La cadena de conexión 40 Vamos a obtener la misma a partir de dos posibles orígenes: o el programador especifica la cadena de conexión cuando crea una instancia, o indica el nombre de la entrada correspondiente en el archivo de configuración. Dado que esta es una clase abstracta, no puede crearse una instancia directamente. Ese trabajo será de las clases que hereden de ella. Sin embargo, sí podemos almacenar dicha cadena en esta clase base. Listado 3 [ NOTA Este tipo de manejo transaccional es óptimo siempre que estemos tratando con bases de datos que entiendan el espacio de nombres System.Transactions, como es el caso de SQL Server 2005. En otros casos, tal vez deberá utilizarse los objetos Transaction propios de cada clase de conexión a base de datos, o implementar EnterpriseServices, si se requiere manipular distintos orígenes de datos. ] << dnm.plataforma.net [ NOTA Si no se necesita manipular la transacción, sino simplemente utilizar la que exista en el entorno (como ocurriría en el caso de componentes de tipo ‘reglas de negocio’ que controlen por ellos mismos la transacción), en lugar de crear una instancia de CommitableTransaction se puede usar la que se maneja automáticamente, utilizando en el método de lectura la siguiente instrucción: mTransaction = System.Transactions.Transaction.Current ] Cada uno de los demás métodos utilizará entonces el miembro GetCommand. Como un ejemplo, veamos el método Execute: Public Function Execute(_ ByVal procedureName As String, _ ByVal ParamArray parameters As Object())_ As Integer Using c As DbCommand = _ GetCommand(procedureName, parameters) Return c.ExecuteNonQuery End Using End Function De los comandos a ejecutar Public Class DbCommandCollection Inherits Solid.Tools.SpecialCollection( _ Of String, System.Data.Common.DbCommand) End Class Nuestra clase abstracta expondrá una instancia de esta colección, como una propiedad que solo ella y aquellas que la hereden puedan utilizar: Cuando se requiera hacer algo con la base de datos, o sea, cuando se necesite un objeto Command, se buscará en dicha colección. En caso que el comando aún no esté, la clase hija deberá crear la instancia y obtener los parámetros que le correspondan. Ahora bien, una de las diferencias entre los motores de bases de datos es que cada uno maneja los parámetros de distintas maneras. Por ello, asignar valores a dichos parámetros puede diferir entre motores. Entonces, cuando hay diferencias, que cada quien se haga cargo de lo que le toca. Nuestra clase abstracta exigirá que sus hijas implementen un método que retorne un objeto comando listo para usar. Como además la forma de obtener los parámetros, etc. puede cambiar, también las clases hijas serán responsables de manipular la colección. Nuestra clase abstracta solo exigirá la definición del método, y hará uso de él. Protected MustOverride Function _ GetCommand( ByVal procedureName As String,_ ByVal ParamArray parameters() As Object)_ As System.Data.Common.DbCommand En los casos de los métodos que obtienen valores, el código de ejecución será algo distinto (listado 4). Public Function GetValue(_ ByVal procedureName As String, _ ByVal ParamArray parameters As Object())_ As Object Using c As DbCommand = _ GetCommand(procedureName, parameters) Return c.ExecuteScalar End Using End Function Public Function GetValues(_ ByVal procedureName As String, _ ByVal ParamArray parameters As Object())_ As Object() Using c As DbCommand = _ GetCommand(procedureName, parameters) With c.ExecuteReader( _ System.Data.CommandBehavior.CloseConnection) .Read() Dim retValues(.FieldCount-1) As Object .GetValues(retValues) Return retValues End With End Using End Function Listado 4 Al igual que cuando deba retornar readers o tablas (listado 5). Los métodos que utilizan sentencias SQL (mal hecho, insisto) harían cosas similares, con la excepción que, al no haber posibilidad de estar seguros de que son los mismos comandos que en la llamada anterior, habrá que crear el comando cada vez. Entonces, nuestra clase abstracta exigirá que exista un creador de coman- <<dotNetManía Uno de los temas importantes acerca de la utilización de los comandos de ejecución para procedimientos almacenados es que existen distintas formas de crearlos. En una de ellas, se va a la base de datos a buscar cuáles son los parámetros que dicho procedimiento almacenado utiliza. Dinámico, simple de codificar…, pero que consume muchos recursos del motor de base de datos. Conceptualmente, este componente de acceso a datos se basa en ese mecanismo, ya que recibe siempre el nombre del procedimiento almacenado, con lo cual este componente debe encargarse de investigar acerca de sus parámetros. Por ello, y para disminuir los “viajes” al servidor de datos para simplemente obtener metadatos, implementaremos un mecanismo de persistencia de objetos comando, que almacene los mismos con todos sus parámetros. La primera vez que se utiliza un comando, se investigan sus parámetros y se guarda el objeto Command correspondiente en caché. Aquí podemos entonces utilizar la colección especializada de nuestra entrega anterior, utilizándola como base de una clase específica para nuestros objetos comando: Protected mColCommands As _ DbCommandCollection Protected ReadOnly Property _ CommandColection() As DbCommandCollection Get If mColCommands Is Nothing Then mColCommands=New DbCommandCollection End If Return mColCommands End Get End Property 41 << dnm.plataforma.net como propiedades u otras funcionalidades especializadas. De momento, solo me queda espacio para sentar las bases. Primero, nuestra clase comando. Definimos una clase nueva, que implemente System.Data.IDbCommand. Public Function GetTable(ByVal procedureName As String, _ ByVal ParamArray parameters As Object()) As System.Data.DataTable Dim dt As New DataTable Using c As DbCommand = _ GetCommand(procedureName, parameters) dt.Load( _ c.ExecuteReader( _ System.Data.CommandBehavior.CloseConnection)) End Using Return dt End Function Public Class Command Implements System.Data.IDbCommand End Class Public Function GetReader(ByVal procedureName As String, _ ByVal ParamArray parameters As Object()) As System.Data.Common.DbDataReader Using c As DbCommand = _ GetCommand(procedureName, parameters) Return c.ExecuteReader( _ System.Data.CommandBehavior.CloseConnection) End Using End Function Al pulsar [Intro] en la línea que determina la herencia, aparecerán muchos métodos que deberemos codificar. Para ello, definiremos una variable de tipo DBCommand, y utilizaremos los mismos miembros de dicha variable, por ejemplo: Listado 5 dos a partir de sentencias SQL y hará uso de él (listado 6). can” entre sí, y que podamos, por ejemplo, manipular los parámetros Private mCommand As _ System.Data.Common.DbCommand Public Sub Cancel() Implements _ System.Data.IDbCommand.Cancel mCommand.Cancel() End Sub Protected MustOverride Function CreateCommand( ByVal sql As String) As DbCommand Y así con el resto, que espero podáis completar para el próximo número. Luego, en nuestra clase de acceso a datos, agregamos variantes de los mismos métodos que reciban esta clase como primer argumento (figura 2). <Obsolete(“Sería mejor que uses un procedimiento almacenado”)> _ Public Function ExecuteSQL(ByVal sql As String) As Integer Using c As DbCommand = CreateCommand(sql) Return c.ExecuteNonQuery End Using End Function Listado 6 <<dotNetManía Haciendo las cosas de la mejor manera 42 A veces, esto de preguntarle a la base de datos por los parámetros puede ser útil, pero no es la mejor manera de hacer las cosas. Por eso, vamos a tener los mismos métodos, pero que reciban objetos comando como parámetros. De esta forma, serán los componentes que usen este servicio quienes creen y definan los parámetros en cada caso. Y más que eso, nuestro servicio de datos reconocerá una clase abstracta para que todos los objetos comando se “parez- Figura 2. Diagrama de clases hasta el momento Isla VB Guillermo «Guille» Som Una isla para Visual Basic en dotNetManía O cómo hacer las cosas mejor con Visual Basic Con el quinto año de dotNetManía estrenamos una nueva columna en la revista dedicada exclusivamente a los que gustan desarrollar con Visual Basic. No es que antes no hubiera nada paraVisual Basic, pero ya iba siendo hora de que tuviésemos nuestro propio rincón. En esta isla tendremos ocasión de ver muchas cosas relacionadas con Visual Basic, particularmente con la versión 9.0, que es la que se incluye en Visual Studio 2008, aunque muchos conceptos serán válidos también para las versiones anteriores. Empezando por el principio (o casi) Guillermo “Guille” Som Es Microsoft MVP de Visual Basic desde 1997. Es redactor de dotNetManía, mentor de Solid Quality Mentors, tutor de campusMVP, orador de Ineta Latam, y autor de los libros “Manual Imprescindible de Visual Basic .NET” y “Visual Basic 2005”. http://www.elguille.info. Y qué mejor que empezar esta nueva sección con algunos consejos. No es que vayamos a tratar solo de cosas de iniciación, pero como soy consciente que muchos de los que se deciden a usar Visual Basic para .NET son desarrolladores que antes han usado Visual Basic 6.0, qué mejor que empezar comentando cosas que sirvan para ir deshaciéndose de viejas costumbres y preparando el terreno para lo que .NET nos ofrece, porque no debemos olvidar que Visual Basic 2008 (VB9 para los amigos) utiliza todo lo que las clases de .NET ofrecen, así que mejor acostumbrarnos a usar esas clases, al menos en la medida de lo posible. Sobre nombres y versiones Empecemos aclarando nombres y versiones, ya que algunos se confunden con la forma de llamar al lenguaje. Visual Basic 9.0 es el nombre “oficial” del compilador que se incluye con .NET Framework 3.5, que es el que se distribuye con Visual Studio 2008. Esta versión de .NET Framework en el fondo usa la versión 2.0 del runtime o motor de ejecución (CLR), aunque añade nuevas funcionalidades en forma de librerías (ensamblados DLL), que nos permiten usar cosas como LINQ y otras de las novedades de Visual Studio 2008 (para más información, ver el libro que los suscriptores de dotNetManía recibirán con el número de marzo de 2008). Con el entorno de desarrollo de Visual Basic 2008 (ya sea desde Visual Studio 2008 o desde la versión Express) podemos crear aplicaciones para cualquiera de las tres versiones de .NET Framework que se basan en el CLR 2.0, que son: .NET Framework 2.0, el mismo que se incluye con Visual Basic 2005 (o VB8); .NET Framework 3.0, que es el que permite crear aplicaciones basadas en Windows Presentation Foundation (WPF) y que se distribuye con Windows Vista; y finalmente, para .NET Framework 3.5. Al crear un nuevo proyecto, podemos seleccionar la versión de .NET que usaremos como “destino”, es decir, qué características queremos que estén a nuestra disposición. En la figura 1 podemos ver que al crear un nuevo proyecto tenemos la posibilidad de elegir la versión de .NET que queremos usar; por supuesto, dependiendo de qué versión usemos, tendremos disponibles los tipos de proyectos “soportados” por esas versiones. << dnm.isla.vb El compilador de Visual Basic 9.0 sabe más que los ratones coloraos No, no estamos en el programa de Canal Sur que dirige Jesús Quintero, pero esa expresión nos viene como anillo al dedo, ya que lo que debemos saber es que independientemente de la versión de destino, las novedades del compilador de Visual Basic 9.0 estarán a nuestra disposición. Es decir, el que elijamos .NET 2.0 como destino no querrá decir que solo podamos usar las instrucciones que había en la versión anterior de Visual Basic; lo que quiere decir es que no podremos usar nada que esté en las librerías que se distribuyen con versiones posteriores. Para que quede claro, el compilador que siempre se usa con los proyectos creados con Visual Studio 2008 es el compilador de esta nueva versión, es decir, el compilador de Visual Basic 9.0, independientemente de que usemos o no los nuevos ensamblados distribuidos con las versiones posteriores a la elegida al crear el proyecto. Por ejemplo, con Visual Basic 2008 (que usa el compilador de VB9) podemos usar todo lo relacionado con la inferencia de tipos (ya veremos con detalle esto de la inferencia, pero por ahora solo decir que el compilador de Visual Basic usará el tipo de datos adecuado para una variable, dependiendo del tipo que se le asig- ne). Lo mismo es aplicable a la creación de tipos anónimos; es decir, con Visual Basic 2008 podemos escribir un código como el del listado 1 y después de compilarlo, ejecutar ese programa en un Windows que solo tenga el runtime de .NET 2.0 Dim nums As Integer() = {1, 2, 3, 4, 5, 6, 7, 8} instalado. ¿Cómo es posible For Each n In nums que esto funcione en Console.WriteLine(n) .NET Framework 2.0? Next Por la sencilla razón de Dim dnm = New With {.Nombre = “dotNetManía”, .Año = 2008} que esas “novedades” de Visual Basic 9.0 Console.WriteLine(“dnm.Nombre = {0}, dnm.Año = {1}”, _ están en el compilador dnm.Nombre, dnm.Año) y ese compilador genera el código IL (el usa- Listado 1. Código de Visual Basic 2008 que se ejecutará con .NET 2.0. do por el runtime de .NET para compilar el código en tiempo de ejecución) adecuado para la versión de .NET que se ha elegido como destino, (en nuestro ejemplo el .NET Framework 2.0), y por tanto, el código funcionará en cualquier equipo que solo tenga instalado el runtime de la versión 2.0. Aclarar que el código del listado 1 utiliza Option Strict On; es Figura 2.Visual Basic 2005 no reconoce decir, que todas las las novedades de Visual Basic 2008. variables deben tener <<dotNetManía Figura 1. Las versiones de .NET disponibles al crear un nuevo proyecto. un tipo de datos adecuado, y por tanto no se permite que se asigne a una variable un valor que no sea adecuado. De esta opción “estricta” hablaremos en un momento. Este código también usa Option Infer On, que es la opción que permite al compilador “inferir” el tipo que debe tener una variable. Por ejemplo, en el bucle For Each se usa la variable n, que no tiene un tipo definido, pero que el compilador deduce (acertadamente) que debe ser de tipo Integer, ya que se utiliza para recorrer los valores de un array de ese tipo. Lo mismo ocurre con la asignación a la variable dnm, en la que el valor asignado es un valor de tipo anónimo, es decir, un tipo de datos creado al vuelo. Por supuesto, si el código del listado 1 lo usamos con Visual Studio / Basic 2005 no funcionará, ya que el compilador de esa versión, aunque también use .NET 2.0, no sabe qué hacer con “esas cosas raras”; el resultado es un montón de erro- 45 << dnm.isla.vb res, tal como vemos en la figura 2, empezando por la definición de Option Infer y continuando con la variable n, o por el uso inadecuado de With, para finalizar con la variable dnm, ya que se intenta acceder a miembros inexistentes de una variable que teóricamente sería de tipo Object, aunque, en realidad, el error producido por el uso de With no le ha dado tiempo al compilador de indicarnos que Option Strict On requiere que todas las variables se declaren usando un tipo. Aunque VB9 sea muy listo, no hace milagros <<dotNetManía Lo que no podrá hacer el compilador de Visual Basic 2008 es usar características que no están disponibles, salvo porque haya otras librerías (o extensiones) que le permitan usarlas. Por ejemplo, en el listado 2 tenemos un código que usa las extensiones de LINQ, que están definidas en una librería que se distribuye con .NET Framework 3.5; por tanto, ese código no podrá compilarse para una versión de .NET anterior. 46 Figura 3. El diálogo “Agregar referencia” sólo muestra como disponibles las librerías adecuadas para la versión de destino de .NET Framework elegida. pulsando en el botón de opciones avanzadas de compilación, y eligiendo en el cuadro de diálogo mostrado (ver la figura 4) la versión de .NET que queremos usar. Por supuesto, si elegimos como destino .NET Framework 3.5, el equipo en el que vayamos a ‘ Esto no se podrá usar en .NET 2.0 usar este ensam‘ porque no están las referencias a LINQ blado tendrá que Dim q = From n In nums _ tener instalada esa Where n > 3 _ Select New With {.Numero = n, _ versión del runti.Descripcion = “El número es “ & n} me de .NET. Aunque, dependiendo Listado 2. Un código que usa las instrucciones para LINQ. de la alineación de los planetas, puede que no sea necesario. En serio Es posible que piense: vale, no pue(el VB9 no sabe nada de astrodo usar esa característica porque el compilador necesita una referencia a la librería System.Data.Linq.dll, ¿y si la añado? NOTA Bueno, puede intentarlo, pero no lo permitirá, tal como vemos en la figura 3, en En el código de ejemplo que la que se nos indica que esa librería no es acompaña al artículo, el procompatible con la versión de .NET que yecto dnm.vb.console2 utilihemos elegido como destino. za .NET Framework 3.5, Por tanto, si queremos usar esas pero también funcionará en librerías “especiales” de .NET Frameun sistema con .NET 2.0, work 3.5, podemos hacerlo, pero para aunque la llamada al método ello debemos cambiar la versión de que utiliza características .NET para la que queremos compilar, específicas de .NET 3.5 no en este caso a .NET 3.5. Ese cambio lo funcionará. podemos hacer en la ficha de compilación (en las propiedades del proyecto), [ ] nomía), el que indiquemos una versión de .NET superior a la 2.0 solo nos obligará a tener esa misma versión instalada en el equipo en el que despleguemos la aplicación si usamos alguna de las características incluidas en esa versión. Por ejemplo, si compilamos el código del listado 1 con .NET Framework 3.5 como destino, también funcionará en sistemas que no tengan instalado el runtime de .NET 3.5, ya que el código no usa ninguna característica de esa versión de .NET Framework. Sin Figura 4. En cualquier momento podemos cambiar la versión de .NET que queremos usar en nuestro proyecto embargo, para que funcione el código del listado 2, el equipo en el que se utilice esa aplicación deberá tener instalado el runtime de .NET 3.5. Recomendaciones para un buen uso de Visual Basic Para terminar este primer artículo de la nueva sección de dotNetManía dedicada a Visual Basic, voy a dar una serie de consejos que nos facilitarán la programación con Visual Basic 2008 (algunas también son válidas para las versiones anteriores). << dnm.isla.vb mos que se hará una conversión es que eso nos dará pistas para saber que es posible que esa cadena no contenga en realidad un valor adecuado para un tipo entero (para más detalles, ver mis artículos en dotNetManía 43 y 44 relacionados con la depuración en .NET). Option Strict está desactivada al instalar el Visual Studio Cuando instalamos Visual Studio (o Visual Basic Express), el valor predeterminado de Option Strict será Off (desactivado); por tanto, recomiendo encarecidamente que la dejemos activada para todos los nuevos proyectos. Esa activación la haremos seleccionando “Opciones” (“Options”) del menú “Herramientas” (“Tools”) y del cuadro de diálogo mostrado, en la rama “Proyectos y soluciones” (“Projects and Solutions”) tendremos la opción “Valores predeterminados de VB” (“VB Defaults”) en el que podremos elegir el valor On para Option Strict, tal como vemos en la figura 5, que está capturada de una instalación en inglés de Visual Studio 2008, ya que a la hora de escribir este artículo la versión en español aún no está disponible. Una vez modificada esta opción, cada vez que creemos un nuevo proyecto se usará el valor On de forma predeterminada. Usar adecuadamente Option Strict (y el resto de opciones) Una de las ventajas que tiene Visual Basic es que estas opciones las podemos personalizar a nivel de fichero de código. Es decir, si en nuestro proyecto tenemos activada Option Strict (o cualquier otra de las opciones mostradas en la figura 5) y necesitamos usar una de esas opciones de forma desactivada en un fichero concreto (por ejemplo, en una clase que use COM –ver dotNetManía número 22–, y que por las circunstancias queramos usar esos objetos COM mediante lo que se conoce como enlace tardío –late binding–), podemos utilizar la instrucción Option Strict Off en ese fichero en concreto; de esta forma, “todo” el código que usemos en ese fichero de código tendrá desactivada la comprobación estricta. [ ] NOTA Si queremos “mezclar” código que use diferentes valores de Option Strict, lo podemos hacer creando clases parciales, de forma que el código que use el valor desconectado (Off) esté en un fichero independiente, con idea de que no perdamos las ventajas de usar esa opción en modo On. Figura 5. Predeterminar Option Strict On para los nuevos proyectos <<dotNetManía Una de las recomendaciones que siempre hago es que dejemos activada la opción de comprobación estricta del código, es decir, dejar siempre en On la opción Option Strict. Al usar Option Strict On en nuestro código, el compilador de Visual Basic hará ciertas comprobaciones de buen uso de las variables y las conversiones que realicemos. Algunos verán que tener activada esta opción es... ¡un peñazo!, ya que nos obliga a pensar un poco antes de hacer las cosas. Con esa opción estricta activada, tendremos que declarar todas las variables con un tipo de datos adecuado, además de que al realizar las conversiones entre tipos diferentes de datos siempre tendremos que hacer la conversión (cast, que dice la gente que prefieren usar lenguajes de la familia C) adecuada. Pero en realidad esto no es un impedimento, y a la larga saldremos ganando, particularmente porque nos evitaremos quebraderos de cabeza en errores (bugs) que en el otro caso serían difíciles de detectar. Cuando Option Strict está desconectado, el compilador hará las conversiones que estime oportunas, que algunas veces pueden ser erróneas. Pero esa no es la peor parte, ya que si definimos una variable sin un tipo específico, el compilador usará el tipo Object como tipo de datos (siempre que no tengamos activado Option Infer), con lo cual el rendimiento final puede que no sea el mejor. En cualquier caso, el entorno de trabajo de Visual Basic (IDE) utiliza lo que se conoce como compilación en segundo plano; es decir, cada vez que escribimos una línea de código, el IDE comprueba si esa línea de código es correcta, y en caso de que no lo sea, nos muestra opciones para rectificarla. Por ejemplo, si queremos asignar a una variable de tipo entero el valor de una variable (o constante) de tipo cadena, nos avisará de que Option Strict no lo permite, y nos ofrecerá una alternativa, que no es otra cosa que la conversión de la cadena en un entero, por medio de la función CInt. Vale, muy bien, pero eso mismo es lo que hará el compilador en tiempo de ejecución. Sí, pero la ventaja de que sepa- 47 << dnm.isla.vb Sobre la inferencia automática de tipos (Option Infer) Como hemos visto en el código de los listados mostrados anteriormente, Visual Basic 9.0 es capaz de “adivinar” el tipo de datos que tendrá una variable dependiendo del valor que le asignemos. Esto es posible solo si tenemos activada Option Infer, que es el estado por defecto que tendrán todos los proyectos creados con Visual Basic 2008. Esto supone que si por olvido (o comodidad) no indicamos el tipo de datos de una variable, el tipo será el que el compilador crea que debe ser. Por ejemplo, si declaramos esta variable: <<dotNetManía Dim saludo = “Hola, Visual Basic” 48 El tipo de datos que tendrá la variable saludo será String, ya que es eso lo que estamos asignando, una cadena. La inferencia automática de tipos es independiente del valor que tenga Option Strict, y solo la podemos usar si usamos Option Infer On. En cualquier caso, no debemos abusar de la inferencia automática de tipos, y mi recomendación es usarla solo en casos muy concretos, como puede ser el uso de variables en bucles o cuando estemos haciendo pruebas; pero no es recomendable usarla en todas las ocasiones, ya que la lectura de nuestro código se puede complicar más de lo deseado. Como excusa o justificación, decir que siempre nos queda el recurso de pasar el cursor del ratón sobre una variable y averiguar de qué tipo de datos esa variable es, tal como vemos en la figura 6, donde a pesar de estar desactivada la comprobación estricta de tipos, el tipo de la variable es String y no el que tendría de forma predeterminada ( Object) si no tuviésemos activado Option Infer. Lo que sí debemos saber es que la inferencia automática de tipos solo se aplica a variables locales, es decir, a las variables declaradas “dentro” de un procedimiento (método, propiedad, constructor, etc.). Por lo tanto, no podremos usar esta característica para ne la clase Process (y que Visual Basic siempre importa automáticamente). Figura 6. La inferencia automática de tipos es independiente del valor de Option Strict. los parámetros de esos procedimientos ni como valor devuelto por los mismos, y tampoco podremos declarar variables “inferidas” a nivel de módulo. En este último caso, si tenemos desactivada Option Strict, la variable será de tipo Object, y si tenemos activada la comprobación estricta, recibiremos un error indicándonos que eso no es aceptable, ya que todas las variables deben declararse con la cláusula As. Como recomendación, yo siempre procuro indicar en cada fichero qué opciones uso, independientemente de cómo las tenga configuradas en las opciones del proyecto (o del IDE), así las opciones estarán claras para todos los que lean mi código. También procuro añadir las importaciones de los espacios de nombres que uso en cada fichero, pero esto son más bien manías mías..., aunque en muchos casos me facilita la lectura del código, particularmente el que he escrito tiempo atrás; esa es otra de las características que siempre ha tenido Visual Basic para .NET, la de importar automáticamente los espacios de nombres que el compilador “cree” que debemos usar en nuestros proyectos. Y si después quiero reutilizar ese fichero de código en otro proyecto que no tiene la importación adecuada, pues... no es que pase mucho, ya que el IDE de Visual Basic 2008 me “asiste” y me da la opción de agregar la importación del espacio de nombres adecuado; al menos si eso es posible, tal como podemos ver en la figura 7, en la que en el proyecto no tengo agregada de forma automática la importación del espacio de nombres System.Diagnostics, que es el que defi- Figura 7. El IDE de Visual Basic 2008 permite agregar las importaciones que necesitemos. Por supuesto, para que esta asistencia funcione, debemos tener agregada la referencia correspondiente; es decir, si queremos usar la clase MessageBox desde una aplicación de consola, solo podremos hacerlo si previamente hemos agregado una referencia al ensamblado System.Windows.Forms.dll, ya que el editor de Visual Basic 2008 es bueno, pero, como dije antes, no hace milagros. Conclusiones Y esto es todo para este primer encuentro con la nueva sección dedicada exclusivamente a Visual Basic. Quisiera aclarar que aunque el código que mostraré en estos artículos será para Visual Basic 2008 (o VB9), en la medida de lo posible también crearé proyectos para C# que usen lo que trate en estos artículos; así los que han seguido mi sección “Inicio” en esta revista (desde que se inició, en el número 16, de Junio de 2005) no se sentirán abandonados. Por supuesto, muchas de las cosas que veremos usarán características propias de Visual Basic; en esos casos es posible que no haya código equivalente para C#, pero siempre nos quedará el recurso de crear un ensamblado de Visual Basic y usarlo desde otro proyecto de C#, que esa es una de las ventajas de la interoperabilidad de .NET. Lo importante es que... ¡nos seguimos viendo en dotNetManía! todonet@qa Dino Esposito es mentor de Solid Quality Learning. Es ponente habitual en los eventos de la industria a nivel mundial.Visite su blog en: http://weblogs. asp.net/despos. (todotNet.QA@ dotnetmania.com) Cachés globales y Silverlight En este artículo cubriremos las características relacionadas con la configuración de un sistema global de caché que abarca todas las máquinas de una granja de servidores Web y desvelaremos un sencillo truco para detectar la versión del plug-in de Silverlight instalado, para construir páginas Silverlight dependientes de la versión. Recientemente, hemos escalado una aplicación Web grande a múltiples servidores Web, organizados en una granja. La mayoría de las páginas hacen uso intensivo de la caché de ASP.NET. En la nueva configuración, experimentamos algunos problemas menores relativos a la caché. Era como si los mismos datos fueran descargados y puestos en la caché varias veces y no siempre aparecieran en sincronía. Después de alguna investigación, hemos determinado que se debe a la nueva arquitectura multi-servidor, donde la caché no parece estar compartida entre los servidores. La pregunta es ¿cuál es la mejor forma de crear un sistema global de caché en ASP.NET? El objeto Cache de ASP.NET es específico del Dominio de Aplicación, y, por supuesto, no puede ser compartido entre múltiples CPU y servidores. Las técnicas de caché en ASP.NET pierden mucho cuando la aplicación se escala a un modelo de jardín de servidores (un servidor y múltiples CPU) o de granja (múltiples servidores y una CPU cada uno). En ASP.NET, se crea una instancia de Cache para cada aplicación y cada aplicación se asocia a un Dominio de Aplicación en su proceso. La clase Cache se implementa como un wrapper (envoltorio administrado) en torno a una clase interna que es un contenedor del tipo IEnumerable. La clase real usada para implementar el objeto Cache varía dependiendo de las CPU implicadas. Si solo hay una disponible, el sistema usa una clase interna llamada CacheSingle; en otro caso, utiliza la clase CacheMultiple. De cualquier manera, se almacenan ítems de datos en una tabla hash (una estructura de datos extremadamente rápida en su acceso, y con un tiempo de acceso prácticamente constante). Existirá una tabla hash por cada CPU. En su operativa, lo que sucede es que CacheSingle opera sobre una sola tabla hash, mientras que CacheMultiple maneja un array de tablas hash. Asumido este modelo, no sorprende que en una máquina multiprocesador en la que se ha vinculado más de una CPU con el proceso ASP.NET, cada procesador acaba teniendo su propia copia del objeto Cache para cada dominio de aplicación (léase, aplicación ASP.NET) gestionado. Estos objetos Cache no están sincronizados. En un jardín no puede garantizarse que los usuarios harán sus peticiones a la misma CPU (y proceso) en distintas solicitudes de páginas. Así que el estado de la caché no garantiza una coherencia con lo que la última página hizo en la última petición. <<dotNetManía [email protected] Dino Esposito 49 <<dotNetManía [email protected] [email protected] << dnm.todonet@qa 50 Lo mismo es válido para granjas de servidores. En un escenario así, cada servidor Web tiene su propio objeto Cache por Dominio de Aplicación. Pero no puede asumirse que los usuarios volverán al mismo servidor en peticiones subsiguientes. Y si una petición que debe consumir datos alcanza un servidor donde no hay datos en la caché, se necesita una nueva lectura. Como consecuencia adicional, cualquier cambio realizado a un dato en el servidor A no será visible en el B. Mientras que habrá ocasiones en que todo parece ir bien, se acabará produciendo una falta de sincronía entre los datos de diferentes servidores que puede originar anomalías en algunas páginas de la aplicación. Esto es difícil de abordar y debe ser resuelto de una forma u otra. Así que la pregunta es: ¿existe un objeto similar a Cache que funcione a través de una arquitectura múltiple? ¿O existe alguna configuración que permita al objeto Cache soportar escenarios remotos? La respuesta, desafortunadamente, es que no, y si necesitamos uno, tenemos que construírnoslo nosotros mismos. Para construir un contenedor global de datos que funcione en estos escenarios, necesitamos recurrir a un recurso remoto y compartido, tal como una instalación de Microsoft SQL Server (u otra base de datos). Alternativamente, podemos considerar la creación de una capa de servicios (por ejemplo, un servicio WCF o un Servicio Web) que almacene los datos en una copia local de la base de datos, o use su propia memoria para almacenarlos. En otras palabras, se necesita replicar el modelo de extensibilidad que Microsoft ha creado para el estado de la sesión. El acceso a datos en caché de forma remota requiere serialización y deserialización y es una operación sujeta a los efectos de latencia de red. Francamente, no puede esperarse obtener el mismo nivel de respuesta que se obtiene en un escenario simple. El corolario de esto es que el modelo general para una caché global y multiservidor es lo suficientemente complejo como para invalidar las ventajas obtenidas del uso de la caché. La dicotomía a estudiar, a efectos de este problema, es la del acceso a datos semipreparados en contraposición a la obtención de nuevos datos mediante la ejecución de otra consulta. ASP.NET ofrece una infraestructura efectiva de caché en servidor simple, porque eso es lo que se necesita en la mayoría de las ocasiones. Extender la infraestructura para soportar otros escenarios es decisión propia, pero puede guardar desagradables sorpresas en el rendimiento. Dicho esto, ¿qué puede hacerse? Reescribir la aplicación no es una opción. Puedo imaginar que la mayor parte de los datos en caché son de solo lectura, o, al ASP.NET ofrece una infraestructura efectiva de caché en servidor simple, porque eso es lo que se necesita en la mayoría de las ocasiones menos, que cambien con poca frecuencia. Esa la razón de su uso, la mayor parte de las veces. También asumo que la aplicación tiene la facultad de rellenar la caché si encuentra que está vacía. El comportamiento del objeto Cache asegura que cada servidor de la granja obtiene sus datos de la fuente original y los ubica en la caché local. Esto quiere decir que se realizan varias consultas para rellenar las cachés de todos los servidores. Para este comportamiento, no se necesitan cambios en el código. A mí no me importaría acceder a la base de datos directamente siempre que se necesite. Manejar ficheros XML requiere algún trabajo de administración con el que podría ser complicado operar. Y la pregunta clave aquí es ¿cómo puedo notificar a otros servidores que ha tenido lugar un cambio en los datos almacenados en la caché de un servidor concreto? En un escenario de granja de servidores, aprovecharía el mecanismo de dependencia del objeto Cache de ASP.NET: añadimos los datos consultados al objeto Cache usando dependencia de ficheros. De esta forma, cuando el sello temporal (timestamp) del fichero se modifica, el contenido de la caché es invalidado y se carga nuevamente en el siguiente acceso. Si se utiliza un fichero ubicado en una zona compartida de red, se pueden invalidar simultáneamente todas las cachés de todos los servidores. La siguiente petición lanzará una nueva consulta sobre el servidor solicitado. Si el cambio que invalida la caché se refleja inmediatamente en la base de datos, no hay problema. Si el cambio está limitado a la caché del servidor donde sucede la petición, y se persiste a la base de datos posteriormente, entonces la que los datos expiraron: usando esta información podemos discernir si es necesario rellenar la caché a partir de la base de datos, porque es el primer acceso, o acceder al recurso temporal, dado que algunos datos han cambiado. Estoy haciendo una aplicación en Silverlight 1.0 para nuestra Intranet. A causa del soporte limitado de WPF en esta versión, estoy usando un montón de Javascript para añadir capacidades de introducción de datos y un mínimo de posicionamiento en pantalla. Tenemos planes de migrar esta primera aplicación a Silverlight 2.0 tan pronto como podamos. ¿Hay forma de introducir algún tipo de control de versión en el código de una aplicación Silverlight? ¿Se puede escribir código para las dos versiones y discriminar la versión instalada? Cualquier página Web equipada con una versión de Silverlight debe enlazar el fichero Silverlight.js, que es parte actual del SDK. No se puede decir demasiado acerca del futuro, pero, actualmente, este fichero es el mismo para las versiones 1.0 y 1.1 (que se denominará 2.0 en la versión final). En ese fichero se encuentra un objeto llamado Silverlight con algunos métodos estáticos. El más típico es createObject, que se usa para instanciar el motor de Silverlight en una página Web. Otro método interesante, que es justo lo que buscas, es isInstalled. Este método determina si una versión concreta del producto está actualmente instalada en la máquina del usuario: if (!Silverlight.isInstalled("1.0")) alert("Please, install version 1.0."); El número de versión consta de 4 valores: versionMajor.versionMinor.buildNumber.revisionNumber. Sin embargo, normalmente, tú solo usarás versionMajor.versionMinor. El método devuelve ver- dadero igualmente si la versión instalada es superior a la comprobada. Así que, en el ejemplo anterior, la pregunta real es ¿tienes al menos la versión 1.0 instalada? Claramente, este método está diseñado para permitir deshabilitar algunas características en una página que requiera una nueva versión del plug-in de Silverlight. Es interesante notar que, sin embargo, no hay una forma directa de saber –a partir del complemento– de qué versión se trata, ni puede leerse directamente la versión mínima que fue solicitada por la página. El siguiente fragmento de código muestra una forma simple de averiguar cuál es la versión instalada. La figura 1 muestra un ejemplo en acción cuando se implementa esta característica. Figura 1 var results = "Unknown"; var versionToCheck = "2.0"; var v = Silverlight.isInstalled(versionToCheck); if (v) results = versionToCheck; else { versionToCheck = "1.1"; v = Silverlight.isInstalled(versionToCheck); if (v) results = versionToCheck; else { versionToCheck = "1.0"; v=Silverlight.isInstalled(versionToCheck); if (v) results = versionToCheck; } } Traducido al castellano por Marino Posadas <<dotNetManía puede ser necesario crear un fichero (o tabla) temporal en la ubicación compartida, y modificar el código existente para rellenar la caché para que utilice este recurso. El mecanismo de dependencia informa al código que hace la llamada acerca de la razón por [email protected] [email protected] << dnm.todonet@qa 51 Octavio Hernández Laboratorio.net Visual Guard .NET El reciente caso aparecido en la televisión nacional, donde una factura recibida por un usuario mostraba los apellidos de éste cambiados por palabras ofensivas, ha puesto en evidencia una vez más la importancia de que las aplicaciones a través de las cuales los empleados manipulan los datos con que las empresas operan realicen una correcta autenticación y registro de actividad de sus usuarios. Este mes presentamos Visual Guard .NET, un cómodo y útil marco de trabajo que permite gestionar de una manera eficiente y centralizada los usuarios, roles y permisos de una aplicación y asegurar el acceso de los usuarios a cada uno de los elementos de la misma. Ficha técnica Nombre: Visual Guard .NET Versión: 2.6 Fabricante: Novalys Sitio Web: http://www.visual-guard.com Categoría: Marcos de trabajo, seguridad Precio: solicitar cotización a través de la página Web (ddescuento del 15% a lectores de dotNetManía) ¿Qué nos ofrece Visual Guard? Visual Guard simplifica enormemente tareas como las siguientes: Gestionar los usuarios y roles de una aplicación y sus posibilidades de acceso Octavio Hernández es Mentoring Team Leader de Plain Concepts, editor técnico de dotNetManía y tutor de campusMVP. Es MVP de C# desde 2004, MCSD y MCT. El producto permite realizar la autenticación de usuarios (creados especialmente o provenientes de un Directorio Activo o una base de datos de SQL Server u Oracle) y su autorización para el acceso a una aplicación o partes de ella en base a un conjunto de roles y permisos creados por nosotros y almacenados en una base de datos de cualquiera de los tipos anteriores o en un fichero encriptado. Toda esa información de seguridad se almacenará en un repositorio que podrá o no compartirse entre diferentes aplicaciones. El formulario de autenticación puede ser personalizado según nuestros deseos. Permitir o impedir el acceso a aplicaciones o partes de ellas. En base a los datos suministrados durante la fase de autenticación, el motor de Visual Guard autorizará o no el acceso del usuario a una aplicación o a partes específicas de ella, como menús o formularios. Filtrar y proteger los datos confidenciales En dependencia de la identidad del usuario, ciertos elementos de la aplicación que muestren datos confidenciales podrán ser ocultados o hechos de solo lectura para impedir su visualización y/o modificación. Personalizar la interfaz de usuario También en función de la identidad del usuario, será posible (y muy fácil) modificar la interfaz de usuario de los formularios que componen la aplicación, por ejemplo mostrando u ocultando botones, campos, pestañas de un cuaderno, etc. << dnm.laboratorio.net Figura 2. Diálogo de autenticación estándar Figura 1. Consola de administración Parametrizar la lógica de negocio de la aplicación ¿Cómo funciona Visual Guard? Visual Guard consta de dos componentes fundamentales: • La Consola de administración de Visual Guard (figura 1), una herramienta para la edición visual de los usuarios, mecanismos de autenticación a utilizar, roles, permisos, acciones a llevar a cabo en casos de que un Ensamblado Contenido Novalys.VisualGuard.Security Las clases principales de Visual Guard. La referencia a este ensamblado es obligatoria. Novalys.VisualGuard.Security.File Clases necesarias para acceder a un repositorio almacenado en un fichero. Novalys.VisualGuard.Security.Oracle Clases necesarias para acceder a un repositorio almacenado en una base de datos de Oracle (8i o superior). Novalys.VisualGuard.Security.SQLServer Clases necesarias para acceder a un repositorio almacenado en una base de datos de SQL Server (2000 o superior). Novalys.VisualGuard.Security.WebForm Clases necesarias para integrar Visual Guard en una aplicación o servicio ASP.NET. Novalys.VisualGuard.Security.WinForm Clases necesarias para integrar Visual Guard en una aplicación Windows Forms. Tabla 1. Ensamblados que componen el motor de Visual Guard <<dotNetManía usuario no tenga ciertos permisos, etc. La Consola de administración permite actuar sobre un repositorio de seguridad, en el que se almacena toda esa información para una aplicación o grupo de aplicaciones específico. Este repositorio podrá almacenarse en una base de datos SQL Server u Oracle o en un fichero encriptado. • El motor de ejecución de Visual Guard, un conjunto de ensamblados a los que harán referencia las aplicaciones y que modificará el comportamiento de éstas en función de la identidad del usuario actual y de los permisos que tenga asignados en el repositorio de seguridad de la aplicación. Como parte del motor se incluye un proveedor de membresía (membership), mediante el cual los desarrolladores podrán gestionar programáticamente los usuarios y roles. La tabla 1 muestra los ensamblados que componen el motor. Otra cosa que puede ser necesaria y que Visual Guard hace muy sencilla es la parametrización de la lógica de negocio en función de los usuarios y roles. Por ejemplo, es común que un vendedor “corriente” tenga fijado un importe máximo de pedido inferior al de sus jefes. Visual Guard ajustará la regla de validación correspondiente en función del usuario actual. 53 << dnm.laboratorio.net El motor de ejecución de Visual Guard modificará el comportamiento de las aplicaciones en función de la identidad del usuario Figura 3. La aplicación de ejemplo, utilizada por un administrador visibles los clientes británicos, y no se permite al usuario la inserción o borrado de clientes. El proceso de integración <<dotNetManía Figura 4. La aplicación de ejemplo, utilizada por un empleado de recursos humanos británico 54 En tiempo de ejecución, el motor de Visual Guard: 1. Gestiona la autenticación del usuario. La figura 2 muestra el diálogo de autenticación estándar, utilizado en la aplicación de ejemplo que acompaña al producto. Como hemos comentado anteriormente, usted puede suministrar su propio diálogo, si lo prefiere. 2. Se conecta al repositorio y recupera los permisos del usuario. 3. Ajusta dinámicamente la aplicación en función de esos permisos. Por ejemplo, cuando se abre un formulario, el motor de Visual Guard oculta controles y filtra datos según sea necesario. Las figuras 3 y 4 muestran la aplicación de ejemplo mientras está siendo utilizada por un administrador y un empleado del departamento de Recursos humanos de la oficina del Reino Unido, respectivamente. En el segundo caso, solo están El proceso de integración del motor de Visual Guard dentro de cualquier proyecto que estemos desarrollando constará típicamente de los siguientes pasos: 1. Agregar los ensamblados de Visual Guard al proyecto y activar la seguridad. 2. Implementar el formulario de autenticación de Visual Guard (o definir uno propio). 3. Crear un repositorio de seguridad y registrar el proyecto dentro de ese repositorio. 4. Los desarrolladores, utilizando la Consola, añaden al repositorio los permisos asociados al proyecto. 5. El proyecto es compilado y desplegado. 6. Una vez desplegado el proyecto, el administrador: a) Define roles y les asocia permisos de entre los existentes en el repositorio. b) Crea usuarios y los asocia a roles específicos. Observe que no se requieren conocimientos o habilidades técnicas especiales para administrar la seguridad. Conclusiones A lo largo de este artículo hemos intentado mostrar las principales posibilidades que nos ofrece Visual Guard para ayudarnos a crear aplicaciones seguras y personalizables en función de la identidad del usuario que las utiliza en cada momento. Se invita al usuario a descargar la versión de evaluación y probar las aplicaciones de ejemplo que se incluyen con el producto. Referencias [ 1] Sitio Web de Visual Guard: http://www.visual-guard.com biblioteca.net Silverlight 1.0 Unleashed Adam Nathan Editorial: Sams Publishing Páginas: 272 Publicado: octubre de 2007 ISBN: 978-0672330070 Idioma: inglés Ya hemos hablado en ocasiones anteriores del autor dentro de esta sección; la última, a propósito de su “WPF Unleashed”, considerado uno de los mejores libros en su temática. Hay que añadir que Nathan es Senior Software Development Engineer en Microsoft Redmond y también el arquitecto jefe de Popfly, el primer producto construido directamente sobre Silverlight. No necesita, pues, más presentación en cuanto a su conocimiento del tema. Esta es una obra que, abordando todo lo necesario para la construcción de páginas en Silverlight, se limita a la versión actual en producción: la 1.0. No hay referencias a la próxima versión (todavía en fase alfa). Pero la cobertura es excelente, los ejemplos sencillos y directos de utilizar, y los tips o trucos propios del SDK, de un valor excepcional. Muchos problemas cotidianos que se presentan durante el trabajo con esta versión se resuelven a través de estas ayudas. Hay mejores introducciones, no obstante, pero ésta es absolutamente recomendable. Silverlight 1.0 Devin Rader, Jason Beres, J. Ambrose Little y Grant Hinkson Editorial: Wrox Páginas: 288 Publicado: octubre de 2007 ISBN: 978-0470228401 Idioma: inglés Lo ideal sería tener –al menos hasta que aparezcan otras obras en preparación– la obra anterior junto a la que presentamos aquí. En esta ocasión, hay que resaltar dos aspectos que no son abordados –o no lo son con esta profundidad– por el libro de Nathan: la introducción y explicación de la parte estructural y arquitectónica en la que se basa Silverlight, junto al uso correcto de las herramientas para desarrollar en esta versión y, especialmente, la parte de Silverlight 1.1 (que se llamará 2.0, cuando se produzca su aparición oficial). novedades Los ejemplos son muy explicativos de la funcionalidad ofrecida por el SDK, y no hay parte de éste que no se encuentre citada en la obra. Hay, además, un capítulo interesante sobre el CLR y Silverlight 1.1 que sirve para vislumbrar las inmensas posibilidades que ofrecerá la nueva versión, y otro dedicado a explicar un caso de ejemplo, muy ilustrativo del abordaje de una aplicación. Microsoft ASP.NET 3.5 Developer Reference Dino Esposito. Editorial: Microsoft Press. Páginas: 992. Publicado: febrero de 2008. ISBN: 978-0735625273. Idioma: inglés. Pro Silverlight 1.1 Matthew McDonald. Editorial: APress. Páginas: 400. Disponible: abril de 2008. ISBN: 9781590599495. Idioma: inglés. TEXTO: MARINO POSADAS comunidad.net AndorraDotNet <<dotNetManía Grupo de usuarios .NET de Andorra 56 AndorraDotNet nace como el primer grupo de usuarios de .NET en Andorra, el pequeño país de los pirineos. La idea empezó a formarse en mi cabeza durante el MVP Summit realizado en Seattle en 2005, entre sesiones técnicas, barbacoas y alguna que otra visita a la Company Store. Allí, en medio de todo ese gentío empecé a pensar en el gran espíritu de la comunidad que flotaba por allí, y en que lo realmente bonito de estos eventos es el altísimo nivel de comunicación entre la gente y la pasión que trasmiten todos y cada uno de ellos. Esta pasión por las ganas de aprender, de enseñar, de poner lo mejor de cada uno en hacer las cosas bien, de transmitir conocimientos… ¡Qué diablos!, me dije, ¿por qué no iba a tenerla en casa?, ¡al fin y al cabo frikis amantes del desarrollo hay en todas partes! Después de hablarlo con Pep Lluís Baño, fundador de Spain.Net y por aquel entonces compañero de viaje (y de habitación) empecé a verlo más claro, y gracias a su ayuda inicial y a la de otra mucha gente, al final este proyecto se ha hecho realidad: el próximo día 1 de febrero se realizará el primer evento del grupo de usuarios, en el que a petición popular hablaremos de SharePoint como plataforma de servicios para el desarrollador. Desde entonces hasta aquí ha habido un largo camino, ya que la creación de un grupo de usuarios tal vez no parezca gran cosa, pero os aseguro que conlleva mucho trabajo. Primeramente debemos encontrar gente que tenga los mismos intereses y esté dispuesta a compartir el trabajo, hay que definir unos estatutos, buscar patrocinadores y colaboradores, crear el sitio portal del Web del grupo, visitar universidades y escuelas, y no hay que olvidarnos de la planificación de los eventos… Sin embargo, cuando existen ganas e ilusión en tirar un proyecto hacia adelante, no hay trabajo que valga, y si por ende contamos con miembros de lujo como Jorge Serrano (MVP ex-residente en el país), Carlos Segura (MVP fundador del Navarra User Group), el propio Pep Lluís Baño, o Miquel Viladrich (profesor en la universidad de Andorra), la verdad es que todavía resulta mucho más sencillo. Así pues, el objetivo de AndorraDotNet es el de realizar eventos periódicamente, en los que los << dnm.comunidad.net AndorraDotNet • • • • • • miembros del grupo (y todo el que quiera mientras no tengamos problemas de aforo) nos reuniremos para hablar de lo que mueve nuestra pasión: el desarrollo de software y todo lo relacionado con él. Desde arquitectura hasta metodologías de gestión de proyectos, pasando por nuestros lenguajes favoritos de .NET Framework, innovaciones tecnológicas, y ubicación: Andorra fecha de fundación: octubre 2007 fundador: Lluís Franco miembros: 20 página web: http://www.andorradotnet.com email de contacto: [email protected] cualquier cosa que se nos pase por la cabeza. Todo el mundo es bienvenido. Tanto si eres un profesional consolidado como si todavía estás estudiando, da lo mismo que residas en el país, en las cercanías, o simplemente estés de paso porque has venido a sacar brillo a los esquíes. ¡Lo realmente importante es que sientas esa pasión! Lluis Franco Eventos de OnobaNET El pasado 14 de diciembre, Bruno Capuano y Juan Luis Guerrero visitaron Huelva para un evento. Bruno presentó MS Robotics y Lego Mindstorms, y mostró las cosas que se pueden hacer con ellos. Por su parte, Juan Luis Guerrero mostró algunas de las novedades que trae el nuevo Visual Studio 2008. Este evento contó con gran afluencia de público (50 asistentes), entre los que destacaron dos pequeñines que vinieron a ver cómo funcionaba el robot :-). El 16 de enero de este nuevo año, nos visitó Eladio Rincón, del grupo de usuarios GUSENET, quien dió una magnífica charla sobre rendimiento de SQL Server 2008, junto con Miguel Rodríguez de OnobaNET, que realizó varias demos de Inteligencia de Negocio en MOSS (Form Server, Excel Services y KPI). Ambos eventos acabaron con preguntas a los ponentes acerca de los temas expuestos. Luego, para terminar los eventos, “unas tapitas” en el bar de la amiga Elvira, mientras su dueña conversaba con los ponentes y algunos asistentes. eventos.eventos.eventos.eventos.eventos desván Marino Posadas Más allá de Windows Vista <<dotNetManía Stephen Sinofski, Windows Senior VicePresident ha anticipado recientemente algunas novedades sobre el sistema operativo que seguirá a Vista. Inicialmente nombrado como BlackComb y posteriormente como Vienna, parece que estas nomenclaturas vuelven a un estado de pureza y se habla de Windows 7. Y se dice que no estará disponible hasta 2010 (aunque ha surgido últimamente alguna voz indicando la segunda mitad de 2009). Dispondrá de dos versiones de procesador: 32 y 64 bits. Mientras tanto, los grandes Service Pack para Vista serán Windows Vista Service Pack 1 (actualmente en beta pública) y un nuevo tipo de actualización que se denomina Microsoft Desktop Optimization Pack, que es más bien un mecanismo de optimización del sistema, configurable por el usuario. 58 Respecto al diseño y aspecto visual del sistema, el éxito obtenido en Office 2007 ha movido a Microsoft a reclutar a Julie Larsen-Green, una de las artífices principales del cambio de aspecto y modo operativo de la suite, para repensar –especialmente— “el modo en que a los usuarios les gustaría utilizar el sistema”. Caben esperar grandes cambios, a tenor de su labor para la actual versión de Office. Pero, ¿habrá grandes cambios en la arquitectura interna del sistema? Se dice que no. Que la calidad y el rendimiento serán el objetivo primordial. Pero también hay quien apunta ideas de primera mano salidas de la propia casa central (o eso afirman): todas las aplicaciones ejecutándose como no-Administrador, nueva estructura de drivers gráficos (para evitar las caídas por esta vía, ya que la queja por parte de Microsoft es que las compañías son muy proclives a cuidar al máximo el rendimiento de sus drivers gráficos, pero no su estabilidad), y la imposibilidad de que cualquier servicio del sistema tenga la facultad de crear una interfaz de usuario. Es llevar el concepto de ejecución en máquina virtual hasta las últimas consecuencias en aras de la seguridad. Otras características anunciadas son la posibilidad de afinar la instalación para que cada fabricante o similar pueda personalizar el sistema según sus necesidades (Component Delivery Platform), y StrongBox, mecanismo ideado para minimizar el impacto que algunas aplicaciones tienen sobre otras. De cara al usuario, Windows Live estará tan integrado con el sistema, que simplemente será como una extensión natural de éste. Y es que Redmond se ha dado cuenta de una gran verdad: si el usuario de una aplicación no se siente cómodo con ella, si su uso supone una pesadilla en lugar de un placer, o si tiene que leer largos manuales para empezar a ser productivo, de poco sirve que la infraestructura y arquitectura sean excelentes. Al usuario no le interesa el código fuente, le interesa su propia experiencia (de usuario). noticias.noticias.noticias documentos en la red Configuring Visual Studio to Debug .NET Framework Source Code. Es un excelente artículo sobre cómo podemos cambiar la configuración del IDE de forma que podamos ver cómo se ejecuta el propio código fuente de las librerías de .NET Framework. Algo más que una curiosidad, cuando encontremos algún bug que se resista. Ver el blog de Shawn Burke (http://blogs.msdn.com/sburke/archive/2008/01/16/configuring-visual-studio-to-debug-net-framework-source-code.aspx), no es menos recomendable. (Gracias a Daniel Seara por el enlace). Visual Studio 2008 Learning Guide. ¿Quiere ponerse al día en las novedades de VS 2008, y no tiene tiempo para una formación adecuada? Puede intentar descargar los documentos disponibles en WinDevelopment, bajo el epígrafe “Visual Studio 2008 Learning Guide” (http://searchwindevelopment.techtarget.com/generic/0,295582,sid8_gci1280711,00.html?track=NL-150&ad= 612250&asrc=EM_USC_2568652&uid=6264165). Comprendemos que no tenga muchas ganas de teclear todo ese enlace. Así que quizá prefiera buscarlo en http://searchwindevelopment.techtarget.com. sitios del mes El blog de David Salgado. Trepidante. En constante cambio, como los service packs. Aparte de buenos y profundos comentarios sobre una de sus especialidades (la depuración a bajo nivel), veremos noticias, trucos, anuncios de eventos de desarrollo, y hasta un diario-gadget muy “á-la-mode”. Disponible en http://blogs.msdn.com/davidsalgado. Developer Fusion. Un sitio excelente, ubicado en el Reino Unido, y con una cantidad ingente de información sobre desarrollo (no solo en .NET). Me gustaría recalcar un artículo sobre Silverlight, escrito por un buen amigo (Dave Wheeler, del equipo de desarrollo del SDK de NewsReaders, en Redmond). Con montones de código fuente y artículos sobre programación (http://www.developerfusion.co.uk). utilidades del mes ClipX. Para los que –muchas veces- hemos echado de menos utilidades extra asociadas al comportamiento del Portapapeles de Windows. Ocupa más o menos 100K, es gratis, y permite funcionalidades extendidas como hacer búsquedas en Internet sobre el contenido del Portapapeles, o comandos para abrir una sesión nueva del navegador con la URL contenida en él. Descargable de http://www.bluemars.org/clipx. Microsoft TechDays {The Evolution Show} Y el 26 y 27 de febrero en Madrid Palacio Municipal de Congresos (Campo de las Naciones) Porque tú eres nuestro verdadero protagonista, no puedes faltar. Ven a {The Evolution Show}, todo un acontecimiento y una experiencia inolvidable donde te presentaremos las nuevas versiones de Windows Server 2008, Visual Studio 2008 y SQL Server 2008. Premier técnica, demos, laboratorios, Steve Ballmer en directo desde Los Ángeles,... ¡No te lo puedes perder! Regístrate ya en www.microsoft.es/lanzamiento2008 Mucho más que una evolución. Una revolución… Con la colaboración de: