This document was ed by and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this report form. Report r6l17
Overview 4q3b3c
& View Visual Studio .net Framework 3.5 Para Profesionales - Maximiliano Firtman-librosvirtual.pdf as PDF for free.
A mi mujer y a mi familia, por apoyarme en cada nuevo desafío. Lic. Maximiliano Firtman
A mi familia, a mi novia, a mis amigos y a todos aquellos que, de alguna u otra forma, siempre me alentaron a seguir adelante. Leonardo Natale
Mensaje del editor Los conocimientos son esenciales en el desempeño profesional. Sin ellos es imposible lograr las habilidades para competir laboralmente. La universidad o las instituciones de formación para el trabajo ofrecen la oportunidad de adquirir conocimientos que serán aprovechados más adelante en beneficio propio y de la sociedad. El avance de la ciencia y de la técnica hace necesario actualizar continuamente esos conocimientos. Cuando se toma la decisión de embarcarse en una vida profesional, se adquiere un compromiso de por vida: Mantenerse al día en los conocimientos del área u oficio que se ha decidido desempeñar. Los libros de Alfaomega están diseñados para ser utilizados dentro de los procesos de enseñanza-aprendizaje, y pueden ser usados como textos guía en diversos cursos o como apoyo para reforzar el desarrollo profesional. Alfaomega espera contribuir así a la formación y el desarrollo de profesionales exitosos para beneficio de la sociedad.
6ISUAL3TUDIO &IRTMAN .ATALE
Alfaomega
Acerca de los autores Lic. Maximiliano Firtman Es licenciado en sistemas, docente y profesional de tecnologías web desde 1996. Es fundador y director del Instituto ITMaster Professional Training, donde también es profesor de tecnologías móviles y Web. En su carrera profesional dictó varias conferencias y publicó artículos en diversas revistas técnicas y medios especializados. Fue reconocido finalista en el premio Sadosky a la inteligencia argentina aplicada a la sociedad de la información y obtuvo el reconocimiento como uno de los más destacados desarrolladores móviles del mundo en el programa Forum Nokia Champion. Es autor de varios libros de desarrollo, entre ellos, AJAX Web 2.0 para profesionales, de esta misma editorial.
Leonardo Natale Desde el año 1998 trabajó en empresas relacionadas con las telecomunicaciones y la informática. Actualmente dicta cursos y charlas y escribe en diferentes medios sobre desarrollo Mobile y Web con tecnologías Microsoft. Es el fundador de Cobaires, consultora de software especializada en soluciones Mobile y Web.
Para seguir en o con los autores visite el sitio Web que ellos mismos desarrollaron para este libro www.librovisualstudio.com
Capítulo 8 Introducción a la programación para Windows Mobile Más de 20 minutos de video sobre estos cuatro puntos: 1- Familias de dispositivos. 2- Repaso por VS2008 y Emuladores. 3- Mi primera aplicación Mobile. 4- Datos locales, primera aproximación.
#ONTENIDO
Capítulo 9 Visual Studio 2010 y .NET 4.0 Video adelanto de las próximas versiones de las herramientas de desarrollo de Microsoft: 1- Cambios en la interfaz. 2- Repaso de novedades de .NET 4.0. 3- Lo nuevo en ASP.NET 4.0. 4- Las versiones disponibles.
Para ver los videos visite: http://www.alfaomega.com.mx/archivosadicionales
Alfaomega
6ISUAL3TUDIO &IRTMAN .ATALE
Introducción La vorágine tecnológica de los últimos años nos ha llevado a trabajar en una de las profesiones que más actualización requiere del ámbito laboral. Un arquitecto, un abogado, un contador e incluso un médico tienen una frecuencia de actualización profesional mucho menos estresante que la nuestra. La plataforma .NET, no ajena a la actualización de la tecnología, ha evolucionado –realmente a pasos agigantados– entre su lanzamiento en los primeros años del milenio y la versión 4.0, aún en preparación durante la edición de este libro. No sólo se actualiza de versión a versión, sino que aparecen versiones intermedias, actualizaciones aparentemente menores (como Service Packs) y frameworks paralelos, que luego forman parte de las versiones estables (como AJAX o MVC). Todo esto nos obligaría a mantenernos en constante alerta; pero, por otro lado, debemos reconocer que los desarrollos ya existentes no tienen tal frecuencia de actualización. Miles de sistemas desarrollados en la actualidad siguen funcionando bajo el Framework 1.0 o 2.0, sin que haya intenciones directas de migrar en el corto plazo mientras nosotros seguimos trabajando en ellos. Debido a las políticas internas de cada empresa, o del conocido espíritu de –“esperemos unos años a que salgan todos los parches y todo el mundo le encuentre los errores”, existen decenas de excusas que nos mantienen fuera del uso de las últimas actualizaciones. Pero si en algún momento no decidimos retomar las riendas nuevamente, corremos el riesgo de perder el tren y pasar a formar parte de esa masa de programadores a los cuales se los convoca cuando hay que modificar un sistema antiguo, sintiéndose casi analfabetos de la modernidad en una conversación con colegas. Es por este motivo que invitamos con este libro a todos aquellos programadores .NET –ya sean de VB o de C#– a subir nuevamente al tren y actualizarse a la última versión y a las futuras versiones, ya que pueden haber bajado en las estaciones de .NET 1.0, 2.0, 3.0 o 3.5 mientras podrían aprovechar todas las últimas características de los lenguajes y de las plataformas, que nos harán más eficiente y productiva nuestra tarea. Lic. Maximiliano Firtman Director ITMaster Professional Training
6ISUAL3TUDIO &IRTMAN .ATALE
Alfaomega
Antes de comenzar a leer: En este libro se utiliza la tipografía Courier en los casos en que se hace referencia a código o acciones que se han de realizar en la computadora, ya sea en un ejemplo o cuando se refiere a alguna función mencionada en el texto. También se usa para indicar menús de programas, teclas, URLs, grupos de noticias o direcciones de correos electrónicos.
Alfaomega
6ISUAL3TUDIO &IRTMAN .ATALE
Alfaomega e ITMaster Professional Training te dan la posibilidad de que certifiques tus conocimientos y experiencias adquiridos como lector de este libro, mediante una evaluación gratuita. Su aprobación te permitirá tener la certificación .NET Framework 3.5, C# 3.0, Visual Basic 2008 y ASP.NET 3.5. Luego de la obtención del certificado, podrás continuar tu formación en la carrera corta de especialización Experto en .NET y en los cursos cortos Optimización de Sitios Web, JavaScript y AJAX, Silverlight, Desarrollo para Windows Mobile, Desarrollo de portales móviles con .NET y Adobe Flex, para completar así tus conocimientos en tecnologías relacionadas con .NET y Visual Studio. La capacitación podrás realizarla en forma presencial en las sedes habilitadas o a distancia a través de Internet, con una capacitación online, en cualquier ciudad del mundo donde te encuentres. Para dar la evaluación de certificación ingresa en el sitio Web de este libro (www.librovisualstudio.com) o utiliza la dirección correspondiente a tu país para recibir mayor información. Para realizar cualquier consulta adicional, se detalla a continuación la dirección de correo electrónico y Web que corresponde a tu país:
Introducción Así como un arquitecto necesita herramientas para diseñar planos y un cirujano necesita instrumental quirúrgico para poder operar, quienes desarrollamos software necesitamos de herramientas para llevar a cabo nuestra tarea. ¿Nos imaginamos a un mecánico de automóviles intentando desarmar un motor con solamente un destornillador? Puede que después de mucho tiempo lo logre, pero habrá malgastado horas, el trabajo no será realizado de la mejor manera y hasta quizá dañe la pieza, dejando la posibilidad de que en un futuro aparezcan problemas de la nada. Todo por no disponer de los elementos necesarios. El mundo de la programación no escapa a lo mencionado en el párrafo anterior. Durante las primeras ediciones beta de Visual Studio 2008, se podían ver algunas de las características que muchos de los arquitectos y jefes del equipo responsables del producto comentaban en sus blogs. Lo mismo se ve en Visual Studio 2010. Uno de los motivos que impulsó la salida de Visual Studio 2008 fue, sin dudas, Windows Vista, cuya liberación presentaba también un nuevo framework –el .NET Framework 3.0– con importantes novedades tanto para el final como para nosotros. De manera similar Windows 7 impulsa a Visual Studio 2010. El tiempo corría y, para entonces, Visual Studio 2005 no tenía todo lo que necesitábamos de forma nativa para construir aplicaciones que utilizasen el potencial de ese framework. El lanzamiento de Visual Studio 2008, en conjunto con el Framework 3.5, implica un cambio importante para nosotros. De la mano de éstos tenemos nuevos lenguajes, más funcionalidad, mayor integración, mejoras de calidad, performance y ampliación del campo de acción. Visual Studio - Firtman, Natale
Alfaomega
2
1- Visual Studio
En este capítulo vamos a repasar las principales novedades, características y funcionalidades que fueron incorporadas. Sin dudas, el kit de desarrollo que estábamos esperando.
Versiones Microsoft nos provee de diversas interfaces de desarrollo, de acuerdo con las necesidades y complejidades que requiramos. Se diferencian por sus características y prestaciones, así como también por el soporte técnico y el precio de cada una de ellas. Disponemos de versiones realmente muy livianas –que nos brindan todo lo rico de los nuevos lenguajes y del .NET Framework– y de entornos de desarrollo potentes para grandes equipos de programadores, donde la construcción de aplicaciones críticas requieren de herramientas especializadas y, por sobre todo, de colaboración.
Ediciones Express Son versiones gratuitas y livianas, que nos permiten desarrollar aplicaciones específicas basadas en el .NET Framework, en función del producto que decidamos instalar. Si nunca hemos utilizado Visual Studio, son un buen punto de partida para comenzar a interiorizarnos en cuestiones de usabilidad, programación y distribución de componentes. Las ediciones express, pueden descargarse de manera gratuita desde el sitio www.microsoft.com/express, en el cual tendremos también la posibilidad de bajar e instalar SQL Server Express, el motor de base de datos también gratuito de Microsoft. Al ser productos reducidos, son ideales para aquellos que comienzan a adentrarse en el mundo de desarrollo de Microsoft, ya sea para conocer el producto en sí, la tecnología .NET o alguna de sus plataformas. Si descargamos estas versiones desde el sitio oficial, nos garantizaremos tener siempre la última edición, incluyendo Services Packs. Tendremos la posibilidad de descargar una aplicación que instala cada una de las versiones, o bien, un archivo imagen de DVD, con extensión .iso, que contiene todos los productos express para una mayor comodidad. Una vez instalados, tendremos treinta días para registrar el producto. La registración es gratuita y sólo requiere de conexión a Internet y una cuenta de Windows Live (como por ejemplo, una cuenta de correo de Hotmail). Las mayores limitaciones de estas versiones se relacionan con el hecho de que no están pensadas para trabajar con soluciones ni con dos proyectos al mis-
Alfaomega
Visual Studio - Firtman, Natale
Versiones
3
mo tiempo en el mismo entorno de trabajo; no tienen capacidades de trabajo en equipo, no permiten desarrollar aplicaciones móviles para Windows Mobile y no tienen para istrar bases de datos remotas. Visual Basic Express Es un entorno de desarrollo para aquellos que utilizan como lenguaje principal VB.NET para desarrollar aplicaciones Windows. Incluye asistencia para la escritura de código (IntelliSense), soporte para LINQ, Windows Presentation Foundation y Entity Framework, temas que iremos revisando a lo largo del libro. Podremos utilizar como base de datos tanto SQL Server Express como su versión compacta SQL Server Compact. Con esta versión no se pueden construir soluciones para ASP.NET o dispositivos móviles. Visual C# Express Similar a Visual Basic Express, pero orientado a desarrolladores C#. Brinda todas las características y mejoras del nuevo lenguaje C# 3.0. Visual C++ Express Orientado a desarrolladores C++. Permite lograr interfaces para crear aplicaciones nativas Win32, aprovechar todas las características del SDK de Windows Platform y lograr integración directa con el kit de desarrollo para juegos 2D y 3D DarkGDK (http://gdk.thegamecreators.com).
Fig. 1-1. Productos gratuitos para desarrollar aplicaciones.
Visual Studio - Firtman, Natale
Alfaomega
4
1- Visual Studio
Visual Web Developer Express Herramienta para desarrollar sitios Web utilizando C# o VB.NET. Soporte para istrar estilos y propiedades CSS, ASP.NET AJAX, AJAX Control Toolkit, Windows Communication Foundation y Silverlight 2. Incluye el motor de dibujado y edición Web del paquete Expression Web. Podremos descargar los Starter Kits (http://www.asp.net/community/projects), que nos permitirán generar muy rápidamente sitios Web. Ideal para investigar cómo funcionan, dado que tendremos el código fuente de cada uno de ellos, o crear nuestras propias páginas Web personalizadas.
Edición Estándar A diferencia de las versiones express, esta edición es comercial y está orientada a un desarrollador que necesite, en un mismo aplicativo, diversos escenarios de trabajo. Suple la mayoría de los requerimientos, tanto en proyectos Windows como Web, independientemente del lenguaje que utilice cada programador. Es ideal para quienes estén por dar el paso siguiente al mundo del desarrollo de sistemas Microsoft e ir madurando sus conocimientos junto a la tecnología. Veamos las principales características desde la versión 2008: s
Creación de proyectos: – –
Formularios Windows. Web.
s
Soporte para múltiples versiones de .NET Framework (desde 2.0)
s
ClickOnce.
s
Editor XML, XSL y XSLT.
s
Múltiples lenguajes de programación (C#, VB.NET, C++, J++) y posibilidad de instalarle más.
s
Compatibilidad y herramientas para: – – –
Windows Communication Foundation (WCF). Windows Workflow Foundation (WWF). Windows Presentation Foundation (WPF).
s
LINQ.
s
Diseñador relacional de objetos.
s
Creación de complementos y agregados para Visual Studio.
Alfaomega
Visual Studio - Firtman, Natale
Edición Profesional
s
Compatibilidad con AJAX.
s
Instalación de SQL Server Express.
s
Servicios de reportes de SQL.
5
Edición Profesional Es una versión diseñada para programadores profesionales que requieran herramientas adicionales para poder construir y probar sus aplicaciones. Incluye todas las características de la edición estándar, más algunas otras que harán de esta versión el aplicativo que muchos elijan para abarcar un espectro importante de las demandas que actualmente tienen las empresas. Las funcionalidades agregadas son: s
Soporte para desarrollo de aplicaciones móviles.
s
Creación de soluciones para el paquete Office.
s
Diseñador de clases.
s
Herramienta de pruebas de objetos.
s
Compatibilidad con Crystal Reports.
s
Pruebas unitarias.
s
Incluye SQL Server Developer Edition.
s
Posibilidad de adquirir el producto en conjunto con alguna de las suscripciones del MSDN disponibles.
Los requerimientos mínimos necesarios para instalar la versión estándar o profesional son similares: s
Procesador 1,6 Ghz o superior.
s
Windows Vista o 7 (excepto la edición Starter), Windows XP con Service Pack 2, Windows 2003 Server con Service Pack 1 o Windows 2008 Server.
s
2,5 GB de espacio libre en disco.
s
Memoria:
s
–
768 MB para Vista, Windows 7 o Windows 2008 Server.
–
384 MB para Windows XP o Windows 2003 Server.
Monitor de resolución 1024 x 768 o superior.
Visual Studio - Firtman, Natale
Alfaomega
6
1- Visual Studio
En la tabla 1-1 tenemos una comparación entre las versiones estándar y profesional basada en las principales características: Característica
Edición Estándar
Edición Profesional
Soporte creación formularios Windows
9
9
Soporte creación formularios Web
9
9
Creación aplicaciones para MS Office
2
9
Creación aplicaciones para dispositivos móviles
2
9
ASP.NET AJAX, WWF, WCF, WPF, LINQ
9
9
Explorador de servidores Generación de pruebas unitarias
2 2
9 9
Bases de datos incluidas
SQL Server Express
SQL Server Developer
ClickOnce
9
9
Tabla 1-1. Comparación entre VS Estándar y VS Profesional.
Edición Team System Es el producto de mayor envergadura de la familia de Visual Studio 2008. Está orientado a grandes equipos de desarrollo que requieren herramientas potentes, donde cada programador realiza tareas específicas a lo largo del ciclo de vida de un proyecto. Esta edición permite orquestar cada uno de los eslabones para mejorar la calidad y productividad de nuestro equipo, brindando grandes capacidades de colaboración y comunicación entre los grupos de programadores que intervienen en la construcción de soluciones de software. Brinda una visión sobre el estado de cada hito, permitiéndonos tomar las mejores decisiones para lograr nuestras metas. Posee integración con Microsoft Project para istrar todos los proyectos y disponer así de documentación vital para reasignar tareas, modificar tiempos y analizar el impacto sobre cada cambio que realicemos. Sin dudas, es una IDE que une a desarrolladores, líderes de proyectos, equipos de testeo y al personal involucrado en el éxito del producto que se ha de desarrollar. El producto hace hincapié sobre cuatro pilares principales: Alfaomega
Visual Studio - Firtman, Natale
Edición Team System
s
Arquitectura.
s
Base de datos.
s
Desarrollo.
s
Pruebas.
7
La versión que integra todos ellos es Visual Studio Team System Team Suite, aunque también disponemos de cuatro ediciones, con objetivos específicos, que pueden ser instaladas por separado según nuestra necesidad y el rol de cada equipo: Edición para Arquitectos Pensado para los arquitectos de software que necesiten diseñar la estructura de una solución, basándose en buenas normas de programación. Posee herramientas de validación para sistemas distribuidos e interesantes diseñadores visuales que ayudarán a reducir los tiempos, verificando diversos escenarios antes de llevarlos a producción. Edición Base de Datos Provee de herramientas y funcionalidades para gestionar cambios y pruebas sobre la base de datos. Permite a los desarrolladores involucrados en la manipulación de los datos comprender el impacto que puede llegar a tener un cambio sobre el modelo. Edición Desarrollo Brinda todo lo necesario para el equipo de desarrollo. Puede detectar código no seguro o que pueda ser mejorado basado en buenas prácticas. Herramientas como análisis, medición y cobertura de código permitirán escribir líneas de la mejor manera posible. Edición para Pruebas Los responsables de realizar pruebas para velar por el buen funcionamiento dispondrán de excelentes herramientas para realizar esta tarea. Provee funcionalidades de simulación de carga y análisis de performance, entre otras. En torno a estos productos se encuentra el Team Fundation Server, un servidor que nuclea a todos ellos, brindando colaboración entre los roles, control de versionamiento, reportes, documentación y istración de plantillas para definir procesos de desarrollo. Para aquellos que estén interesados en probar el producto, pueden descargarse una imagen de máquina virtual donde tendrán todo listo para comenzar a utilizarlo sin modificar la configuración de sus equipos.
Visual Studio - Firtman, Natale
Alfaomega
8
1- Visual Studio
Cambios desde Visual Studio 2005 Múltiples plataformas Quizá, uno de los cambios más importantes que vamos a notar, si migramos desde alguna versión de Visual Studio 2005 a una superior, es la posibilidad de trabajar con varias versiones del framework (por ejemplo .NET Framework 2.0, 3.0 y 3.5) en el mismo entorno. Esto nos habilita a poder crear y mantener proyectos que se encuentren desarrollados bajo versiones anteriores al .NET Framework 3.5, sin tener que utilizar diferentes versiones de Visual Studio para cada escenario. Es interesante destacar que a partir de Visual Studio 2008, el entorno sólo nos mostrará los componentes y controles que existan acorde con la versión seleccionada de framework, actualizando la barra de herramientas, el IntelliSense y las referencias a los ensamblados. En la figura 1-2 vemos cómo podemos seleccionar la versión de .NET Framework con la cual vamos a trabajar.
Fig. 1-2. Seleccionando la versión del .NET Framework que vamos a utilizar.
Alfaomega
Visual Studio - Firtman, Natale
Cambios desde Visual Studio 2005
9
Si quisiéramos, podríamos cambiar, dentro de las propiedades del proyecto, la versión original del .NET Framework por otra y si alguna característica no está soportada en la opción seleccionada, Visual Studio resolverá por nosotros esta situación –por ejemplo, quitando las referencias que no se corresponden con la versión que se ha de compilar–. La figura 1-3 muestra cómo realizar esto en un proyecto VB.
Fig. 1-3. Cambiando la versión del .NET Framework en un proyecto VB.
Múltiples proyectos Tal como mencionábamos al principio del capítulo, cuando apareció Windows Vista se liberaba también la versión 3.0 del .NET Framework, con importantes novedades como Windows Presentation Foundation y Windows Communication Foundation, entre otras. En aquella época sólo teníamos Visual Studio 2005 y para poder crear proyectos que utilizasen AJAX, WCF o WPF debíamos instalar extensiones al entorno y así generar soluciones con ellos. Desde la versión 2008, Visual Studio ya trae integrado todo esto y mucho más de forma nativa. Su capacidad de integración con Microsoft Expression Blend y Expression Web nos va a permitir crear soluciones mucho más ricas y potentes. Desarrollar flujos de trabajos o complementos para Office 2003 y 2007 Visual Studio - Firtman, Natale
Alfaomega
10
1- Visual Studio
son algunas de las tantas opciones que disponemos sin instalar adicionales. La figura 1-4 nos muestra algunos de los proyectos que podemos generar desde Visual Studio 2008 sin haber instalado adicionales.
Fig. 1-4. Plantilla de Visual Studio 2008 para WPF.
Nuevas características visuales Repasemos algunos cambios visuales y algunas mejoras que se produjeron desde Visual Studio 2008.
Una misma fuente para todo Una nueva opción ha sigo agregada para cambiar la fuente del entorno de nuestro IDE sin realizar múltiples modificaciones, como lo era en Visual Studio 2005. Dentro de las opciones del menú Herramientas, podremos seleccionar el ítem Fuente del entorno para luego elegir un formato de fuente que será aplicado a todas las ventanas de Visual Studio (figura 1-5). Alfaomega
Visual Studio - Firtman, Natale
Nuevas características visuales
11
Fig. 1-5. Cambiando la fuente para el entorno de Visual Studio 2008.
Navegador de ventanas y archivos Tenemos una vista preliminar de la opción que estemos seleccionando. Por defecto, el navegador de ventanas y archivos puede ser accedido de dos formas: s
Combinado Ctrl + Tab: Podremos navegar a través de los archivos que tengamos abiertos.
s
Presionando Alt + F7: Nos permite navegar a través de las ventanas activas.
En cualquiera de las alternativas descriptas, podremos ver una ventana reducida del contenido de cada una de las ventanas activas o de los archivos activos. En la figura 1-6 vemos esta nueva característica del navegador de Visual Studio.
Accediendo a los foros Desde el menú Ayuda, una nueva forma de acceder a los foros de discusión es mediante la opción Foros de MSDN, tal como lo muestra la figura 1-7. Es un espacio de colaboración y de intercambio de conocimientos, en el cual los s pueden realizar consultas así como también responder las de otros programadores. Está totalmente en español y se encuentra dividido por temas. Visual Studio - Firtman, Natale
Alfaomega
12
1- Visual Studio
Fig. 1-6. Vista preliminar del navegador de ventanas y archivos.
Fig. 1-7. Accediendo a los Foros del MSDN en español.
Alfaomega
Visual Studio - Firtman, Natale
Nuevas características visuales
13
Plantillas ordenadas Cuando agreguemos un nuevo elemento a nuestro proyecto, una nueva ventana modal (figura 1-8) aparecerá mostrándonos las plantillas disponibles ordenadas por categorías, una característica que nos permitirá hacer la selección más rápida y ordenada.
Fig. 1-8. Plantillas agrupadas por categorías.
Explorador de Objetos Debido al soporte de múltiples versiones de .NET Framework, el explorador de objetos tiene la posibilidad de filtrar por ellos, mostrando solamente los componentes que se encuentran en el .NET Framework seleccionado (figura 1-9).
IntelliSense Un agregado interesante, es la posibilidad de hacer transparente la ventana del IntelliSense, dado que muchas veces nos tapa las líneas que se encuentran detrás de él. Esto se logra presionando la tecla Ctrl. Veamos su comportamiento mientras escribimos una consulta LINQ (figura 1-10).
Visual Studio - Firtman, Natale
Alfaomega
14
1- Visual Studio
Fig. 1-9. Filtrando por versión del .NET Framework en el explorador de objetos.
Fig. 1-10. La ventana de IntelliSense se puede volver transparente.
Ventanas estándar Quienes utilicen Windows Vista o Windows 7 notarán que se han cambiado las ventanas de diálogo por un formato más amigable e intuitivo, que nos permitirá navegar por la estructura de directorio de manera mucho más fácil. Se encuentra disponible en diversas operaciones como abrir archivos, guardar documentos, agregar elementos existentes, etc. En la figura 1-11 podemos apreciar esta característica. Alfaomega
Visual Studio - Firtman, Natale
Nuevas características visuales
15
Fig. 1-11. Ventana estándar con mejoras de navegación.
Explorando carpetas Muchas veces hemos tenido la necesidad de abrir la carpeta donde se encontraba nuestra solución o un proyecto mientras estábamos dentro de la interface de desarrollo; para ello, nos fijábamos en las propiedades de los mismos y copiábamos y pegábamos la ruta en un explorador de archivos. Pues bien, ahora si nos posicionamos sobre el proyecto o sobre la solución, haciendo clic en el botón derecho, tendremos la opción de abrir la carpeta directamente en el explorador de archivos (figura 1-12).
Visual Studio - Firtman, Natale
Fig. 1-12. Abriendo la carpeta de origen de una solución o de un proyecto desde Visual Studio.
Alfaomega
16
1- Visual Studio
Nuevas características de código Nuevos lenguajes De la mano del .NET Framework 3.5, dos nuevas versiones de los lenguajes C# y VB.NET nos permitirán incrementar nuestra productividad mediante nuevas características que fueron incorporadas en C# 3.0 y Visual Basic 9. Las principales novedades incorporadas son: s
Tipos implícitos: Escribir nuestras variables sin definir explícitamente su tipo, permitiéndole al compilador inferirlo por nosotros.
s
Métodos extensores: Agregar funcionalidad a tipos del .NET Framework, o definidos por nosotros, sin modificar el código original.
s
Expresiones lambda: Funciones anónimas que pueden contener expresiones o declaraciones que podremos utilizar con delegados.
s
Inicializadores de objetos: Crear objetos y poder asignarle valores a sus propiedades en una misma línea de código.
s
Tipos anónimos: Crear objetos que no poseen una clase que los represente.
s
Expresiones de consulta: Forma de escribir consultas cuya sintaxis es similar a T-SQL.
En los capítulos 3 y 4 veremos en detalle cada una de ellas, con ejemplos para su mejor comprensión. Cada una de estas nuevas características podrán ser utilizadas en nuestros proyectos Web, formularios Windows y aplicaciones móviles, sin ningún tipo de problema. Y en conjunto darán el nuevo soporte a LINQ. LINQ es una nueva forma de escribir consultas dentro de nuestro código a diferentes orígenes de datos, como bases de datos SQL, XML o colecciones de objetos en memoria. Este tema será explicado con detenimiento en el capítulo 5. Mejorando nuestro código Los programadores C# estamos acostumbrados a utilizar todo el tiempo el refactorizador, dado su gran potencial en este lenguaje. No así aquellos que programan en Visual Basic, que no poseen las funcionalidades que desearían. Pero hay una buena noticia: Microsoft, en un acuerdo con la empresa DevExpress, convino en brindar a los desarrolladores VB.NET un plug-in totalmente gratis para Visual Studio 2008 –llamado Refactor!– que brindará funcionalidades de refactorización.
Alfaomega
Visual Studio - Firtman, Natale
Nuevas características de código
17
Para descargar el refactorizador deberá dirigirse a la página Web Devexpress.com (http://www.devexpress.com/Products/Visual_Studio_Add-in/VBRefactor/) (fig. 1-13) e instalar el ejecutable de aproximadamente 34 MB.
Fig. 1-13. Sitio Web de DevExpress que posee interesantes componentes para Visual Studio.
Otra alternativa interesante para los desarrolladores es ReSharper, de la empresa JetBrains (www.jetbrains.com). Este plug-in posee soporte tanto para Visual Basic como para C#. No es gratuito, pero ofrece grandes funcionalidades a la hora de programar. Su última versión brinda algunas de las siguientes características: s
Soporte para XAML y XML.
s
Scripts de NAnt y MSBuild.
s
Análisis de código para C#.
s
Plantillas de código.
s
Pruebas unitarias.
Visual Studio - Firtman, Natale
Alfaomega
18
1- Visual Studio
Fig. 1-14. Sitio Web de ReSharper.
JavaScript IntelliSense Una herramienta fundamental en el día a día de la programación es el uso del IntelliSense para ayudarnos a escribir más rápido y para evitar errores de sintaxis. Visual Studio, desde la versión 2008, posee soporte para JavaScript, ya sea cuando estemos dentro de un archivo .aspx o si codificamos nuestras líneas en un archivo externo. La figura 1-15 nos muestra cómo el asistente nos guía al codificar una función. Depuración En ciertas ocasiones nos encontramos con la necesidad de tener que depurar código JavaScript en alguna de nuestras páginas Web, ya sea para resolver algún error o simplemente para recorrer paso a paso nuestras líneas y monitorear el valor que adoptaban ciertas variables; pero Visual Studio carecía de esa funcionalidad y debíamos resolverla mediante otros métodos. Alfaomega
Visual Studio - Firtman, Natale
Nuevas características para Windows Forms
19
Visual Studio 2008 incorpora la funcionalidad de depurar bloques JavaScript totalmente integrada al entorno.
Fig. 1-15. IntelliSense en JavaScript.
Nuevas características para Windows Forms .NET Framework Client Profile Es un subconjunto del .NET Framework 3.5 Service Pack 1 (SP1), que contiene todos los ensamblados necesarios para que podamos diseñar aplicaciones clientes que no requieran de características avanzadas del .NET Framework. Este pequeño framework, de aproximadamente 26 MB, nos proporciona funcionalidades básicas en formularios Windows, WPF y WCF. La idea original fue armar un empaquetado optimizado de menor tamaño que el .NET Framework 3.5, para que nuestras aplicaciones clientes (Web o Windows) sean instaladas en equipos que no posean componentes del .NET Framework. En la figura 1-16 veremos una nueva opción llamada Subconjunto de .NET Framework de solo cliente, agregada desde el Service Pack 1 de .NET Framework 3.5, que nos permite seleccionar si queremos utilizar o no el .NET Framework Client Profile. Si tildamos esta opción, Visual Studio corroborará que las referencias de nuestro proyecto estén dentro de este pequeño empaquetado, caso contrario, nos advertirá de ello. De esta manera, estaremos generando una distribución más liviana y una instalación mucho más rápida. Es importante tener en cuenta que el foco del mismo es para aplicaciones clientes; es por ello que no posee ensamblados para entornos de servidor. Visual Studio - Firtman, Natale
Alfaomega
20
1- Visual Studio
Fig. 1-16. Nueva opción en las propiedades del proyecto de nuestra aplicación cliente.
ClickOnce Todo lo relacionado con la distribución e instalación de aplicaciones se ha mejorado, al igual que la incorporación de características que mejoran este proceso. A partir de la versión Service Pack 1 de Visual Studio 2008, una nueva ventana de opciones (figura 1-17) nos habilitará para configurar, entre otras cosas, el Nombre del conjunto de aplicaciones, que creará una carpeta en el menú inicio sobre la cual accederemos mediante el nombre definido y, a través de URL del error, definiremos una página Web que será invocada cuando ocurra algún error en la instalación. Lo interesante es que a dicha página se le pasará como parámetro una variable (por QueryString) que contiene el error generado. Para mejorar ciertos inconvenientes en esquemas de distribución de paquetes de instalación, podremos evitar tener que definir el deploymentProvider (la ruta de actualización) de los manifiestos de nuestra aplicación, permitiéndonos implementarla desde diversos sitios de red sin la necesidad de firmarla nuevamente. Alfaomega
Visual Studio - Firtman, Natale
Nuevas características para Windows Forms
21
Fig. 1-17. Nueva ventana de opciones para ClickOnce.
Mediante la exclusión de deploymentProvider, solucionaremos el problema de que terceros puedan implementar el aplicativo desarrollado y firmado por nosotros en sus propias redes. Otras novedades interesantes: s
Soporte del .NET Framework Client Profile como ítem de prerrequisitos.
s
Mejoras en la integración con el navegador Firefox (no tendremos que instalar plug-in de terceros como CoHelper para distribuir con ClickOnce).
s
istración de asociación de archivos a nuestro aplicativo mediante interface gráfica.
s
Soporte para la distribución de aplicaciones Web de WPF.
s
En conjunto con el Instalador de Microsoft, forma una gran solución de despliegue, combinando lo mejor de cada uno de ellos.
Windows Presentation Foundation Al igual que otras funcionalidades existentes antes del lanzamiento de Visual Studio 2008, con respecto a Windows Presentation Foundation vamos a encontrar algunas novedades en la nueva IDE.
Visual Studio - Firtman, Natale
Alfaomega
22
1- Visual Studio
El soporte nativo de WPF 3.5, nuevos diseñadores e importantes mejoras en cuanto a la escritura del código hacen de Visual Studio una herramienta ideal para construir aplicaciones ricas. Hay varias características para mencionar aquí, pero serán desarrolladas con detenimiento en el capítulo 2.
Servicios de aplicación cliente Esta nueva característica, permitirá que aplicaciones de formularios Windows o WPF, puedan consumir servicios de autenticación de s, roles y perfiles expuestos desde ASP.NET. Todo esto lo podremos hacer en forma gráfica; para ello, se ha agregado una nueva solapa (figura 1-18) en las propiedades del proyecto de formularios Windows o WPF, denominada Servicios, mediante la cual configuraremos todo lo necesario para hacer uso de los mismos.
Fig. 1-18. Nueva solapa para utilizar servicios de ASP.NET.
Alfaomega
Visual Studio - Firtman, Natale
Nuevas características para ASP.NET
23
Al habilitar el uso de esta funcionalidad, Visual Studio automáticamente agrega un archivo app.config (el cual alberga información sobre los proveedores que empleará nuestro aplicativo) y las referencias necesarias para consumir los servicios remotos, para luego escribir nuestras líneas de validación de , consultar por roles y manipular las propiedades del perfil en cuestión. Algo para destacar es que disponemos también de la posibilidad de trabajar en forma desconectada, ya sea albergando información en el caché local de la computadora o bien en una base de datos.
Nuevas características para ASP.NET Proyectos Web Un cambio que se produjo desde Visual Studio 2008 SP1 es la vuelta del proyecto de tipo aplicación Web, un modelo de proyecto similar al que teníamos en Visual Studio 2003 y que había sido reemplazado en Visual Studio 2005. De esta manera, ahora podremos elegir trabajar con una carpeta (sitio Web) o una solución ASP. NET conformada por varios proyectos, generando un ensamblado por cada uno de ellos (dentro de la carpeta Bin por defecto). Así, podremos definirles ciertos atributos como nombre, descripción, nombre de la compañía y versión, entre otros. Una forma mucho más ordenada y escalable de trabajar en aplicaciones ASP.NET. En la figura 1-19 vemos una solución con varios proyectos de aplicaciones Web.
Visual Studio - Firtman, Natale
Fig. 1-19. Generación de ensamblados en proyectos de aplicaciones Web.
Alfaomega
24
1- Visual Studio
Múltiples plataformas Al igual que en otros tipos de proyectos dentro de Visual Studio, podremos seleccionar alguna de las versiones del .NET Framework cuando trabajamos con ASP.NET. Si abrimos algún proyecto existente generado con alguna versión anterior a Visual Studio 2008, automáticamente un asistente nos propondrá migrar a 3.5, para aprovechar todo lo rico de este nuevo framework. Las plataformas disponibles en la versión 2008 son: .NET Framework 2.0. .NET Framework 3.0. .NET Framework 3.5. Si estuviésemos programando con Visual Web Developer Express, no tendríamos, en principio, la posibilidad de elegir la plataforma, debido a que, por defecto, nos creará proyectos para .NET Framework 3.5. De todas formas, luego de abrir el proyecto podremos cambiar la versión desde las propiedades a .NET Framework 2.0 ó 3.0
Nuevos controles Más adelante nos dedicaremos a las novedades de ASP.NET 3.5. A modo de resumen, comenzaremos a nombrarlas para luego tratarlas con detenimiento. ListView Es un control que nos permitirá trabajar con fuentes de datos como SqlDataSource, LinqDataSource, XmlDataSource o cualquier objeto que implemente la interface IEnumerable. Pero, a diferencia de otros controles existentes –tales como el GridView–, la representación de la información se realizará sobre plantillas personalizables definidas por nosotros. DataPager Es un control que nos provee de funcionalidad para realizar paginación cuando tenemos como resultado varios ítems. Generalmente, se lo utiliza con ListView, aunque también se puede emplear en conjunto con controles que implementen la interface IPageableItemContainer.
Alfaomega
Visual Studio - Firtman, Natale
Nuevas características para ASP.NET
25
Fig. 1-20. Muestra de datos utilizando ListView.
Fig. 1-21. Paginando el resultado de nuestra página Web. Visual Studio - Firtman, Natale
Alfaomega
26
1- Visual Studio
DataPager nos provee, además, de la posibilidad de mostrar los números de las páginas para navegar a través de ellas, así como también definir una plantilla personalizada que será empleada para poder paginar. LinqDataSource Se trata de un control para utilizar LINQ en nuestras páginas Web y manejar datos provenientes de una colección en memoria. Esta nueva técnica será tratada con más detenimiento en el capítulo 5, donde se analiza en detalle LINQ. En la figura 1-22 vemos, en la barra de herramientas, los controles que hemos visto aquí.
Fig. 1-22. Nuevos controles en Visual Studio 2008.
Diseñador HTML En el nuevo diseñador de Visual Studio vamos a encontrar algunas mejoras de importancia, como por ejemplo la posibilidad de trabajar con una nueva vista donde, al mismo tiempo, podremos ver la ventana de diseño como la de código HTML, permitiendo que cualquier cambio que se realice en alguna de ellas sea reflejado en la otra. En la figura 1-23 vemos esta nueva opción. Aquellos que utilicen Expression Web se van a sentir totalmente familiarizados con este nuevo entorno, dado que se utiliza el mismo motor.
Alfaomega
Visual Studio - Firtman, Natale
Nuevas características para ASP.NET
27
Fig. 1-23. Nueva vista del diseñador Web.
Para mejorar aún más la experiencia del mientras trabaja en proyectos Web, fueron incorporadas herramientas de regla y cuadrícula, a las cuales se puede acceder desde el menú Ver. Éstas nos permitirán distribuir nuestros componentes sobre la superficie del diseñador, mejorando el look and feel de la página Web.
Trabajando con hojas de estilo Otra mejora sustancial realizada es la incorporación de un nuevo motor de istración de las hojas de estilo CSS de nuestras páginas Web. Disponemos de tres nuevas herramientas a las cuales se puede acceder desde el menú Ver, tal como muestra la figura 1-24.
Visual Studio - Firtman, Natale
Alfaomega
28
1- Visual Studio
Fig. 1-24. Nuevas herramientas para trabajar con CSS.
istrar estilos Mediante esta herramienta (figura 1-25) vamos a poder trabajar con todos los estilos que se encuentren definidos en nuestra página, como así también con aquellos que se encuentren en un archivo externo. Si lo deseamos, tenemos la posibilidad de modificar algún estilo, simplemente seleccionándolo, y una ventana nos habilitará a realizar todos los cambios que necesitemos, tal como muestra la figura 1-26. Este nos permite observar un rápido resumen de todos los estilos que estemos utilizando actualmente, con la posibilidad de ver sobre el diseñador aquellos elementos que tengan un estilo en particular. Notemos que algunos estilos son representados mediante puntos de diferentes colores y algunos hasta tienen un círculo gris. Esta codificación significa: s
Un punto verde: Estilos basados en clase.
s
Un punto azul: Estilos basados en elementos.
s
Un punto amarillo: Estilos definidos en línea.
s
Un punto rojo: Estilos basados en identificadores (ID).
s
Cuando tienen un círculo gris: Están siendo utilizados actualmente.
Alfaomega
Visual Studio - Firtman, Natale
Nuevas características para ASP.NET
29
Fig. 1-25. de estilos.
Fig. 1-26. Cambiando las propiedades de un estilo. Visual Studio - Firtman, Natale
Alfaomega
30
1- Visual Studio
Propiedades de CSS Es una herramienta ideal para tener un resumen de las reglas aplicadas y todas sus propiedades (figura 1-27), sobre las cuales podremos realizar modificaciones directamente seleccionando algún elemento HTML o un componente ASP.NET. Algo interesante que debemos destacar es que podemos observar si una regla sobreescribe a otra, situación que es representada con una línea roja sobre la propiedad que es reemplazada.
Fig. 1-27. Visualizando las propiedades de estilo de un elemento HTML.
Aplicar estilos Será utilizada para aplicar estilos a elementos o componentes desde cualquiera de las vistas disponibles. Esta herramienta nos permitirá crear, modificar y duplicar estilos, todo desde una interface gráfica, sin necesidad de escribir código.
AJAX Como mencionábamos líneas más arriba, desde Visual Studio 2008 no es necesario instalar ninguna extensión para poder crear aplicaciones Web que utilicen el Alfaomega
Visual Studio - Firtman, Natale
Nuevas características para ASP.NET
31
Fig. 1-28. Aplicando estilos.
framework de AJAX; por defecto ya viene integrada en la interface y la veremos con detenimiento más adelante. En la barra de herramientas tenemos todo para agregar AJAX a una solución en blanco o a alguna ya existente. También disponemos de plantillas (figura 1-30) para generar controles de servidor y controles extensores de AJAX, para extender nuestras funcionalidades y utilizarlos en nuestros proyectos. AJAX Control Toolkit Es un paquete de controles AJAX ya desarrolla- Fig. 1-29. Barra de herramientas con componentes de AJAX lisdos y listos para usar, que nos permitirán hacer tos para usar. nuestros sitios Web mucho más ricos. Para descargarlos hay que acceder a la página Web de CodePlex (www.codeplex.com/ajaxcontroltoolkit) e instalar alguna de sus variantes, dado que podremos seleccionar si queremos, además, los fuentes o simplemente los ensamblados para integrarlos en Visual Studio. Visual Studio - Firtman, Natale
Alfaomega
32
1- Visual Studio
Fig. 1-30. Plantillas existentes para crear controles AJAX.
Fig. 1-31. Sitio de descarga del AJAX Control Toolkit.
Alfaomega
Visual Studio - Firtman, Natale
Nuevas características Mobile
33
Luego de haber instalado el AJAX Control Toolkit, dispondremos, en la barra de herramientas, de una gran cantidad de controles, como los que se representan en la figura 1-32.
Nuevas características Mobile El desarrollo de aplicaciones móviles está creciendo vertiginosamente; nuevos equipos dotados de diversas capacidades salen al mercado casi como en una carrera tecnológica, requiriendo de funcionalidades cada vez más parecidas a las de computadoras de escritorios. Para poder lograrlo, necesitamos de herramientas de diseño y construcción potentes y versátiles. Visual Studio no es la excepción y se convierte en una interface de desarrollo por excelencia, aportando importantes cambios y agregados para la materia. Repasemos las principales características. Para poder construir proyectos para dispositivos móviles, tendremos que tener Visual Studio versión profesional o superior, dado que ni las versiones Express ni la estándar soportan la generación de este tipo de proyectos.
Fig. 1-32. Controles AJAX listos para ser utilizados en nuestras páginas ASP.NET.
.NET Compact Framework 3.5 Esta versión del framework para dispositivos móviles viene integrada desde Visual Studio 2008, de la mano de importantes novedades como Windows Communication Foundation y LINQ. Básicamente, es una extensión de su antecesor, la versión 2.0, con mejoras en performance, usabilidad y proyección. .NET Compact Framework 3.5 nos provee de un soporte limitado para WCF, donde sólo podremos consumir servicios, no albergarlos localmente. En la actualidad, la mensajería está restringida a operar sobre dos opciones de transporte, mediante HTTP o vía correo electrónico. En la figura 1-33 vemos el modelo en capas de WCF para .NET Compact Framework 3.5.
Visual Studio - Firtman, Natale
Alfaomega
34
1- Visual Studio
Capa del motor en tiempo de ejecución del servicio (no itida) Capa de mensajería Interfaz IChannel Protocolos WS-Security WS-Addressing Codificador Condificador de texto Transporte
Transporte HTTP Transporte de correo electrónico
Fig. 1-33. Modelo de capas para WCF en dispositivos móviles.
Para poder utilizar estos servicios, será necesario emplear la herramienta NetCFSvcUtil.exe, que se encuentra dentro de los Power Toys, para la creación de la clase proxy que será utilizada en nuestro código. .NET Compact Framework 3.5, junto con las nuevas características de los lenguajes C# 3.0 y VB.NET 9.0, nos brinda la posibilidad de utilizar LINQ en: s
Objetos.
s
XML.
s
DataSet.
Esto significa que podremos integrar nuestras consultas directamente en nuestras líneas de código, potenciando así las búsquedas en las aplicaciones, reduciendo tiempos y costos. En el capítulo 5 veremos este tema con mayor detenimiento sobre todos los proveedores disponibles. Otra novedad que encontraremos está relacionada con los controles de los formularios Windows, a saber: s
La clase Control (clase base) sobre la cual algunos controles heredan (incluidos los desarrollados por nosotros) nos permite cambiar la propiedad BackColor y ite la fuente ClearType.
s
Se agregó soporte para gráficos en: – – – –
Alfaomega
. TabPage. Splitter. PictureBox. Visual Studio - Firtman, Natale
Nuevas características Mobile
s
35
No se ite más la propiedad SelectionStart ni la propiedad SelectionLength del ComboBox.
Se ha incluido también la clase SoundPlayer para poder mezclar sonidos, ideal en desarrollo de video juegos. La reproducción de varios sonidos al mismo tiempo está supeditada al hardware del dispositivo. Con respecto a temas de compresión, podremos utilizar dentro del espacio de nombres System.IO.Compression: s
El enumerador CompressionMode.
s
La clase 'HÀDWH6WUHDP para trabajar con el algoritmo Deflate.
s
La clase GZipStream para trabajar con datos GZip.
Power Toys para .NET Compact Framework 3.5. Es un paquete de herramientas muy útiles, que puede ser descargado gratuitamente desde el sitio de Microsoft, y que, entre otras cosas, incluye: s
Remote Performance Monitor: Herramienta de análisis que nos permite obtener información de performance, compatible con el Monitor de Performance de Windows de escritorio.
s
CLR Profiler: Nos permite obtener información acerca de la memoria sobre un proceso determinado y del comportamiento del Recolector de Basura.
s
NetCFSvcUtil.exe: Utilidad para la creación de la clase proxy que se ha de usar para consumir servicios WCF.
s
Remote Logging Configuration: Gráficamente podemos configurar opciones de informes de auditoría o bitácora, como interop, network y ¿QDOL]HU, entre otros.
Creando aplicaciones La primera diferencia que vamos a encontrar desde la versión 2008 es el agregado de una nueva ventana, sobre la cual deberemos seleccionar la plataforma destino y la versión del .NET Compact Framework. Visual Studio trae por defecto varias plataformas listas para ser utilizadas, es decir que no deberemos instalar nada más para poder desarrollar proyectos sobre los siguientes sistemas: s s s
Pocket PC 2003. SmartPhone 2003. Windows Mobile 5.
Visual Studio - Firtman, Natale
Alfaomega
36
1- Visual Studio
s s
Windows CE 5.0. Windows CE 6.0.
Si queremos trabajar con Windows Mobile 6 o superior, instalaremos los SDK´s (Software Development Kits) que se encuentran disponibles de forma gratuita desde el sitio de Microsoft. Estos paquetes nos proveen de todas las librerías, herramientas e imágenes necesarias para trabajar y además traen información muy útil para el desarrollador, que es importante siempre tener a mano. Visual Studio nos da la libertad de elegir sobre qué versión del .NET Compact Framework construir nuestras aplicaciones, disponiendo de dos posibilidades: .NET Compact Framework 2.0. .NET Compact Framework 3.5. Observamos que se ha quitado soporte para la versión 1.0. De todas maneras, si abrimos una solución hecha con esta versión, Visual Studio automáticamente nos propondrá migrarla a la versión 2.0. En la figura 1-34 podemos apreciar la nueva ventana modal para configurar estos valores.
Fig. 1-34. Ventana donde seleccionaremos la plataforma y la versión del framework. Alfaomega
Visual Studio - Firtman, Natale
Nuevas características Mobile
37
Emulador 3.0 Los emuladores son aplicaciones de escritorio que nos permiten simular un dispositivo móvil para probar nuestros aplicativos. Son de gran utilidad, dado que nos liberan de tener que comprar cada modelo que sale al mercado para poder desarrollar soluciones de este tipo. Actualmente, disponemos de varias imágenes en distintos idiomas y tamaños de pantallas para emular, por ejemplo, un SmartPhone o un equipo Pocket PC. Muchas de ellas vienen con los SDK’s, aunque también hay otras tantas para descargar desde el sitio de Microsoft. Desde la llegada de Visual Studio 2008, trabajaremos con la nueva versión 3.0 del emulador, con características innovadoras. Este software hace una actualización totalmente transparente de alguna versión anterior –si la hubiese en la carpeta original–, generalmente en la ruta C:\Archivos de programa\Microsoft Device Emulator\1.0, si el sistema operativo es en español. Para istrar todas las imágenes instaladas, se utiliza el de Emuladores de Equipos, el cual nos permite conectarnos a una imagen en particular, tal como muestra la figura 1-35.
Fig. 1-35. de Emuladores de Equipos.
Este puede ser llamado desde dos lugares diferentes: s
Generando un directo al archivo dvcemumanager.exe.
s
Desde el entorno (figura 1-36).
Visual Studio - Firtman, Natale
Alfaomega
38
1- Visual Studio
Fig. 1-36. Accediendo al de Emuladores desde Visual Studio 2008.
Algunas de las características incorporadas: s
Mejor istración y ordenamiento de imágenes (podemos crear las nuestras y almacenarlas en el nodo Mis emuladores de dispositivos).
s
Provee de un archivo XML para configurar ciertas propiedades de los emuladores.
s
Interfaces de automatización ideal para realizar pruebas o rutinas que ejecutamos a diario.
s
Modelo de objetos para utilizar en scripts VB.
SQL Server Compact 3.5 SQL Server versión compacta es una base de datos relacional ideal para dispositivos móviles, que fue liberada junto con Visual Studio 2008 y SQL Server 2008, con mejoras que hacen mucho más fácil la portabilidad de los datos. Si bien generalmente su uso se hace en dispositivos con Windows Mobile, también se la puede emplear sin problemas en equipos de escritorio o Tablet PC –cuando se necesita almacenar un volumen de datos no mayor a 4 Gb y donde no se requieren de todas las prestaciones de istración y performance que su versión express o para servidores provee–. Alfaomega
Visual Studio - Firtman, Natale
Nuevas características Mobile
39
Nuevas características soportadas: s
Mejoras en la construcción de consultas mediante T-SQL.
s
Se incorporaron operadores como Top y Cast.
s
Incorporación del tipo de dato timestamp.
s
Posibilidad de hacer consultas anidadas dentro de una cláusula FROM.
s
Compatibilidad de encriptación de versiones anteriores.
s
Mejoras en escenarios de suscripciones múltiples.
s
Mejoras para la sincronización de múltiples s simultáneos.
s
Sincronización de valores de columnas que hayan sufrido algún cambio, sin tener que replicar una fila completa.
Desde el Service Pack 1 se incorporaron varias funcionalidades extra: s
ADO.NET Servicios de Sincronización.
s
Mejoras de replicación con SQL Server 2000, 2005 y 2008.
s
Soporte para tipos de datos espaciales.
s
Soporte para date, time, datetimeoffset y datetime2, incorporados en SQL Server 2008.
s
ADO.NET Entity Framework.
Es importante aclarar que este tipo de base de datos es simplemente un archivo con extensión .sdf, que sólo vive cuando es instanciado. Carece de procedimientos almacenados, vistas, funciones y programación de tareas, entre otras cosas. Para la creación de un archivo de SQL Server Compact 3.5 tenemos tres opciones principales: s
Desde Visual Studio.
s
Mediante el Management Studio de SQL Server 2008.
s
Desde código.
La figura 1-37 nos muestra cómo crear desde el Explorador de Servidores una base de datos SQL Server Compact 3.5. Muy fácilmente podemos generar tablas y realizar consultas, todo absolutamente integrado en el entorno (figura 1-38).
Visual Studio - Firtman, Natale
Alfaomega
40
1- Visual Studio
Fig. 1-37. Creando una base de datos SQL Server Compact 3.5.
Fig. 1-38. Creando tablas y consultas sobre la base de datos.
Pruebas de unidad Otra de las características incorporadas desde la versión 2008 es la posibilidad de realizar pruebas de unidades para dispositivos móviles, a través de un framework exclusivo para estos equipos.
Alfaomega
Visual Studio - Firtman, Natale
Nuevas características Mobile
41
El Framework de Pruebas Unitarias para Dispositivos (así se lo llama) es un subconjunto del Framework de Pruebas Unitarias, es por ello que posee algunas limitaciones relacionadas con manejo de datos, ASP.NET y servicios Web. De todas maneras, nos provee de funcionalidades muy útiles para validar porciones de código a lo largo del ciclo de desarrollo, velando por el buen funcionamiento de la aplicación. Lo interesante es que posee soporte .NET Compact Framework 2.0 y 3.5, por ende, podremos someter a este tipo de pruebas a proyectos que hayan sido desarrollados con Visual Studio 2005. Dichas pruebas se generan en un proyecto independiente dentro de la solución y son ejecutadas directamente sobre un emulador o dispositivo real. Para crear una prueba de unidad, podemos simplemente posicionarnos sobre el método que se ha de validar y en el menú contextual tenemos esta opción, tal como lo muestra la figura 1-39.
Fig. 1-39. Generando una prueba de unitaria.
Un asistente nos guiará para la creación del proyecto y, mediante diversos Asserts, podremos escribir nuestra rutina de validación. Dispondremos de una ventana de Vistas de resultados, donde podremos obtener un reporte de las pruebas realizadas, sus correspondientes estatus e información relevante –como valores esperados y devueltos–. En la figura 1-40 observamos cómo se vería una prueba fallida.
Visual Studio - Firtman, Natale
Alfaomega
42
1- Visual Studio
Fig. 1-40. Prueba unitaria fallida. El valor esperado no coincide con el devuelto.
Alfaomega
Visual Studio - Firtman, Natale
Introducción
.NET Framework 3.5
43
2
Introducción A lo largo de los años, en el ambiente de desarrollo de Microsoft hemos estado utilizando diversas versiones del .NET Framework, siempre atentos a cada novedad que era liberada en cada una de ellas, así como también pendientes de algún Service Pack –que no sólo ha traído mejoras, sino también agregado de funcionalidades–. Aquí veremos principalmente la versión 3.5, aunque también repasaremos brevemente algunos temas de interés, que fueron incorporados en versiones anteriores y que para muchos de los programadores todavía son novedades importantes.
Novedades en la versión 2.0 Al mismo tiempo que salía a la luz la tan esperada versión 2.0 del framework, aparecía Visual Studio 2005 (con sus versiones Express) y SQL Server 2005 con herramientas potentes para los desarrolladores de aplicaciones. Muchos profesionales consideran a ésta como la versión que marcó un antes y un después en la historia del desarrollo de Microsoft con .NET Framework. Algunos de los cambios que encontramos en estas épocas fueron: s Diseño de aplicaciones de 64 bits. s Nuevas características de ADO.NET. s .NET Compact Framework 2.0. s ClickOnce. Visual Studio - Firtman, Natale
Alfaomega
44
2- .NET Framework 3.5
s
ASP.NET: – – – – – – – –
Nuevos controles. La posibilidad de armar una pequeña aplicación sin escribir una línea de código. Caché de datos. Creación de páginas maestras. Web Parts. Profile. Themes y skins. Kits de iniciación para Visual Studio 2005.
s
Novedades en los lenguajes y compiladores (C# 2.0 y VB.NET 8.0): – Genéricos. – Tipos nulos. – Iteradores. – Clases parciales. – Métodos anónimos.
s
Mejoras para trabajar con datos XML.
s
Mejoras en el modelo de datos gracias al BindingSource.
s
Formularios Windows: – ToolTrip. – DataGridView. – WebControl. – Mejoras en el ListView. – SplitContainer.
s
Mejoras para el manejo de la globalización.
Novedades en la versión 3.0 El lanzamiento de esta versión, en conjunto con Windows Vista, al principio había hecho las cosas un poco complicadas, dado que no se había liberado una nueva versión del IDE y, para probar muchas de las características que traía, era necesario instalar unos ejecutables que nos permitirían ir conociendo lo que vendría o, por qué no, empezar a utilizarlas en nuestros proyectos. Algunas de las novedades: Alfaomega
Visual Studio - Firtman, Natale
Windows CardSpace
s
Windows Communication Foundation.
s
Windows CardSpace.
s
Windows Workflow Foundation.
s
Windows Presentation Foundation.
s
LINQ.
s
AJAX.
45
Windows CardSpace Introducción Hoy en día, accedemos a una gran cantidad de servicios y aplicaciones en Internet a través de alguna de las formas de autenticación disponibles. La más común de ellas es ingresar un y una contraseña. Pero es una realidad el hecho de tener varias cuentas, con diferentes contraseñas, en alguno de estos servicios o aplicaciones. Repasemos tan sólo un par de ellos: s
Nuestra casilla de correo.
s
Cuenta bancaria online.
s
Intranet corporativa.
s
Blogs.
s
Suscripciones a diarios o revistas.
Es muy probable que en cada uno de los puntos tengamos un , una clave, una pregunta de seguridad y una dirección de correo electrónico diferente, lo cual es evidente que es complicado de mantener y, claro, de recordar. La seguridad, desde hace unos años, comenzó a ser un tema crucial y varias empresas alrededor del mundo unieron fuerzas para desarrollar protocolos y estándares, que permitan minimizar al máximo cualquier intento de a datos protegidos por algún intruso. ¿A quién no le sucedió alguna vez intentar ingresar a algún servicio con una cuenta que, en realidad, era la que estaba registrada en otro? Y como no recordábamos la contraseña, íbamos a la sección de recuperación de claves, donde un asistente nos solicitaba la pregunta secreta –pregunta que tampoco recordábamos–. Visual Studio - Firtman, Natale
Alfaomega
46
2- .NET Framework 3.5
De a poco, nos vamos dando cuenta de que es necesario contar con un sistema unificado que nos permita istrar toda esa información, es decir, que istre nuestras credenciales de una manera ubicua y fáciles de mantener.
Qué es El manejo de identidades, es decir quiénes somos, no es un asunto de interés sólo en sistemas. Todos nosotros disponemos de un documento que nos identifica como personas de un país, un carnet de socio que dice que pertenecemos a algún club deportivo, un registro de conducir que informa nuestro tipo de sangre y la categoría de vehículo que podemos manejar, etc. Es decir que disponemos de diferentes “tarjetas”, para utilizar en diferentes medios, con el fin de informar ante alguien quiénes somos. Windows CardSpace fue introducido en Windows Vista con el .NET Framework 3.0 (y también en Windows XP mediante Windows Update) y nos permitirá operar con identidades digitales, que podremos emplear al ingresar a algunos de los servicios que lo soporten, mientras provee de un camino seguro y confiable para validar quiénes somos. CardSpace es una tecnología de identificación que utiliza tarjetas digitales que nos permitirán iniciar sesión en sistemas que utilicen CardSpace, sin la necesidad de ingresar un y una contraseña.
Crear una tarjeta Utilizar CardSpace, desde el punto de vista de un cliente, es sencillo. Para generar las tarjetas que nos permitirán identificar, deberemos ir al de control y allí tendremos el ícono de Windows CardSpace para acceder a esta tecnología (figura 2-1). Si hacemos doble clic, un asistente nos guiará en la creación de las tarjetas digitales. Lo primero que nos preguntará es si vamos a generar una tarjeta personal o una istrada. Las primeras son tarjetas que generamos nosotros mismos, donde ingresamos algunos datos, que son encriptados y almacenados en la tarjeta, para el intercambio con un servicio que requiere que nos autentiquemos. Las del tipo istradas son generadas por empresas u organismos (pensemos en un banco o la empresa para la cual trabajamos), que nos las pueden enviar como un archivo digital para poder instalarlas en nuestra computadora. En nuestro caso vamos a crear una personal, proponiéndonos completar algunos de los datos (o todos) que vemos en la figura 2-2. Alfaomega
Visual Studio - Firtman, Natale
Windows CardSpace
47
Fig. 2-1. Ícono de Windows CardSpace en el de control.
Fig. 2-2. Creando una tarjeta personal.
Visual Studio - Firtman, Natale
Alfaomega
48
2- .NET Framework 3.5
Si bien podemos llenar sólo algunos de ellos, es probable que el sitio Web al que queramos acceder nos requiera otros, los cuales nos serán informados al momento de enviar la tarjeta. Algo que nos puede ayudar a identificar fácilmente las tarjetas que tenemos generadas, se da a través de la imagen que podemos asignar; por ejemplo, podríamos asignar una imagen con el logo de cada uno de los bancos sobre los cuales tengamos mediante esta tecnología. En la figura 2-3 vemos la tarjeta generada y algunas operaciones que podremos ejecutar sobre ella: s
Realizar una copia de seguridad para poder restaurarla en otro equipo.
s
Modificar alguno de los datos que ingresamos.
s
Duplicar una tarjeta para hacer pequeñas modificaciones y reutilizarla en algún sistema similar.
Fig. 2-3. Operaciones sobre las tarjetas generadas.
Para aquellos que compartan la PC sobre la cual tienen las tarjetas, una alternativa para mejorar la seguridad es utilizar el Número de Identificación Personal (NIP), una clave alfanumérica que nos será solicitada al momento de realizar una modificación sobre alguno de sus datos, así como el uso ante un servicio. Es importante saber que si olvidamos la contraseña no hay forma de recuperarla. Alfaomega
Visual Studio - Firtman, Natale
Windows CardSpace
49
Cómo funciona Para entender a grandes rasgos el funcionamiento de Windows CardSpace, veamos la figura 2-4. 1. Un intenta acceder a un servicio online o a una página Web a través de una aplicación que soporte CardSpace, como por ejemplo el Internet Explorer (o Firefox con el complemento adecuado). 2. La respuesta que obtiene contiene, entre otras cosas, la información requerida para que pueda ser validado (por ejemplo, nombre, apellido y correo electrónico). Aquí, el selector de identidades toma el control y le solicita al que brinde una tarjeta digital para continuar con el proceso. 3. Una vez que el navegador tiene esta información, se a con algún proveedor de identidad (si es una tarjeta personal, es el mismo equipo del ) para que le genere un token de seguridad válido. 4. La devolución es recibida en el equipo desde donde se realizó la petición para continuar con el paso siguiente. 5. Con el token generado, CardSpace lo envía al servicio que se intenta acceder para que nos autentique. Todo esto se logra gracias a los protocolos de transportes que permiten sistemas que los operen entre sí. Proveedores de identidad
Servicio / Sitio Web
5
Token de seguridad
4 3
1
Token de seguridad
2
con CardSpace
Fig. 2-4. Proceso de autenticación con CardSpace. Visual Studio - Firtman, Natale
Alfaomega
50
2- .NET Framework 3.5
Utilizando nuestra tarjeta digital Cuando nos registramos, por ejemplo, en una página Web que soporta CardSpace, ésta nos dará la posibilidad de asociar nuestra cuenta con una tarjeta digital. Al volver a ingresar al sitio, no será necesario tener que brindar un o una contraseña. El selector de identificaciones automáticamente aparecerá mostrándonos aquellas tarjetas que cumplen con los requisitos que el sitio solicita y, de esta manera, seleccionaremos nuestra tarjeta para enviarla al servidor y ser autenticados.
Windows Communication Foundation Introducción Es casi imposible pensar hoy en sistemas que no se conecten entre sí. Aplicaciones que consumen servicios expuestos por otras para realizar transacciones, validaciones u operaciones comerciales son muy comunes. Podemos tener interconectividad dentro de una misma red, como así también hacer uso de Internet como medio de comunicación, con todo lo que ello también implica: Cuestiones de seguridad, intercambio de datos, etc. Supongamos que tenemos que desarrollar un sistema de gestión para una empresa y nos anticipan de antemano que éste expondrá funcionalidad para otras aplicaciones satélite, como por ejemplo: s
Una página Web, hosteada en un servidor externo, para que los clientes carguen pedidos y consulten el estado de su cuenta corriente.
s
Una aplicación de escritorio WinForms, que realice el control de stock.
s
Una intranet, donde se podrán realizar diversas tareas relacionadas con el negocio.
s
Un sistema de toma de pedidos Mobile sobre plataforma Java.
Pero, además, nos comentan que es muy probable que nuestra solución por desarrollar pueda ser integrada con el sistema de nuestra casa central. Como podemos notar, un punto clave en el diseño de nuestra arquitectura será la interconexión de todos los sistemas. Para lograr ello, antes debíamos pensar dónde implementar alguna, o varias, de las siguientes opciones disponibles para el correcto intercambio de información:
Alfaomega
Visual Studio - Firtman, Natale
Windows Communication Foundation
s
COM+.
s
Enterprise Services.
s
MSMQ (Message Queue).
s
. Net Remoting.
s
WS (Web Services).
s
WSE (Web Services Enhancements).
51
Posiblemente, nos encontremos ante la situación de tener que implementar más de una de ellas para lograr el objetivo, lo cual implica un diseño complicado, ya sea desde el punto de vista arquitectónico, de diseño y, por supuesto, un tema no menor, el mantenimiento de las mismas a lo largo del tiempo.
Qué es Windows Communication Foundation (WCF) es un modelo de programación unificado orientado a servicios, que fue incluido en el .NET Framework 3.0, donde se integran las plataformas de interconexión que antes mencionamos. La idea es tener un único framework para construir aplicativos conectados, permitiendo tener una arquitectura adaptable, escalable y con la característica de ser mantenido en el tiempo para nuestras soluciones distribuidas. WCF permitirá que clientes de diversas plataformas consuman los servicios que hayamos decidido exponer, mediante alguno de los estilos de comunicación provistos por el framework a través del intercambio de mensajes, por ejemplo utilizando SOAP o algún otro protocolo, tal como muestra la figura 2.5.
Cliente WCF
Servicio WCF
Fig. 2-5. Intercambio de mensajes entre cliente y servicio.
Conceptos fundamentales Para crear un servicio que será consumido por un cliente (u otro servicio), debemos seguir una serie de pasos bien definidos:
Visual Studio - Firtman, Natale
Alfaomega
52
2- .NET Framework 3.5
1. Definir un Contrato de Servicio. 2. Implementar la clase generada en el punto anterior. 3. Albergar el servicio en alguna de las opciones disponibles y exponer puntos de . 4. Configurar un cliente para que pueda consumir el servicio. Contrato de Servicio Básicamente, es una clase que va a definir qué métodos vamos a exponer en WCF. Para ello, se utilizan principalmente dos atributos decoradores: ServiceContract: Decora la clase servicio que vamos a utilizar. OperationContract: Decora los métodos que serán publicados para que los clientes puedan consumirlo. Aquellos métodos que no posean este atributo no serán accedidos. En la práctica, esta clase es generalmente una interface que puede definir más de un contrato en la clase servicio, permitiéndonos flexibilidad a la hora de su implementación. Disponemos, además, de otros atributos que nos van a permitir serializar tipos de datos no primitivos, ellos son: DataContract. DataMember. En conjunto son utilizados para determinar un tipo de dato que, generalmente, definimos nosotros para nuestro servicio, que debe ser intercambiado entre un cliente y un servicio. Supongamos que tenemos nuestro tipo de dato Cliente, a través de DataContract y DataMember definiremos los que se utilizarán en la comunicación. Implementar el Contrato de Servicio Como antes mencionábamos, en la práctica nuestro contrato de servicio es una interface. En este punto crearemos una clase que implemente la clase servicio que posee los contratos. Alojar el servicio y exponer puntos de Aquí deberemos elegir alguna de las opciones (procesos Windows) que nos da WCF para albergar los servicios que expondremos a nuestros clientes. Este proceso, que puede tener más de un servicio, será el responsable de istrar el contexto y el ciclo de vida de los mismos.
Alfaomega
Visual Studio - Firtman, Natale
Windows Communication Foundation
53
Entre las alternativas disponibles tenemos: s
IIS: A partir de la versión 5.1 de Internet Information Server podemos publicar nuestros servicios, aunque con algunas limitaciones, dado que al igual que IIS 6.0 sólo lo hacen a través de HTTP. En ciertas ocasiones, IIS 7.0 será la solución a la cual debamos recurrir, dado que soporta no sólo HTTP, sino también T, MSMQ y pipes.
s
WAS: Windows Process Activation Service es un mecanismo que nos permite dar vida a servicios WCF, tanto en Windows Vista como en Windows Server 2008. Se trata de otra alternativa sin tener que recurrir a IIS, brindándonos los mismos protocolos de transporte que IIS 7.0 con mínimas configuraciones.
s
Servicio Windows: Aquí el responsable de istrar el ciclo de vida es el sistema operativo. Para exponer servicios se crea un objeto ServiceHost.
s
En una aplicación: Utilizando ServiceHost podemos hacer que nuestro servicio sea mantenido en un formulario o en una aplicación de consola. Muchas veces esta última opción es utilizada para hacer pruebas o depuración.
Toda la comunicación que ocurre entre un cliente y un servicio se realiza a través de puntos de llamados endpoints. Éstos permitirán acceder a aquellas operaciones que hayamos declarado en el servicio. Mediante los endpoints podremos, por ejemplo, exponer una misma operación (aquellos métodos que decoramos con el atributo OperationContract) sobre diversos protocolos, esquemas de seguridad, etc. Es decir, nos encargaremos de configurar el medio de transporte de los mensajes para que el par cliente/ servicio pueda intercambiar información. En la figura 2-6 podemos apreciar cómo un servicio define varios puntos de .
Endpoints expuestos
{
Clase Servicio
WCF Proceso
Fig. 2-6. Un servicio exponiendo varios endpoints. Visual Studio - Firtman, Natale
Alfaomega
54
2- .NET Framework 3.5
Los endpoints se componen de tres piezas fundamentales que nos permitirán configurar los servicios para especificar dónde se encuentran, cómo nos conectaremos a ellos y qué es lo que exponen: s
Dirección (Address).
s
Vinculación (Binding).
s
Contrato (Contract).
Address: Nos permite definir la ruta de nuestro servicio para que lo clientes puedan enviar los mensajes. Básicamente es una URL (dirección de recurso), que indica el protocolo utilizado, el nombre de la computadora que está prestando servicio y, opcionalmente, un número de puerto según el protocolo que se está empleando. Por ejemplo, si estamos utilizando HTTP, podría ser: http://localhost:8081/ServiciosWCF/StockService. Otro ejemplo pero con pipes (para comunicaciones entre procesos) sería: net.pipe://localhost/ServiciosWCF/StockService. Es muy común utilizar direcciones base cuando exponemos varios endpoints que comparten la misma ruta; de esta manera, evitamos realizar duplicaciones de nombres de computadoras o de puertos. Binding: Especifica cómo el mundo exterior se comunicará con un servicio. Aquí es donde podremos configurar varios aspectos como seguridad, protocolos de transporte, si soporta o no transacciones, comunicación sincrónica o asincrónica, etc. WCF nos provee de una serie de bindings ya definidos, que podremos utilizar según sus características, o bien podremos crear uno que cumpla con las necesidades requeridas. Veamos algunos que ya poseemos en la librería de WCF, listos para ser empleados: 1. BasicHttpBinding: Cumple con la especificación WS-I Basic Profile 1.1. 2. WSHttpBinding: Cumple con las especificaciones WS-* para soportar seguridad, transacciones distribuidas, Http y Https habilitados, codificación de los mensajes con la posibilidad de utilizar MTOM para codificar mensajes que posean datos binarios, etc. 3. NetTBinding: Utiliza el protocolo de transporte T para transmitir mensajes, empleando codificación binaria. Ideal para escenarios donde se necesita buena performance en una LAN entre computadoras con sistema operativo Windows. Provee soporte de transacciones y seguridad.
Alfaomega
Visual Studio - Firtman, Natale
Windows Communication Foundation
55
4. NetNamedPipeBinding: Emplea named pipes para comunicaciones entre procesos en una misma computadora. Provee alta performance, seguridad y soporte de transacciones. 5. Contract: Define qué operaciones vamos a estar exponiendo hacia el exterior. Configurar el cliente Nuestra aplicación cliente consumirá los métodos que decidimos poner a disposición, a través de una clase proxy que debe ser generada para encapsular varios aspectos (comunicación, codificación), que nos habilitarán a hacer uso de los servicios. Más adelante, veremos un ejemplo de cómo generar el proxy y cómo un cliente se configura para enviar un mensaje a un endpoint.
Creando un servicio y un cliente Ahora que hemos realizado un repaso sobre WCF, nada mejor para comprender algunos conceptos que realizar algún ejemplo. Servicio Lo primero que haremos será crear un proyecto dentro de Visual Studio del tipo %LEOLRWHFDGHVHUYLFLRV:&), tal como muestra la figura 2-7.
Fig. 2-7. Creando un proyecto WCF en Visual Studio. Visual Studio - Firtman, Natale
Alfaomega
56
2- .NET Framework 3.5
De esta manera, Visual Studio automáticamente nos generará la solución y el proyecto correspondiente, con dos clases que ya poseen funcionalidad implementada, ellas son: IService1 y Service1. Estas dos clases ya están listas para ser utilizadas y nos permitirán comprender el funcionamiento de WCF. Analicemos el código de IService1: XVLQJ6\VWHP uVLQJ6\VWHP&ROOHFWLRQV*HQHULF XVLQJ6\VWHP/LQT XVLQJ6\VWHP5XQWLPH6HULDOL]DWLRQ XVLQJ6\VWHP6HUYLFH0RGHO usLQJ6\VWHP7H[W namesSDFH:&)'HPR { >6HUYLFH&RQWUDFW@ SXEOLFLQWHUIDFH,6HUYLFH ^ >2SHUDWLRQ&RQWUDFW@ VWULQJ*HW'DWDLQWYDOXH >2SHUDWLRQ&RQWUDFW@ &RPSRVLWH7\SH*HW'DWD8VLQJ'DWD&RQWUDFW&RPSRVLWH7\SH FRPSRVLWH ` >'DWD&RQWUDFW@ SXEOLFFODVV&RPSRVLWH7\SH ^ ERROERRO9DOXH WUXH VWULQJVWULQJ9DOXH +HOOR >'DWD0HPEHU@ SXEOLFERRO%RRO9DOXH ^ JHW^UHWXUQERRO9DOXH` VHW^ERRO9DOXH YDOXH` ` >'DWD0HPEHU@ SXEOLFVWULQJ6WULQJ9DOXH ^ JHW^UHWXUQVWULQJ9DOXH` VHW^VWULQJ9DOXH YDOXH` ` ` ` Alfaomega
Visual Studio - Firtman, Natale
Windows Communication Foundation
57
Podríamos dividir el contenido de IService1 en tres bloques bien claros: La declaración de los using, una interface IService1 y una clase que se llama &RPSRVLWH7\SH. Veamos cada uno de ellos. En la declaración de los using vamos a necesitar básicamente dos espacios de nombres para poder trabajar: uno será 6\VWHP6HUYLFH0RGHO y el otro será 6\VWHP5XQWLPH6HULDOL]DWLRQ. Se requiere esto dado que los decoradores ServiceContract y OperationContract se encuentran dentro del primero y DataContract y DataMember, en el segundo. Miremos el código correspondiente a la creación de la clase &RPSRVLWH7\SH, que será utilizada como un tipo de datos: >'DWD&RQWUDFW@ SXEOLFFODVV&RPSRVLWH7\SH ^ ERROERRO9DOXH WUXH VWULQJVWULQJ9DOXH +HOOR >'DWD0HPEHU@ SXEOLFERRO%RRO9DOXH ^ JHW^UHWXUQERRO9DOXH` VHW^ERRO9DOXH YDOXH` ` >'DWD0HPEHU@ SXEOLFVWULQJ6WULQJ9DOXH ^ JHW^UHWXUQVWULQJ9DOXH` VHW^VWULQJ9DOXH YDOXH` ` `
El atributo DataContract lo que hará será serializar esta clase, permitiendo que tanto el servicio como el cliente puedan utilizarla en el intercambio de mensajes. Veamos que tenemos dos propiedades públicas que se encuentran decoradas con DataMember, lo que implica que ambas son parte del contrato de datos (DataContract) y serán serializadas. Como podemos apreciar, utilizando DataContract y DataMember podremos crear los tipos de datos que necesitemos, habilitándonos a intercambiarlos entre un cliente y un servicio, a través de algún transporte de comunicación que hayamos definido. Ahora pongamos atención en la interface:
Éste es el contrato de servicio, notemos los atributos ServiceContract y OperationContract. Aquí lo que tenemos es un contrato de servicio que expone dos operaciones: *HW'DWD y *HW'DWD8VLQJ'DWD&RQWUDFW . Gracias a OperationContract, los clientes harán uso de ellos. Notemos cómo *HW'DWD8VLQJ'DWD&RQWUDFW hace uso del tipo de dato &RPSRVLWH7\SH que vimos en el bloque anterior. Miremos el código correspondiente a la clase Service1: QDPHVSDFH:&)'HPR { SXEOLFFODVV6HUYLFH,6HUYLFH ^ SXEOLFVWULQJ*HW'DWDLQWYDOXH ^ UHWXUQVWULQJ)RUPDW
Esta es la clase que implementa la interface IService1, aunque también podría haber implementado otras más. Esta clase se conoce comúnmente como clase tipo servicio y este esquema es ideal para no tener que mezclar responsabilidades y poder tener código flexible –y que nos permita ir incorporando funcionalidades a lo largo del tiempo–. Podemos notar cómo se codificó cada operación del contrato de servicio (métodos *HW'DWD y *HW'DWD8VLQJ'DWD&RQWUDFW) y Alfaomega
Visual Studio - Firtman, Natale
Windows Communication Foundation
59
que uno utiliza un tipo de datos primitivo y el segundo el tipo de datos que hemos definido en la interface. Ahora que tenemos definido el servicio y qué métodos vamos a exponer, veamos cómo es la configuración de los endpoints de este ejemplo, generado por el entorno de Visual Studio. Para ello, tenemos dos alternativas: O miramos el archivo app.config que se encuentra dentro del proyecto o recurrimos al editor. En este caso, iremos por el camino de la última opción. Para acceder al editor, debemos posicionarnos sobre app.config y, haciendo clic con el botón derecho, vamos a obtener la opción (GLWDU FRQ¿JXUDFLyQGH:&). Una vez que ingresamos dentro de cada servicio, podremos ver los puntos de definidos –en este caso dos–. El primero de ellos (figura 2-8) está definido de la siguiente manera: s
El Address no está definido, por ende utiliza la dirección base establecida en el servicio.
s
El Binding tiene establecido utilizar wsHttpBinding.
s
El Contract es la interface IService1.
Es decir que los clientes se conectarán a través de servicios Web.
Fig. 2-8. Endpoint que permite el a clientes para consumir las operaciones definidas en el contrato. Visual Studio - Firtman, Natale
Alfaomega
60
2- .NET Framework 3.5
Veamos el segundo endpoint (figura 2-9); éste define un nodo para publicar la metadata del servicio, que será accedida a través de la dirección base + /mex.
Fig. 2-9. Endpoint de la metadata.
Pongamos a correr el servicio presionando la tecla F5. Visual Studio albergará la aplicación y la pondrá en funcionamiento a través del ejecutable ZFIVYFKRVWH[H, al mismo tiempo que un cliente de prueba será activado para que podamos comprobar la operatoria del servicio y aquellos métodos que etiquetamos con OperationContract. Este cliente nos permitirá ingresar algunos valores de prueba y ver su respuesta, tal como muestra la figura 2-10. Cliente En este punto crearemos una aplicación de consola dentro de la misma solución, que utilizará el servicio que hemos visto. Para que una aplicación pueda consumir un servicio, es necesario crear un proxy del lado cliente. Para lograr ello hay dos formas: Utilizando la herramienta de línea de comandos VYFXWLOH[H o a través de Visual Studio. En nuestro caso, optaremos por la última alternativa. Alfaomega
Visual Studio - Firtman, Natale
Windows Communication Foundation
61
Fig. 2-10. Cliente de prueba provisto por Visual Studio 2008.
En la aplicación de consola agregaremos una referencia de servicio, tal como lo indica la figura 2-11:
Fig. 2-11. Agregando una referencia de servicio.
Visual Studio - Firtman, Natale
Alfaomega
62
2- .NET Framework 3.5
Sobre la ventana que aparece, podremos ingresar la URL del servicio, o bien, utilizaremos el botón Detectar que buscará los servicios de nuestra solución. Al realizar esta última opción, podremos ver el servicio que generamos con su correspondiente contrato y las operaciones que hemos visto anteriormente. Definiremos el espacio de nombre y presionaremos Aceptar (figura 2-12).
Fig. 2-12. Creando la clase proxy para nuestro cliente.
Notaremos que se agregó un archivo DSSFRQ¿J, que contiene toda la información necesaria para realizar el encapsulamiento de la comunicación y poder así llamar a los métodos remotos. Pero detengámonos en unas líneas en particular de este archivo:
HQGSRLQWDGGUHVV KWWSORFDOKRVW'HVLJQB7LPH $GGUHVVHV:&)'HPR6HUYLFH ELQGLQJ ZV+WWS%LQGLQJ ELQGLQJ&RQ¿JXUDWLRQ :6+WWS%LQGLQJB,6HUYLFH FRQWUDFW 6HUYLFH3UR[\,6HUYLFH QDPH :6+WWS%LQGLQJB,6HUYLFH! LGHQWLW\! GQVYDOXH ORFDOKRVW! LGHQWLW\! HQGSRLQW!
Alfaomega
Visual Studio - Firtman, Natale
Windows Communication Foundation
63
En este bloque de código tenemos definido el endpoint de nuestro servicio, con su dirección (address), la vinculación a él (binding), el contrato de servicio (contract) y un nombre asignado (name), en este caso :6+WWS%LQGLQJB,6HUYLFH. Ahora deberemos instanciar la clase proxy, pasándole en su constructor el nombre del endpoint que tenemos en el DSSFRQ¿J, y realizaremos las llamadas a los métodos *HW'DWD y *HW'DWD8VLQJ'DWD&RQWUDFW . Nuestra clase Program.cs se vería así: XVLQJ6\VWHP XVLQJ6\VWHP&ROOHFWLRQV*HQHULF XVLQJ6\VWHP/LQT XVLQJ6\VWHP7H[W XVLQJ0L&OLHQWH:&)6HUYLFH3UR[\ QDPHVSDFH0L&OLHQWH:&) { FODVV3URJUDP ^ VWDWLFYRLG0DLQVWULQJ>@DUJV ^ 6HUYLFH&OLHQWSUR[\ QHZ 6HUYLFH&OLHQW:6+WWS%LQGLQJB,6HUYLFH VWULQJYDORU SUR[\*HW'DWD &RQVROH:ULWH/LQH5HVXOWDGR*HW'DWD ^` YDORU &RPSRVLWH7\SHFRP7\SH QHZ&RPSRVLWH7\SH FRP7\SH%RRO9DOXH WUXH FRP7\SH6WULQJ9DOXH 8QPHQVDMHGHSUXHED &RQVROH:ULWH/LQH9DORU%RRO9DOXH^`SUR[\*HW 'DWD8VLQJ'DWD&RQWUDFWFRP7\SH %RRO9DOXH &RQVROH:ULWH/LQH9DORU6WULQJ9DOXH^`SUR[\ *HW'DWD8VLQJ'DWD&RQWUDFWFRP7\SH 6WULQJ9DOXH &RQVROH5HDG ` ` `
Para ver cómo funciona el cliente, deberemos cambiar en las propiedades del proyecto el orden en el que se inicializarán los mismos, de manera tal que primero se ejecute el servicio y luego se lance el cliente (figura 2-13).
Visual Studio - Firtman, Natale
Alfaomega
64
2- .NET Framework 3.5
Fig. 2-13. Cambiando el orden en el que se inicializan los proyectos.
Si presionamos F5, la salida en pantalla sería la que observamos en la figura 2-14.
Fig. 2-14. Una aplicación de consola consumiendo un servicio de WCF.
Alfaomega
Visual Studio - Firtman, Natale
Windows Workflow Foundation
65
Potenciando WCF Si bien aquí repasamos los conceptos básicos de WCF, es importante saber que podemos extender la mayoría de las funcionalidades expuestas por defecto que nos brinda. Podemos crear nuestros propios patrones de mensajes, enlaces, istrar el comportamiento interno de un servicio, etc. En WCF podremos integrar el uso de JSON, AJAX, REST, RSS y ATOM, permitiéndonos potenciar nuestras aplicaciones de una forma sencilla en combinación con las herramientas de Visual Studio. Algo muy interesante para destacar es la posibilidad de utilizar Windows Workflow Foundation en conjunto con WCF para consumir sus servicios, así como también la posibilidad de exponer como un servicio un flujo de trabajo que puede ser consumido por un cliente.
Windows Workflow Foundation Introducción En la mayoría de los sistemas corporativos, los desarrolladores codifican comportamientos de negocios, tareas que son realizadas por personas o procesos sistémicos que corresponden, por ejemplo, a procedimientos istrativos. Utilicemos como caso una orden de venta. Generalmente, cuando un pedido es ingresado en el sistema de gestión, éste pasa por diferentes estados en los que intervienen tal vez varias personas de diferentes sectores. Por ejemplo, se realizan verificaciones de stock, un departamento de créditos analiza si el cliente dispone de saldo suficiente para realizar la operación –pudiendo rechazarla– y, por último, pasaría al sector donde se factura y se prepara el pedido que será despachado al cliente. Ahora bien, si el pedido es rechazado por el sector de créditos, es muy probable que se le envíe un correo electrónico al comprador, informándole que no se pudo realizar la transacción debido a un problema crediticio. Algún que otro lector se puede sentir identificado con este flujo de trabajo típico de un escenario de ventas, pudiendo tener más o menos caminos para su resolución final. Si nos detenemos un poco, podremos ver que esto se puede replicar en otros escenarios, donde determinadas actividades se relacionan entre sí, donde la finalización de una tarea representa el punto de inicio de otra y, donde según el estado o respuesta de ésta, pueda desencadenar otros eventos.
Visual Studio - Firtman, Natale
Alfaomega
66
2- .NET Framework 3.5
Windows Workflow Foundation (WWF) viene a resolvernos la construcción, automatización y mantenimiento de este tipo de actividades enlazadas entre sí.
Qué es Es un framework para modelar flujos de trabajo, que fue introducido en la versión .NET Framework 3.0. Expone una serie de herramientas y un modelo programático, que nos permitirá crear este tipo de soluciones de manera intuitiva y escalable. En la figura 2-15 podemos ver su arquitectura y los componentes que sustentan a WWF. Desde Visual Studio 2008, se incorporaron herramientas, plantillas y diseñadores de forma nativa, sin la necesidad de instalar extensiones, tal como era requerido en versiones anteriores. Pero, para que quede claro, tratemos de definir qué es un flujo de trabajo. Llamaremos flujo de trabajo a un conjunto de unidades de actividades, relacionadas entre sí, para representar, por ejemplo, el caso de la nota de pedido citada anteriormente. Cuando el departamento de créditos evalúa si el cliente dispone de saldo para efectuar la compra, estamos hablando de una actividad. Es decir que el conjunto de actividades ordenadas nos permite representar circuitos istrativos, sobre los cuales podemos tener alguna intervención humana. WWF nos provee de un grupo de actividades listas para ser utilizadas, así como también la posibilidad de crear nuestras propias actividades que realicen tareas específicas, con el beneficio de que puedan ser reutilizadas en diferentes proyectos. En WWF tenemos dos formas de crear los flujos de trabajo: s
Secuencial: Las actividades se ejecutan en un orden preestablecido, donde la operación arranca en un nodo de partida y llega al último donde finaliza toda la operación. El caso del ejemplo citado anteriormente, del circuito que posee una nota de pedido, es un ejemplo de un flujo de trabajo de estilo secuencial. En la figura 2-16 podemos ver un esquema secuencial.
Alfaomega
Fig. 2-15. Arquitectura de Workflow Foundation.
Visual Studio - Firtman, Natale
Windows Workflow Foundation
s
67
Equipos de estado: Es un modelo basado en estados que pueden ir cambiando, donde los eventos tienen la posibilidad de disparar transiciones entre diversos estados. Pensemos, por ejemplo, en un sistema de reclamos de una mesa de ayuda, donde los s cargan un incidente cuyo estado inicial es “abierto”; cuando en el centro de asistencia se comienza con su resolución, se lo cambia a “en proceso”. Inicio Carga de Nota de pedido
Notificación de pedido rechazado
¿Posee crédito el cliente?
Sí
Facturación
Expedición
Fin
Fig. 2-16. Estilo secuencial de un flujo de trabajo.
El host de WWF es el responsable de manejar todas las peticiones del flujo de trabajo, creando una instancia del objeto :RUNÀRZ5XQWLPH. ¿Pero cómo hacen nuestras aplicaciones para consumir esos servicios? Éstos pueden ser hospedados de diversas formas: s
En nuestra propia aplicación.
s
En un servicio Web.
s
A través de Windows Communication Foundation.
s
Servicio Windows.
Visual Studio - Firtman, Natale
Alfaomega
68
2- .NET Framework 3.5
s
Proceso .NET.
s
Sitio ASP.NET.
Al crear un nuevo proyecto de WWF, disponemos de varias plantillas para cada requerimiento que tengamos. En la figura 2-17 podemos ver las plantillas disponibles que nos provee Visual Studio.
Fig. 2-17. Plantilla de Visual Studio para WWF.
Una muy utilizada para hacer pruebas es la Aplicación de consola para flujos de trabajo, dado que ya tiene implementado el código necesario para poder hospedar en ella misma el flujo de trabajo. A continuación, vemos el código generado automáticamente en una aplicación de consola; notemos las referencias a los ensamblados 6\VWHP:RUNÀRZ5XQWLPH y 6\VWHP:RUNÀRZ5XQWLPH+RVWLQJ XVLQJ6\VWHP XVLQJ6\VWHP&ROOHFWLRQV*HQHULF XVLQJ6\VWHP/LQT XVLQJ6\VWHP7H[W XVLQJ6\VWHP7KUHDGLQJ XVLQJ6\VWHP:RUNÀRZ5XQWLPH XVLQJ6\VWHP:RUNÀRZ5XQWLPH+RVWLQJ
Diseñador visual Al generar un proyecto o agregar un ítem de flujo de trabajo, Visual Studio nos mostrará la superficie del diseñador, para que podamos arrastrar sobre éste las actividades necesarias para sistematizar los procesos y validaciones que nuestro sistema utilizará. En la figura 2-18 podemos apreciar un flujo de trabajo del estilo secuencial. El diseñador nos provee de herramientas para manejar la vista, debido a que muchas veces los esquemas son complejos y no caben en la pantalla. Como adicional, en la ventana de propiedades, podremos asignar valores a las propiedades de cada componente, así como codificar eventos y métodos relacionados con éste.
Visual Studio - Firtman, Natale
Alfaomega
70
2- .NET Framework 3.5
Fig. 2-18. Diseñador de WWF en Visual Studio 2008.
Actividades Como mencionábamos anteriormente, disponemos de varias actividades listas para ser utilizadas en nuestros flujos de trabajo. Si bien ellas cubren una gran parte de las necesidades de los programadores, no olvidemos que tenemos la posibilidad de heredar de uno de ellos o de generar nuestra propia actividad y poder distribuirla en diferentes proyectos. En la figura 2-19 podemos ver todas las que poseemos en nuestra barra de herramientas, las cuales ya se encontraban disponibles desde la versión 3.0 del .NET Framework. Notaremos, también, otro grupo dentro de la misma caja de herramientas, denominado Windows Workflow v3.5, que posee dos componentes: 5HFHLYH$FWLYLW\. 6HQG$FWLYLW\. Éstos fueron incorporados en la versión 3.5 del .NET Framework para trabajar en conjunto con Windows Communication Foundation. Permiten enviar y recibir mensa-
Alfaomega
Visual Studio - Firtman, Natale
Windows Workflow Foundation
71
jes, albergan y exponen servicios de flujo de trabajo y habilitan a aplicaciones clientes externas para poder consumirlos a través de este nuevo medio de comunicación.
Fig. 2-19. Actividades listas para ser utilizadas.
Creando un flujo de trabajo Armaremos un pequeño flujo de trabajo para comprender el funcionamiento de algunas actividades que comúnmente se utilizan. La idea será imprimir en una pantalla de consola los números, pares e impares, comprendidos entre 10 y 20. Nuestro primer paso será crear un proyecto de consola de flujos de trabajo secuenciales, como lo indica la figura 2-20. Sobre el diseñador, agregaremos cuatro actividades: ZKLOH$FWLYLW\, LI(OVH$FWLYLW\ y dos FRGH$FWLYLW\, tal como tenemos en la figura 2-21. Visual Studio - Firtman, Natale
Alfaomega
72
2- .NET Framework 3.5
Fig. 2-20. Plantilla para generar una aplicación de consola de flujos de trabajo secuencial.
Fig. 2-21. Diseño del flujo de trabajo. Alfaomega
Visual Studio - Firtman, Natale
Windows Workflow Foundation
73
En la vista de código de :RUNÀRZFV agregaremos una variable denominada contador: SULYDWHLQWFRQWDGRU
Volviendo nuevamente a la vista de diseño, seleccionaremos la actividad ZKLOH$FWLYLW\ y en las propiedades definiremos: &RQGLWLRQ&RQGLFLyQGHUHJODGHFODUDWLYD &RQGLWLRQ1DPH&RQGLFLRQ5DQJR Y, sobre el editor de condiciones de reglas (figura 2-22), escribiremos: WKLVFRQWDGRU WKLVFRQWDGRU!
Fig. 2-22. Editor de condiciones de reglas y vista de las propiedades de la actividad.
Esto permitirá que se ejecuten las actividades hijas dentro de ZKLOH$FWLYLW\ mientras se cumpla dicha condición, es decir que la variable contador se encuentre entre 10 y 20. En la actividad LI(OVH%UDQFK$FWLYLW\ definiremos: &RQGLWLRQ&RQGLFLyQGHUHJODGHFODUDWLYD &RQGLWLRQ1DPH3DUHV Y, sobre el editor de condiciones de reglas, escribiremos el código para obtener los números pares: WKLVFRQWDGRU
Visual Studio - Firtman, Natale
Alfaomega
74
2- .NET Framework 3.5
Seleccionamos la actividad FRGH$FWLYLW\ y, luego, en el de propiedades escribiremos el nombre del método que contendrá el código que se ha de ejecutar; para ello, anotaremos ,PSULPLU3DUHVen([HFXWH&RGH. Automáticamente, Visual Studio generará el método con el nombre designado, al cual agregaremos las líneas: SULYDWHYRLG,PSULPLU3DUHVREMHFWVHQGHU(YHQW$UJVH ^ &RQVROH:ULWH/LQH1~PHURSDU^`FRQWDGRU FRQWDGRU `
De esta manera, cuando se ejecute la actividad FRGH$FWLYLW\, se imprimirá en pantalla el número par y la variable contador irá decreciendo. Hasta aquí lo que hicimos fue decir que: 1. Se ejecuta una actividad (LI(OVH$FWLYLW\) mientras el valor de la variable contador se encuentre entre 10 y 20. 2. Si el valor de contador es par, se imprime en pantalla un mensaje con el valor del mismo. Al reducir el valor de la variable, pasaremos al próximo número. Procederemos a configurar el código para los números impares. Seleccionaremos LI(OVH%UDQFK$FWLYLW\ y definiremos: &RQGLWLRQ&RQGLFLyQGHFyGLJR &RQGLWLRQQRPEUHGHOPpWRGR ,PSDUHV Realizar la evaluación de la condición a través de la opción &RQGLFLyQGH FyGLJR es otra alternativa, la cual genera un método del tipo booleano y, en función del valor, se determina si pasó dicha condición. Visual Studio automáticamente creará el método Impares, al cual agregaremos las siguientes líneas: SULYDWHYRLG,PSDUHVREMHFWVHQGHU&RQGLWLRQDO(YHQW$UJVH { H5HVXOW FRQWDGRU `
En las propiedades de FRGH$FWLYLW\, asignaremos a ([HFXWH&RGH el valor de ImprimirImpares y el código correspondiente a este método será:
En la clase Program.cs agregaremos dos líneas para que, al ejecutar la aplicación, podamos ver el resultado en pantalla: XVLQJ6\VWHP XVLQJ6\VWHP&ROOHFWLRQV*HQHULF XVLQJ6\VWHP/LQT XVLQJ6\VWHP7H[W XVLQJ6\VWHP7KUHDGLQJ XVLQJ6\VWHP:RUNÀRZ5XQWLPH XVLQJ6\VWHP:RUNÀRZ5XQWLPH+RVWLQJ QDPHVSDFH:RUNÀRZ&RQVROH$SS { FODVV3URJUDP ^ VWDWLFYRLG0DLQVWULQJ>@DUJV ^ XVLQJ:RUNÀRZ5XQWLPHZRUNÀRZ5XQWLPH QHZ :RUNÀRZ5XQWLPH ^ $XWR5HVHW(YHQWZDLW+DQGOH QHZ $XWR5HVHW(YHQWIDOVH ZRUNÀRZ5XQWLPH:RUNÀRZ&RPSOHWHG GHOHJDWHREMHFWVHQGHU:RUNÀRZ&RPSOHWHG(YHQW$UJVH ^ZDLW+DQGOH6HW ` ZRUNÀRZ5XQWLPH:RUNÀRZ7HUPLQDWHG GHOHJDWHREMHFWVHQGHU:RUNÀRZ7HUPLQDWHG(YHQW$UJVH ^ &RQVROH:ULWH/LQHH([FHSWLRQ0HVVDJH ZDLW+DQGOH6HW ` :RUNÀRZ,QVWDQFHLQVWDQFH ZRUNÀRZ5XQWLPH &UHDWH:RUNÀRZW\SHRI:RUNÀRZ&RQVROH$SS:RUNÀRZ LQVWDQFH6WDUW ZDLW+DQGOH:DLW2QH ` ` ` `
Potenciando WWF Windows Workflow Foundation es realmente una herramienta muy poderosa y no se limita a simples actividades. Su arquitectura provee de servicios para persistencia de datos, seguimiento de información sobre el flujo de trabajo, mo-
Alfaomega
Visual Studio - Firtman, Natale
Windows Presentation Foundation
77
tores de reglas, integración con otras tecnologías y productos como SharePoint o BizTalk Server. Quienes desarrollen sistemas que requieran de procesos sistémicos o de intervención humana, para que una determinada operación sea llevada a cabo, encontraran en WWF una solución potente y escalable.
Windows Presentation Foundation Introducción Antes de Windows Presentation Foundation (WPF), si una aplicación requería manejar gráficos 2D o 3D, o una presentación mucho más rica que simples formularios Windows, debíamos recurrir a GDI+ o a Direct3D, mezclando tecnologías y donde, quizá, un grupo de desarrolladores no reunía todos los conocimientos necesarios para llevar a cabo tal tarea. Es por ello, que era preciso juntar varios programadores y diseñadores para crear una interface impactante desde el punto de vista visual. Todo esto cambio desde la llegada de WPF.
Qué es WPF es una nueva forma de diseñar las interfaces de de nuestras aplicaciones. Fue incluida en Windows Vista junto con el .NET Framework 3.0 y, obviamente, está presente en las siguientes versiones con grandes mejoras, nuevas herramientas y la posibilidad de seguir mejorando la calidad de nuestros productos. Mediante WPF podremos crear aplicaciones multimedia, animaciones, gráficos 2D y 3D y personalizar controles que podrán ser incorporados en nuestros proyectos. Un punto importante para destacar es que esta plataforma permite trabajar de forma colaborativa, tanto a los diseñadores como a los desarrolladores, en pos de un objetivo en común, que es brindar una interface rica. Esta integración se logra, principalmente, a través del lenguaje que se utiliza en WPF: XAML. WPF posee un modelo gráfico de vectores basado en la tecnología Direct3D, lo que permitirá realizar modificaciones y transformaciones sin perder calidad. Para dibujar en pantalla nuestro aplicativo, WPF tiene dos formas de realizar el proceso de renderización, mediante software o hardware, teniendo cada una de ellas diferentes impactos en el rendimiento y en la visualización.
Visual Studio - Firtman, Natale
Alfaomega
78
2- .NET Framework 3.5
La idea es aprovechar las capacidades de las tarjetas de video (por ejemplo, la aceleración gráfica) de los equipos sobre el cual corre el aplicativo y delegarle la mayor carga de proceso posible a ellas. La mayoría de las placas gráficas, hoy en día, vienen con DirectX 7.0, o superior, integrada en ellas, con grandes capacidades de memoria y procesamiento. Si la placa de video no reúne las características suficientes, WPF puede utilizar las capacidades del U local, lo cual degrada el rendimiento, teniendo implicancia directa en cómo se visualizan, por ejemplo, efectos o animaciones en pantalla. WPF posee tres niveles de representación, que pueden ser consultados mediante una API que expone, para conocer el ámbito en el cual corre nuestro programa y poder determinar si ejecutamos o no tal porción de código. Los niveles son: s
Nivel 0: No posee aceleración por hardware.
s
Nivel 1: Aceleración gráfica parcial.
s
Nivel 2: La mayoría de las características gráficas gozarán de los beneficios de la aceleración de la tarjeta de video.
Dentro del mundo de WPF, hay dos tipos de aplicaciones que podemos construir: s
Aplicaciones independientes.
s
Aplicaciones del explorador.
Aplicaciones independientes Son las típicas aplicaciones que se instalan en la PC y son accedidas localmente. El entorno de seguridad está limitado a los permisos que haya definido el de red para el que está ejecutando el sistema. Aplicaciones del explorador Se las conoce generalmente como aplicaciones XBAP (del inglés XAML Browser Application), que son hospedadas en servidores Web y accedidas desde un navegador, como Internet Explorer o Firefox. Este tipo de proyectos combinan características de soluciones Web y todo lo rico de las interfaces de WPF, es decir, es como si tuviéramos nuestra aplicación de WPF pero dentro de un navegador Web. A diferencia de las aplicaciones independientes, éstas no son instaladas localmente sobre la computadora que las utiliza: Corren en escenarios de seguri-
Alfaomega
Visual Studio - Firtman, Natale
Windows Presentation Foundation
79
dad diferentes, no generan s directos en el menú de inicio de la PC y son distribuidas mediante ClickOnce.
XAML Todos sabemos que al diseñar un sitio Web o una aplicación de formularios Windows, que requieren de gráficos o de una vista rica, necesitamos de un programador y de un diseñador. Cada uno es bueno en su trabajo y, generalmente, los programadores no tenemos todos los conocimientos para utilizar las herramientas gráficas, ni los diseñadores poseen todos los conocimientos para construir capas de datos, reglas de negocio, programar componentes, etc. XAML es el responsable de la comunión entre programadores y diseñadores; nos permitirá utilizar el mismo lenguaje y la misma sintaxis, podremos trabajar en paralelo sin demorar el proyecto, haciendo cada uno lo que mejor sabe hacer. Microsoft posee herramientas exclusivamente orientadas a programadores y otras donde los diseñadores gráficos se sentirán más a gusto, las cuales analizaremos más adelante, ya que aquí haremos hincapié en el lenguaje. XAML significa Lenguaje Extensible de Formato para Aplicaciones (del inglés, Extensible Application Markup Language), cuya pronunciación es algo así como “zammel”. Este lenguaje declarativo basado en XML nos permite definir la interface de de las aplicaciones, utilizando –al igual que ASP.NET– el concepto de code- behind, en donde podremos codificar la lógica de negocio que se une mediante clases parciales, logrando así separar las tareas para diseñadores y para programadores. Al crear un formulario y arrastrar controles sobre éste, notaremos que son traducidos en etiquetas XAML y no generan, en principio, líneas en el archivo de code-behind. Cada elemento XAML tiene una representación de clase en el CLR de .NET, que, a su vez, puede contener uno o varios atributos que corresponden a propiedades o eventos de dicha clase. Un botón tendría la siguiente sintaxis en XAML: %XWWRQ+HLJKW 0DUJLQ 1DPH EWQ(QYLDU 9HUWLFDO$OLJQPHQW 7RS!(QYLDU%XWWRQ!
En el código anterior podemos notar que las propiedades están definidas mediante una sintaxis de atributos, aunque también puede ser codificado mediante la sintaxis de elementos de propiedad de la siguiente manera:
El resultado de esto sería lo que vemos en la figura 2-23. Ciertas propiedades de algunos objetos no pueden ser definidas mediante la sintaxis de atributos, sino que deben ser hechas a través de la sintaxis de elementos de propiedades. Por ejemplo, si quisiéramos agregar un efecto de sombreado al botón, agregaríamos el siguiente código: 'URS6KDGRZ(IIHFW! 'URS6KDGRZ(IIHFW'LUHFWLRQ! 'URS6KDGRZ(IIHFW'LUHFWLRQ! 'URS6KDGRZ(IIHFW!
Fig. 2-23. Representación gráfica de un botón con XAML. Alfaomega
Visual Studio - Firtman, Natale
Windows Presentation Foundation
81
Herramientas Expression Design Es un producto pensado para diseñadores, con el cual podremos trabajar con gráficos vectoriales, exportar a diversos formatos, ofrecer servicios de colaboración para entornos Web y aplicaciones de escritorio, manipulación de código XAML, etc. Desde la dirección ZZZPLFURVRIWFRPH[SUHVVLRQ podremos descargar una versión de prueba y videos introductorios, que nos brindarán un conocimiento global de la herramienta. Expression Blend Poderosa herramienta para crear interfaces ricas, tanto en WPF como en Silverlight. Es una herramienta orientada a diseñadores, mediante la cual podremos personalizar controles, trabajar con diapositivas, audio y video, entre otras cosas. Aprovecha al máximo XAML, lo que permite a diseñadores y programadores trabajar sobre el mismo proyecto en forma simultánea. Podemos descargar una versión de prueba desde el sitio Web de los productos Expression. Visual Studio Es la interface de desarrollo exclusiva para programadores, responsables de la lógica de negocio, a datos y todo lo referente a este rol. De forma nativa ya tenemos las plantillas necesarias para crear aplicaciones WPF, controles de s, controles personalizados y XBAP.
Fig. 2-24. Interface gráfica de Expression Blend. Visual Studio - Firtman, Natale
Alfaomega
82
2- .NET Framework 3.5
El diseñador gráfico (figura 2-25) nos permite cambiar entre las ventanas de diseño y código XAML; la sincronización entre ellas hace que algún cambio en cualquiera sea reflejado en la otra. Para una mejor visualización, podemos modificar la disposición de las ventanas y hacer uso de la herramienta de zoom sobre la ventana de diseño.
Novedades WPF 3.5 y Visual Studio 2008 Repasemos los temas más importantes que se incorporaron desde el .NET Framework 3.5 y Visual Studio 2008 con Service Pack 1. Novedades desde WPF 3.5 Un nuevo complemento permitirá que aplicaciones XBAP puedan ser ejecutadas desde el navegador Firefox 2.0, o superior. Esto nos evitará tener que instalar agregados de terceros para equipos que posean este navegador y necesiten correr soluciones XBAP. Si en nuestro servidor Web tenemos hospedadas aplicaciones Web y XBAP bajo un mismo dominio, éstas podrán compartir las cookies que cada una de ellas crea convenientes, logrando reutilizar la información que fue generada sobre cada tecnología.
Fig. 2-25. Diseñador de Visual Studio 2008 para WPF. Alfaomega
Visual Studio - Firtman, Natale
Windows Presentation Foundation
83
Aún podemos mantener compatibilidad con el .NET Framework 3.0, si nuestra aplicación WPF 3.5 sólo utiliza los ensamblados disponibles en aquella versión del framework y, de la misma manera, soluciones WPF 3.0 pueden correr sobre equipos que posean el .NET Framework 3.5. En materia de imágenes, tendremos: s
La posibilidad de almacenar en el caché local de la computadora imágenes que hayan sido descargadas desde Internet, para luego poder utilizarlas en invocaciones posteriores.
s
La nueva clase 9LHZSRUW'9LVXDO' nos permitirá incorporar elementos interactivos 2D dentro de objetos 3D.
s
Se incorporaron tres nuevas clases para realizar operaciones de transformación en objetos 3D y 2D:
–
*HQHUDO7UDQVIRUP': Para realizar transformaciones entre objetos 3D. *HQHUDO7UDQVIRUP'7R': Transformaciones de objetos 2D a 3D.
–
*HQHUDO7UDQVIRUP'7R': Transformaciones de objetos 3D a 2D.
–
Las clases Binding y MultiBinding incorporan dos propiedades del tipo booleanas para determinar si son incluidos, o no, objetos de reglas de validación de excepciones o de errores. Estas nuevas propiedades son: ValidatesOnDataError: Si es verdadero, incorpora el objeto de regla de validación de errores 'DWD(UURU9DOLGDWLRQ5XOH. 9DOLGDWHV2Q([FHSWLRQV: Si es verdadero, incorpora el objeto ([FHSWLRQ9DOLGDWLRQ5XOH. Para mejorar la usabilidad de nuestras aplicaciones WPF, se han realizado mejoras sustanciales relacionadas con la performance: s
Para determinados escenarios, controles como ListView gozan de un nuevo beneficio llamado virtualización de la interface de , donde sólo se almacenan en memoria los elementos visibles, difiriendo cálculos computacionales que son necesarios para la representación de aquellos que no se encuentran visibles.
s
Para objetos que hereden de ItemsControl y tengan habilitado la virtualización, pueden utilizar una característica de optimización, denominada reciclaje de contenedores, para evitar la carga del proceso de creación y destrucción de contenedores y, así, poder reutilizarlos mientras el se desplaza sobre ellos.
s
Cuando nos desplazamos utilizando el scroll, la vista es actualizada constantemente. Si quisiéramos que sólo se actualice la pantalla cuan-
Visual Studio - Firtman, Natale
Alfaomega
84
2- .NET Framework 3.5
do el suelta la barra de desplazamiento, podemos asignarle el valor true a la propiedad ,V'HIHUUHG6FUROOLQJ(QDEOH de cualquier control que tenga un ScrollViewer. Novedades desde Visual Studio 2008 SP 1 En el de propiedades disponemos ahora de dos opciones básicas, pero que en la primera versión de Visual Studio 2008 no las teníamos: Los botones de eventos y los botones para ordenar las propiedades por categorías o alfabéticamente. Si hacemos doble clic sobre alguno de los eventos, su comportamiento será el esperado, creando el código correspondiente en el code-behind. Estas incorporaciones quizá parezcan algo obvio, pero eran bastante solicitadas por la comunidad de desarrolladores. Una forma de acceder más rápidamente a la ventana del Esquema del Documento, es mediante un nuevo ícono situado en la parte inferior izquierda del diseñador. Esta ventana nos muestra una estructura jerárquica de los elementos de un documento XAML, al igual que su previsualización.
Fig. 2-26. Nuevamente los botones clásicos de propiedades y eventos. Alfaomega
Visual Studio - Firtman, Natale
Windows Presentation Foundation
85
Fig. 2-27. Accediendo a la ventana del Esquema del Documento.
Con la intención de mejorar la distribución de nuestros componentes sobre el diseñador, al arrastrar un elemento el asistente utilizará para los márgenes el marco del contenedor sobre el cual se sitúa el componente en cuestión. La figura 2-28 nos muestra esto.
Fig. 2-28. Vemos cómo los márgenes son contemplados desde el marco del contenedor del elemento. Visual Studio - Firtman, Natale
Alfaomega
86
2- .NET Framework 3.5
Otras cuestiones relacionadas con el código serán encontradas desde el Service Pack 1 de Visual Studio 2008. Al momento de refactorizar, por ejemplo, la propiedad nombre de algún elemento, dicha modificación será realizada tanto en el code-behind como en el documento XAML. Supongamos que nos posicionamos sobre el nombre de un elemento y hacemos clic en ,UDGH¿QLFLyQ desde el menú contextual; automáticamente, Visual Studio nos mostrará el código declarativo en la ventana XAML.
Ejemplo de aplicación WPF Actualmente, hay un sinnúmero de aplicaciones que aprovechan todo lo rico del desarrollo de interfaces de mediante Windows Presentation Foundation. Para citar un ejemplo, la Biblioteca Británica –la más rica del mundo en cuanto a contenidos, información y libros que alberga en su historial– posee una aplicación XBAP, conocida como Sistema de Giro de Páginas (Turning Pages System), que permite a una persona recorrer en forma on-line una cantidad de libros que sería imposible acceder en forma personal. Esta aplicación está desarrollada en WPF y su interface es realmente impactante; además, la usabilidad que propone es fascinante: Podemos girar el libro, realizar el zoom de gráficos o escritos, etc. Para acceder a ella hay que ingresar a www.bl.uk y entrar a la galería on-line de libros. En la figura 2-29 podemos ver cómo es la interface del sistema.
Fig. 2-29. Navegando por un libro de la Biblioteca Británica. Alfaomega
Visual Studio - Firtman, Natale
Windows Presentation Foundation
87
Otro ejemplo, pero de una solución independiente, es el lector de noticias del The New York Times. Esta aplicación es descargada a la PC e instalada localmente; provee de una pantalla desde la cual se podrán leer las noticias, reportajes, entrevistas y videos actualizados vía Internet en forma automática. Su look and feel está muy bien diseñado y es sencillo de utilizar, con controles ricos y una sensación agradable para el . El mismo puede ser descargado desde el sitio ZZZQ\WLPHVFRP, previa registración.
Visual Studio - Firtman, Natale
Alfaomega
Introducción
C# 3.0
89
3
Introducción Quizá, a estas alturas ya todos conozcan y trabajen con C#, independientemente de su versión. Para aquellos que están programando en otros lenguajes, o para quienes sí lo están haciendo con C# pero no lo saben, aquí vamos a repasar un poco su historia y su origen. C# (se pronuncia “si Sharp” en español) es un lenguaje de programación orientado a objetos, desarrollado por Microsoft en el año 2000 como parte de su estrategia del framework .NET, y que luego fue normalizado por la ISO (ISO/ IEC 23270:2006) y el ECMA (European Computer Manufacturers Association) bajo la norma ECMA-334 –la cual establece la forma y la interpretación de los aplicativos escritos en este lenguaje, entre otras cosas–. Para aquellos que realmente estén interesados en profundizar más sobre este tema, pueden acceder a http://www.ecma-internacional.org/publications/standards/Ecma-334.htm, donde encontrarán información muy valiosa al respecto. C# está íntimamente relacionado con otros tres lenguajes: C, C++ y Java. De hecho, quienes vengan del mundo de alguno de ellos se van a sentir muy familiarizados y cómodos, permitiéndoles hacer el porting de forma mucha más rápida y sencilla.
La evolución en C# El mundo avanza de forma muy vertiginosa y la tecnología y los lenguajes no se podían quedar atrás. Cada día necesitamos mejorar el rendimiento de las aplicaVisual Studio - Firtman, Natale
Alfaomega
90
3- C# 3.0
ciones, reducir la cantidad de líneas de código que escribimos, mejorar la calidad de nuestro producto, brindar mayores servicios que estén a la altura de los requerimientos y, como si fuera poco, reducir los costos en el desarrollo. En el 2005 Microsoft liberó la versión 2.0 de C#, que incluía varias e importantes mejoras, como por ejemplo, tipos genéricos, iteradores, métodos anónimos y tipos parciales, entre otros. Es decir que por aquellos tiempos teníamos la nueva IDE de desarrollo Visual Studio 2005, el .NET Framework 2.0 y el nuevo compilador de C# con todas esas funcionalidades, para poder construir soluciones mucho más ricas y potentes. Era todo un desafío y la comunidad de desarrolladores se mostró muy conforme con los resultados, dado que ya se empezaban a ver grandes cambios y, como la historia cuenta, quedaba mucho más por venir.
Características incorporadas en .NET Framework 2.0 Antes de seguir avanzando, hagamos un repaso general de algunas de las características que fueron incorporadas en la versión 2.0, gracias a la adopción que tuvo este lenguaje en la comunidad de desarrolladores y al impulso que le dio Microsoft. En muchos ejemplos, utilizaremos una excelente herramienta, llamada .NET Reflector, de la empresa RedGate. Es posible descargarla de forma gratuita desde la dirección ZZZUHGJDWHFRPSURGXFWVUHÀHFWRU. .NET Reflector es de gran ayuda para analizar el código de los ensamblados, el cual utilizaremos para comprender el funcionamiento de muchas de las características que veremos en este capítulo. En la figura 3-1 vemos el sitio Web donde, además, podremos descargar adicionales muy interesantes.
Genéricos Quizá para muchos, los genéricos (o generics en inglés) sean la característica más importante que fue agregada en la versión 2.0. Si miramos el código que generamos, muchas veces nos vamos a encontrar con varios cast. La mala utilización de éstos provoca degradación de performance, así que debemos cuidar de no realizar una mala programación para no obtener alguna excepción, que muchas veces nos limitan al momento de querer reutilizar nuestro código. Generics viene a solucionar un poco todo esto. Alfaomega
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
91
Fig. 3-1. Página de RedGate para descargar .NET Reflector.
Los genéricos nos van a permitir escribir, de forma fuertemente tipada, colecciones, clases y métodos, entre otros. Disponemos ahora de un nuevo espacio de nombres al que denominamos: System.Collections.Generic, que contiene varias interfaces y clases basadas en tipos genéricos para poder generar nuestras colecciones inflexibles. En el espacio de nombres System tenemos IComparable
para crear métodos de comparación de un tipo definido. Como podemos observar, hay una nueva notación:
. Éste es el tipo que vamos a utilizar como parámetro, por ejemplo int o alguna otra clase que puede ser también definida por nosotros. Utilizar la letra T es simplemente una convención, no quiere decir que siempre tengamos que escribirla, bien podríamos reemplazarla por la letra U si quisiéramos. De hecho, vamos a ver más adelante que, en ciertas circunstancias, es necesario identificar el argumento mediante diferentes letras. Veamos algo de código que seguramente va a echar un poco de luz a este tema tan importante. En las siguientes líneas vamos a utilizar el tradicional ArrayList para definir una lista de nombres de personas:
Visual Studio - Firtman, Natale
Alfaomega
92
3- C# 3.0
using System; using System.Collections; using System.Text; 'H¿QLPRVQXHVWUR$UUD\/LVW ArrayList listaNombres = new ArrayList(); //Comenzamos a agregar algunos nombres listaNombres.Add("Leonardo Natale"); listaNombres.Add("Maximiliano Firtman"); // En tiempo de compilación, no obtenemos ningún error. listaNombres.Add(10); foreach (object nombre in listaNombres) { Console.WriteLine((string)nombre); } Console.Read();
Como podemos observar en este código, además de agregar dos nombres de personas, hemos agregado el valor 10, el cual al momento de compilar no devolvió ningún error. Cuando se ejecute la aplicación, obtendremos una excepción del tipo InvalidCastException, dado que ésta no puede convertir un tipo de datos System.Int32 a System.String. En realidad, en este caso nosotros no deberíamos agregar un valor del tipo int, dado que lo que estamos completando es una lista de nombres, pero como ArrayList espera un valor del tipo object, al momento de compilar no obtenemos ninguna advertencia, dado que todo hereda de ese tipo de dato. Obviamente, esto es un ejemplo muy sencillo de cómo una línea de código puede llegar a poner nuestro sistema inestable. Otra cosa que quizá hayan notado, es que estamos utilizando el espacio de nombres System.Collections, que es donde tenemos disponible ArrayList, en lugar de System.Collections.Generic. Veamos ahora cómo podríamos escribir el mismo ejemplo pero utilizando Generics: using System; using System.Collections.Generic; using System.Text; 'H¿QLPRVQXHVWUR/LVWIXHUWHPHQWHWLSDGRSDUDTXHDFHSWH datos del tipo string
Alfaomega
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
93
// List<string> listaNombresGenerics = new List<string>(); listaNombresGenerics.Add("Leonardo Natale"); listaNombresGenerics.Add("Maximiliano Firtman"); // Error en tiempo de compilación listaNombresGenerics.Add(10); foreach (object nombre in listaNombresGenerics) { 1RWHPRVTXHQRHVQHFHVDULRUHDOL]DUXQFDVW Console.WriteLine(nombre); } Console.Read();
De esta manera, al compilar obtendremos un error que nos informa que estamos insertando en la lista un tipo de dato (en este caso int) para el cual la lista no fue definida. Lo interesante de este ejemplo no es solamente que podemos evitar el hecho de recibir una excepción cuando nuestro programa está ejecutándose, sino que nos ahorramos de tener que realizar conversiones entre tipos de datos y conversiones boxing y unboxing (desde y hacia el tipo base Object), lo cual significa una mejora de rendimiento –más aún, si nos encontramos en algún escenario donde debemos recorrer extensas colecciones–. Visual Studio –desde la versión 2005– al agregar un nuevo proyecto o una nueva clase nos agrega, también, la siguiente línea: using System.Collections.Generic; Es decir, no debemos hacer nada más para utilizar en nuestra solución Generics.
Ahora bien, como dijimos anteriormente, podemos tomar ventaja de los genéricos cuando creamos nuestros métodos y clases. Veamos algunos ejemplos. Nuestra primera clase genérica En el siguiente código vamos a generar un proyecto de consola y agregaremos una nueva clase (ClaseGenerica.cs), que posee una propiedad, llamada MyProperty, que puede adoptar diversos tipos de argumentos en función del valor que se le asigne a
:
Visual Studio - Firtman, Natale
Alfaomega
94
3- C# 3.0
using System; using System.Collections.Generic; using System.Text; namespace EjemplosGenerics { class ClaseGenerica
{ private T myVar; public T MyProperty { get { return myVar; } set { myVar = value; } } public string Tipo { get { return typeof(T).ToString(); } } } }
Para que nos ayude en nuestro estudio, definimos una propiedad denominada Tipo, que nos devolverá el tipo de argumento que posee
mediante la instrucción typeof. Utilizando ClaseGenérica Ya tenemos generada nuestra clase genérica; observemos un ejemplo de cómo utilizarla: class Program { static void Main(string[] args) { // Instanciamos nuestra clase y como tipo de valor le // pasamos un string ClaseGenerica<string> claseString = new ClaseGenerica<string>(); // Instanciamos nuestra clase y como tipo de valor OHSDVDPRVXQÀRDW &ODVH*HQHULFDÀRDW!FODVH)ORDW QHZ
Alfaomega
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
95
&ODVH*HQHULFDÀRDW! // Establecemos valores para nuestras propiedades claseString.MyProperty = "Esto es una cadena de texto"; claseFloat.MyProperty = 9.18f; // Enviamos a Consola valores y tipos Console.WriteLine("Valor de MyProperty de claseString es: {0}", claseString.MyProperty); Console.WriteLine("El tipo de
es: {0}", claseString.Tipo); Console.Write("\n"); Console.WriteLine("Valor de MyProperty de claseFloat es: {0}", claseFloat.MyProperty); Console.WriteLine("El tipo de
es: {0}", claseFloat.Tipo); Console.Read(); }
}
Ejecutemos la aplicación y observemos el resultado (figura 3.2). Cuando instanciamos nuestras clases, simplemente reemplazamos
con el tipo de argumento en cuestión. En el ejemplo de arriba trabajamos con string: ClaseGenerica<string> claseString = new ClaseGenerica<string>();
Fig. 3-2. Resultado de utilizar nuestra clase genérica.
Visual Studio - Firtman, Natale
Alfaomega
96
3- C# 3.0
Y luego con float: &ODVH*HQHULFDÀRDW!FODVH)ORDW QHZ&ODVH*HQHULFDÀRDW!
De esta manera, reutilizamos nuestro código simplemente asignando un tipo de argumento. Y no sólo eso: Además, estamos proveyendo cierta seguridad a nuestra aplicación, dado que si hubiéramos escrito: FODVH)ORDW0\3URSHUW\ 8QWH[WRFXDOTXLHUD
al momento de compilar obtendríamos un error diciéndonos que no podemos convertir de un tipo string a un ÀRDW. Creando métodos genéricos Es posible crear métodos genéricos, tanto en clases genéricas como no genéricas, y pueden aceptar diversos valores en la firma del mismo: void MetodoGenerico(T var1, ref U var2, V var3) { // Cuerpo del método } T OtroMetodoGenerico
(T var1, T var2) { // Cuerpo del método }
Veamos un ejemplo: class MiClase { // Creamos nuestro método genérico public T ObtenerMayor
(T var1, T var2) where T : IComparable { if (var1.CompareTo(var2) > 0) return var1; return var2; } } //Utilizando nuestro método class Program { static void Main(string[] args) {
Alfaomega
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
97
MiClase c = new MiClase(); Console.WriteLine(c.ObtenerMayor(-2, -3)); Console.WriteLine(c.ObtenerMayor(109, 474)); Console.WriteLine(c.ObtenerMayor(23.5, 87.1)); Console.WriteLine(c.ObtenerMayor("Flavio", "Pablo")); Console.Read(); } }
Y el resultado será: -2 474 87,1 Pablo
Es importante que notemos que no hemos especificado el tipo de argumento al momento de llamar al método; esto se debe a que el compilador tiene la capacidad de poder inferir el tipo que se le está enviando. Otro detalle del código anterior es la aparición de la cláusula where: public T ObtenerMayor
(T var1,T var2) where T:IComparable
Mediante la utilización de esta cláusula, lo que hacemos es definir una limitación o restricción para el tipo de parámetro T y, en este caso, estamos especificando que debe implementar la interface IComparable. Por consiguiente, si al llamar al método ObtenerMayor, el tipo de argumento de T que estamos fijando no implementa dicha interface, el compilador nos devolverá un error. Estamos ante otra forma de asegurar nuestro código. Generics es mucho más extenso que lo que aquí vimos; es realmente muy útil y nos va a facilitar mucho nuestra codificación. En el sitio de MSDN de Microsoft vamos a encontrar muchos más ejemplos e información relevante que fueron agregados sobre el tema. Es imprescindible interiorizarse para sacar partido de Generics.
Iteradores Es muy probable que muchos ya hayan trabajado con colecciones de objetos y habrán notado que a veces en determinados escenarios, al querer recorrer y acceder a los elementos del mismo, la implementación de la interface IEnumerator se tornaba complicada, y hasta a veces tediosa.
Visual Studio - Firtman, Natale
Alfaomega
98
3- C# 3.0
Debíamos implementar métodos como Reset o MoveNext para que todo funcionara correctamente y, mediante la propiedad Current, accedíamos a nuestros ítems. Luego, mediante el método GetEnumerator, proporcionado por la Interface IEnumerable, accedíamos a un enumerador que iteraba sobre la colección. Para facilitar esto, en la versión C# 2.0 se agregaron los iteradores, quienes nos van a permitir trabajar con clases iterables de forma muy sencilla, sin tener que implementar explícitamente IEnumerator en su totalidad. Qué son Un iterador puede ser un método, un operador o parte de un bloque de get{}, que nos va a habilitar para trabajar con clases o structs de manera muy sencilla y eficaz sin tener que codificar tantas líneas, como lo hacíamos cuando programábamos en .NET Framework 1.1, a través de sentencias foreach y retornándonos de forma ordenada una secuencia de valores del mismo tipo. Pero para entender un poco mejor qué son y cómo funcionan, veamos algo de código. Primer ejemplo: *HQHUDPRVQXHVWUDFODVHTXHFRQWLHQHXQDFROHFFLyQ // de marcas de autos class MarcaAutos { string[] arrMarcas = { "Subaru", "Audi", "BMW", "Porsche", "Ferrari", "Mercedes-Benz" }; public System.Collections.IEnumerator GetEnumerator() { for (int i = 0; i < arrMarcas.Length; i++) { yield return arrMarcas[i]; } } } // Nuestra clase Program donde vamos a iterar class Program { static void Main(string[] args) { MarcaAutos mAutos = new MarcaAutos(); foreach (string marca in mAutos)
Alfaomega
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
La salida de esta aplicación de consola sería: Subaru Audi BMW Porsche Ferrari Mercedes-Benz
En la clase MarcaAutos nos vamos a encontrar con la implementación del método GetEnumerator de la interface IEnumerable, que devolverá, mediante la instrucción yield return, nuestro ítem. Luego, en Program.cs, foreach encapsulará la llamada por defecto a GetEnumerator y obtendremos los valores del enumerador. El ejemplo anterior podría ser escrito también de la siguiente forma, obteniendo la misma salida: class MarcaAutos { public System.Collections.IEnumerator GetEnumerator() { yield return "Subaru"; yield return "Audi"; yield return "BMW"; yield return "Porsche"; yield return "Ferrari"; yield return "Mercedez-Benz"; } } class Program { static void Main(string[] args) { MarcaAutos mAutos = new MarcaAutos(); foreach (string marca in mAutos) { Console.Write(marca + " "); } Console.Read(); } }
Visual Studio - Firtman, Natale
Alfaomega
100
3- C# 3.0
La palabra yield tiene otra opción: yield break. Esto nos va a permitir finalizar la enumeración que se está realizando ante, por ejemplo, una condición determinada: class MarcaAutos { string[] arrMarcas = { "Subaru", "Audi", "BMW", "Porsche", "Ferrari", "Mercedes-Benz" }; public System.Collections.IEnumerator GetEnumerator() { for (int i = 0; i < arrMarcas.Length; i++) { if (i == 3) { yield break; }; yield return arrMarcas[i]; } } }
La salida de consola sería: Subaru Audi BMW Si queremos utilizar otro nombre para el método GetEnumerator, podemos personalizarlo como se muestra a continuación: class Numeros { int[] arrNumeros = { 2, 5, 7, 9, 10, 8 }; // Reemplazamos IEnumerator por IEnumerable public System.Collections.IEnumerable ObtenerNumeros() { for (int i = 0; i < arrNumeros.Length; i++) { yield return arrNumeros[i]; } } } class Program { static void Main(string[] args) { Numeros nros = new Numeros();
Alfaomega
/ODPDPRVDOPpWRGRGH¿QLGR foreach (int nro in nros.ObtenerNumeros()) {
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
También es importante tener en cuenta algunas restricciones cuando escribimos nuestros bloques de código en los iteradores: s
Un cláusula yield no puede estar dentro de un método anónimo (veremos más adelante de qué se trata).
s
Un método iterador debe retornar siempre IEnumerable, IEnumerator o sus correspondientes versiones de genéricos.
s
No puede poseer parámetros del tipo ref u out.
s
Yield no puede figurar dentro de un ¿QDOO\ de un try/catch.
s
Cuando es utilizado con expresiones, yield return no puede estar dentro de un bloque catch o dentro de un try que posee uno o más catch.
Iteradores + Genéricos: La unión hace la fuerza Hasta aquí hemos visto cómo construir nuestros bloques iteradores con clases no genéricas, utilizando para ello las interfaces que teníamos en System.Collections. La buena noticia es que disponemos también, dentro del ensamblado System. Collections.Generic, las interfaces IEnumerator
e IEnumerable
para poder trabajar con clases genéricas. Veamos cómo podemos reescribir los ejemplos anteriores utilizando clases genéricas e iteradores: // Generamos nuestra clase genérica class ClaseGenerica
{ T[] arrDatos; public ClaseGenerica(T[] datos) { arrDatos = datos; } public IEnumerator
GetEnumerator() { foreach (T dato in arrDatos) yield return dato;
Visual Studio - Firtman, Natale
Alfaomega
102
3- C# 3.0
} } // La clase Program donde testearemos ClaseGenerica class Program { static void Main(string[] args) { int[] arrNumeros = { 2, 5, 7, 9, 10, 8 }; string[] arrMarcas = { "Subaru", "Audi", "BMW", "Porsche", "Ferrari", "Mercedes-Benz" }; ClaseGenerica
genInt = new ClaseGenerica
(arrNumeros); ClaseGenerica<string> genString = new ClaseGenerica<string>(arrMarcas); // Enviamos a consola el arreglo de números foreach (int nro in genInt) { System.Console.Write(nro + " "); } Console.Write("\n"); // Enviamos a consola el arreglo de marcas foreach (string marca in genString) { System.Console.Write(marca + " "); } Console.Read(); } }
Métodos Anónimos Hasta la versión anterior de C#, cuando trabajábamos con delegados hacíamos una especie de receta, donde uno de los puntos era definir el método que estábamos pasando como parámetro. Veamos un ejemplo sencillo: namespace MetodoAnonimo { 'H¿QLPRVQXHVWURGHOHJDGR delegate void MiDelegado(string texto); class Program { static void Main(string[] args) {
Alfaomega
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
103
// Enviamos como parámetro un método MiDelegado pruebaDel = new MiDelegado(Reemplazar); pruebaDel("Reemplazar espacios"); Console.Read(); } public static void Reemplazar(string a) { Console.WriteLine(a.Replace(' ','*')); } } }
Otra de las características que fueron incorporadas en la segunda versión del lenguaje son los Métodos Anónimos. Éstos nos van a permitir pasar una porción de código, en lugar del nombre de un método, como parámetro de un delegado. De esta manera, lo que haremos es reducir la sobrecarga de tener que escribir métodos exclusivamente para los delegados. ¿Arte de magia? No. Como varias de las características que veremos en este capítulo, el compilador es quien juega un papel protagónico, es un aliado de los programadores y facilita las tareas.Observemos como quedaría el ejemplo anterior, pero utilizando métodos genéricos: namespace MetodoAnonimo { 'H¿QLPRVQXHVWURGHOHJDGR delegate void MiDelegado(string texto);
class Program { static void Main(string[] args) { 'H¿QLPRVXQPpWRGRDQyQLPR MiDelegado pruebaDel = delegate(string a) { (OEORTXHGHFyGLJRGHOPpWRGR5HHPSOD]DU Console.WriteLine(a.Replace(' ','*')); }; pruebaDel("Reemplazar espacios en texto"); Console.Read(); } }
}
Visual Studio - Firtman, Natale
Alfaomega
104
3- C# 3.0
Algunas consideraciones s
Las variables locales que utilizamos dentro de un método anónimo son llamadas variables externas o capturadas, donde su ciclo de vida es igual al del delegado, es decir que cuando se cumplan las condiciones necesarias y actúe el recolector de basura, las mismas serán removidas.
s
Es necesario tener cautela cuando compartimos variables externas entre dos o más delegados, dado que es posible que obtengamos resultados inesperados.
s
El alcance de un método anónimo se encuentra limitado en su bloque de código.
s
No es posible utilizar variables ref u out con variables capturadas.
s
No se puede tener a código no seguro dentro del bloque.
Incorporaciones en C# 3.0 Métodos Parciales Los métodos parciales permiten definir un prototipo de método pero que podrá o no implementarse en la clase. No debe confundirse con la herencia o con una interface, dado que los métodos parciales son una implementación de precompilador. Si el compilador detecta que el método parcial se definió, pero no se implementó en ningún archivo de la clase (recordemos el concepto de clases parciales anteriormente visto), entonces elimina todas las llamadas a ese método, como si nunca hubieran existido. Si en cambio el método se definió, entonces se deja la invocación. Recordemos que hablamos siempre de la misma clase y no de la herencia o polimorfismo. ¿Qué objetivo tienen? Pueden ser útiles para métodos de depuración de código, para evitar utilizar interfaces y obligar a un a implementar métodos vacíos y, también, se utiliza para algunos de los nuevos frameworks, como ser el LINQ T o SQL, que veremos luego en este libro. El método sólo puede ser void, no debe contener código, debe incluir la cláusula partial en su prototipo –y no incluir ninguna cláusula en la implementación si existiera–, debe ser privado y puede ser de instancia o de clase (static). En un archivo podemos tener: public partial class Clase1 { public partial void Debug (string Txt); }
Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en C# 3.0
105
Y, opcionalmente, en otro archivo parcial de la misma clase podría estar la implementación: public partial class Clase1 { public partial void Debug (string Txt) { // Codigo a implementar } }
Cualquiera de las llamadas a ese método existirá en el compilado solamente si el método fue implementado. Si no, se evitan y no se compilan.
Tipos Implícitos La nueva inclusión de la instrucción var nos permitirá trabajar con variables cuyo tipo no es definido explícitamente –tal como lo hacíamos hasta ahora–, sino que el compilador será el responsable de deducir el tipo de la misma en función de lo que hayamos definido en el lado derecho de la inicialización. Así definíamos nuestras variables: namespace TiposImplicitos { class Program { static void Main(string[] args) { // Asignación explícita int valor = 1; string nombre = "Leonardo"; ÀRDWQXPHUR I Array arrProvincias = new string[] { "Buenos Aires", "San Juan" , "Mendoza"}; } } }
El mismo ejemplo con tipos implícitos: namespace TiposImplicitos { class Program { static void Main(string[] args) Visual Studio - Firtman, Natale
Alfaomega
106
3- C# 3.0
{ // Asignación implícita var valor = 1; var nombre = "Leonardo"; var numero = 3.5f; var arrProvincias = new string[] { "Buenos Aires", "San Juan" , "Mendoza"}; Console.WriteLine("Variable: valor.GetType().Name); Console.WriteLine("Variable: nombre.GetType().Name); Console.WriteLine("Variable: numero.GetType().Name); Console.WriteLine("Variable: {0}", arrProvincias.GetType().Name);
valor - Tipo: {0}", nombre - Tipo: {0}", numero - Tipo: {0}", arrProvincias - Tipo:
Console.Read(); } } }
La respuesta en la consola es lo que tenemos en la figura 3-3.
Fig. 3-3. El compilador infiere el tipo de las variables.
Veamos el código generado mediante la herramienta .NET Reflector (figura 3-4). Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en C# 3.0
107
Fig. 3-4. Tipos implícitos en el desensamblador.
En la figura 3-4 notamos cómo el compilador deduce los tipos que no especificamos al utilizar la instrucción var, que son los mismos que obtuvimos en la pantalla de consola. Supongamos que definimos una variable que, implícitamente, es del tipo ÀRDW y luego le asignamos un texto; al momento de compilar obtendríamos un error: var miVariable = 4.8f PL9DULDEOH &XDOTXLHUWH[WR(UURU
Como miVariable es del tipo ÀRDW, el compilador nos devolvería un error informándonos que, correctamente, no puede convertir un tipo string a ÀRDW. Es importante tener en cuenta que: s
Siempre debemos inicializar las variables:
YDUPL9DULDEOH(UURU
s
Dada la restricción anterior, se deben inicializar en la misma instrucción:
YDUPL9DULDEOH(UURU PL9DULDEOH (UURU
s
No pueden establecerse como nulos:
YDUPL9DULDEOH QXOO(UURU
s
No está permitido inicializar más de una variable en la misma instrucción:
YDUPL9DULDEOHPL9DULDEOHPL9DULDEOH (UURU
s
No es posible utilizar como inicializador un objeto o una colección:
YDUPL9DULDEOH ^3DEOR5DSKDHO0LFKHODQJHOR`(UURU Visual Studio - Firtman, Natale
Alfaomega
108
3- C# 3.0
Inicializadores de objetos Una nueva funcionalidad ha sido agregada para ayudarnos al momento de tener que asignar un valor a campos, o propiedades accesibles, cuando creamos un objeto. Miremos cómo hacíamos antes para pasar algunos valores: class Producto { public Producto(){} private int _codigo; private string _nombre; private string _almacen; public int Codigo { get { return _codigo; } set { _codigo = value; } } public string Nombre { get { return _nombre; } set { _nombre= value; } } public string Almacen { get { return _almacen; } set { _almacen = value; } } } class Program { static void Main(string[] args) { Producto prod = new Producto(); //Asignamos un valor a cada propiedad prod.Codigo = 3005; prod.Nombre = "Aceite Vegetal"; prod.Almacen = "San Martín"; Console.WriteLine("Código: " + prod.Codigo); Console.WriteLine("Nombre: " + prod.Nombre); Console.WriteLine("Almacen: " + prod.Almacen); Console.Read(); } }
Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en C# 3.0
109
Otra forma era pasar los valores por parámetro en el constructor: class Producto { public Producto(){} public Producto(int codigo, string nombre) { this.Codigo = codigo; this.Nombre = nombre; } private int _codigo; private string _nombre; private string _almacen; public int Codigo { get { return _codigo; } set { _codigo = value; } } public string Nombre { get { return _nombre; } set { _nombre= value; } } public string Almacen { get { return _almacen; } set { _almacen = value; } } }
Hasta aquí nada nuevo. Lo interesante es que mediante inicializadores de objetos vamos a poder definir nuestros valores en una misma instancia creacional, sin tener que invocar a su método en forma explícita, permitiéndonos escribir líneas mucho más claras. Reutilizando el mismo ejemplo anterior, quedaría así: class Producto { // Propiedades autoimplementadas public int Codigo { get; set; } public string Nombre { get; set; } public string Almacen { get; set; }
} class Program { static void Main(string[] args) { Producto prod = new Producto { Codigo = 3005, Nombre = "Aceite Vegetal", Almacen = "San Martín" };
En el ejemplo anterior se puede apreciar que, en lugar de escribir las propiedades con la “vieja receta”, se utilizaron propiedades auto implementadas. Esto no modifica la funcionalidad de la clase Producto, pero es otra modalidad de escribirlas que, quizá, sería bueno ir incorporando en el día a día. Obviamente, también es posible mantener la opción de utilizar una combinación de ambas, es decir, pasar algunos valores por parámetros en el constructor y otros mediante inicializadores de objetos (los que enviamos entre corchetes). Es importante aclarar que, empleando una mezcla de ambas, lo primero que se va a ejecutar será el constructor y luego serán enviados los valores de las propiedades en el orden en que fueron especificadas:
Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en C# 3.0
111
class Producto { public Producto(int codigo) { this.Codigo = codigo; } // Propiedades autoimplementadas public int Codigo { get; set; } public string Nombre { get; set; } public string Almacen { get; set; }
} class Program { static void Main(string[] args) { Producto prod = new Producto(3005) { Nombre = "Aceite Vegetal", Almacen = "San Martín" };
Analicemos otro escenario, que al principio puede parecer complicado pero que no lo es. Supongamos que definimos una clase (VSHFL¿FDFLyQ, que posee algunas propiedades y queremos valorizarlas desde una propiedad de un objeto del tipo Producto, por ejemplo: FODVV(VSHFL¿FDFLRQ { public bool EsImportado { get; set; } public int Lote { get; set; } } class Producto { (VSHFL¿FDFLRQVSHFW QHZ(VSHFL¿FDFLRQ Visual Studio - Firtman, Natale
Alfaomega
112
3- C# 3.0
8QDSURSLHGDGGHOWLSR(VSHFL¿FDFLRQ SXEOLF(VSHFL¿FDFLRQ6SHFW^JHW^UHWXUQVSHFW`` // Propiedades autoimplementadas public int Codigo { get; set; } public string Nombre { get; set; } public string Almacen { get; set; } } class Program { static void Main(string[] args) { Producto prod = new Producto() { Codigo = 900, Nombre = "Aceite Girasol", Almacen = "Pilar", Spect = { EsImportado= true, Lote = 4 } }; Console.WriteLine("Código: " + prod.Codigo); Console.WriteLine("Nombre: " + prod.Nombre); Console.WriteLine("Almacen: " + prod.Almacen); Console.WriteLine("Es Importado: " + prod.Spect.EsImportado); Console.WriteLine("Lote: " + prod.Spect.Lote);
Y en la figura 3-5 podemos apreciar cómo el IntelliSense de Visual Studio nos va aportando información sobre los tipos de datos y nombres de las propiedades definidas de nuestra clase Producto, así como también las escritas en (VSHFL¿FDFLRQFV. Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en C# 3.0
113
Fig. 3-5. IntelliSense de Visual Studio utilizando inicializadores de objetos.
Trabajando con colecciones Podremos explotar también esta característica en nuestras colecciones que implementen IEnumerable o IEnumerable
, mediante los inicializadores de objetos, tal como lo hemos hecho hasta aquí. class Program { static void Main(string[] args) { Producto[] maestroprods = new Producto[] { new Producto { Codigo=1000,Nombre="Aceto", Almacen="CABA", Spect={EsImportado=false,Lote=5} }, new Producto { Codigo=5009,Nombre="Aceite Oliva", Almacen="Pilar", Spect={EsImportado=true,Lote=90} }, }; foreach (Producto p in maestroprods) { Console.WriteLine("Código: " + p.Codigo); Console.WriteLine("Nombre: " + p.Nombre); Console.WriteLine("Almacen: " + p.Almacen); Console.WriteLine("Es Importado: " + p.Spect.EsImportado); Console.WriteLine("Lote: " + p.Spect.Lote);
Visual Studio - Firtman, Natale
Alfaomega
114
3- C# 3.0
} Console.Read(); } }
Ejecutando esta aplicación de consola, veríamos impreso en la pantalla: Código: 1000 Nombre: Aceto Almacen: CABA Es Importado: False Lote: 5 Código: 5009 Nombre: Aceite Oliva Almacen: Pilar Es Importado: True Lote: 90
Veamos ahora un ejemplo con Dictionary
: Dictionary<string, bool> listaAsistencia = new Dictionary<string, bool> { {"Pablo",true},{"Diego",false},{"Fernando",true} };
De esta forma, logramos flexibilidad a la hora de trabajar con colecciones, ahorrándonos líneas de programación y permitiéndonos reutilizar y aprovechar al máximo todas las bondades de genéricos e inicializadores de objetos.
Tipos Anónimos Ahora que hemos visto cómo funcionan los tipos implícitos y los inicializadores de objetos, vamos a estudiar otra nueva característica de C# 3.0: Los tipos anónimos, que se basan, principalmente, en las dos características anteriores. Esta nueva característica nos va a permitir crear propiedades de modo implícito y asignarles un valor, pero que serán de sólo lectura. Básicamente, el compilador crea una clase del tipo anónimo con propiedades de un tipo que él va a inferir en función de nuestra declaración. Pero veamos un sencillo ejemplo: Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en C# 3.0
115
class Program { static void Main(string[] args) { var celular = new { Marca = "Nokia", Modelo = "N95", CantVendida = 10, HayStock = true }; } }
Notemos el uso de la palabra var y de new. ¿Nos es familiar? Claro que sí, estamos utilizando por un lado tipos anónimos y luego los inicializadores de objetos, para asignarles valores a propiedades que serán generadas por el compilador on the fly. También, es posible notar que en el código anterior nunca creamos explícitamente ni una clase, ni un tipo de propiedad, ni el nombre de la misma, así como tampoco sus s (set y get). Recurramos a la herramienta .NET Reflector para observar algunas cosas interesantes que ocurren detrás de escena (figura 3-6).
Fig. 3-6. Tipo anónimo generado por el compilador. Visual Studio - Firtman, Natale
Alfaomega
116
3- C# 3.0
Lo primero que vamos a notar es el nombre del tipo anónimo que creó el compilador, en este caso “<>f__AnonymousType0”. El mismo es codificado en función del orden de las propiedades, sus tipos y cantidades que hayamos definido. Por deducción, podemos afirmar que cuando se altere alguna de estas condiciones, el compilador de C# generará n clases de n tipos según lo considere. Otro dato que tenemos son los campos privados con los nombres que especificamos entre los corchetes, así como también sus correspondientes propiedades. Como decíamos, estas propiedades son de sólo lectura; de hecho, si intentamos asignarles un valor luego de su inicialización, obtendremos un error en tiempo de compilación. Recomendamos que a las diversas conclusiones que podremos obtener, luego de analizar los resultados que nos brinda .NET Reflector, les dediquen unos minutos y naveguen en todo el árbol. Y para los curiosos, a través de ILDASM.exe tendrán disponible más información aún. A pesar de que a veces se torna complicado comprender lo que allí tenemos, es un punto interesante para indagar más sobre las cuestiones que ocurren al trabajar con tipos anónimos –que quizá no notemos–. Cuando es generada esta clase anónima, se sobrescriben tres métodos: Uno de ellos es ToString. En el ejemplo anterior el resultado sería (también lo podemos ver con .NET Reflector): public override string ToString() { StringBuilder builder = new StringBuilder(); builder.Append("{ Marca = "); builder.Append(this.<Marca>i Field); builder.Append(", Modelo = "); builder.Append(this.<Modelo>i Field); builder.Append(", CantVendida = "); builder.Append(this.
i Field); builder.Append(", HayStock = "); builder.Append(this.
i Field); builder.Append(" }"); return builder.ToString(); }
Para probarlo, agreguemos la siguiente línea y observemos la devolución: Console.Write(celular.ToString()); Console.Read();
Supongamos ahora que queremos saber la cantidad total de dinero obtenido por las ventas realizadas; entonces, basándonos en el ejemplo anterior, podríamos hacer: int precioNokia = 100; var celular = new { Marca = "Nokia", Modelo = "N95", CantVendida = 10, HayStock = true }; var totalNokia = new { celular.Marca, Acumulado = celular.CantVendida * precioNokia }; Console.Write("Marca: {0} Acumulado: ${1}", totalNokia.Marca, totalNokia.Acumulado); Console.Read();
Lo interesante es que, al escribir totalNokia, el IntelliSense nos propone, además de la propiedad Acumulado, la opción Marca. Al no haber asignado un nombre para esta última, por defecto lo hereda de nuestra variable anónima celular.
Métodos extensores Los métodos extensores nos brindarán la posibilidad de agregar métodos a tipos existentes, sin tener que derivar de ellos o modificar su implementación original. Muchas veces esto último no es posible, dado que quizá estemos interesados en expandir las funcionalidades de un tipo del .NET Framework o de componentes de terceros. Crear métodos extensores es tan sencillo y fácil como crear cualquier otro método de instancia, con algunas pequeñas diferencias que se han de tener en cuenta: s
Deben ser estáticos al igual que la clase que los contiene.
s
Tienen que tener al menos un parámetro.
s
El primer parámetro de la firma debe tener antepuesto la palabra this.
s
El primer parámetro no puede tener modificadores como ref u out.
s
Siempre deben ser definidos en una clase no genérica.
s
No pueden estar en una clase anidada.
Veamos un simple ejemplo, donde vamos a extender el tipo string: Visual Studio - Firtman, Natale
Alfaomega
118
3- C# 3.0
namespace Miempresa.Extensores { static class Extensores { public static bool TieneNumero(this string x) { Regex regularEx = new Regex("[0-9]"); if (regularEx.IsMatch(x)) return true; return false; } } }
Básicamente, lo que hacemos es generar nuestra clase estática Extensores y nuestro método, estático también, TieneNumero. El objetivo de TieneNumero es devolver un tipo booleano, evaluando si la cadena de texto que se le envía como parámetro posee o no algún carácter comprendido entre 0 y 9. Notemos la palabra reservada this delante del tipo que queremos extender, en nuestro caso string. Se recomienda que los métodos extensores se encuentren en otro espacio de nombres, para poder identificarlos fácilmente y mantener una jerarquía ordenada de ellos. En nuestro caso es Miempresa.Extensores, que deberemos importar a nuestro ambiente de trabajo mediante un using para poder utilizarlos. En el siguiente bloque de código lo utilizamos de la siguiente manera: // Importamos el espacio de nombres using Miempresa.Extensores; namespace MetodosExtensores { class Program { static void Main(string[] args) { 'H¿QLPRVDOJXQDVFDGHQDVGHSUXHED string strPruebaConNumeros = "Ab3cd0EfG7HIJ9k"; string strPruebaSinNumeros = "AbcdEfGHIJk"; Console.WriteLine(strPruebaConNumeros.TieneNumero()); Console.WriteLine(strPruebaSinNumeros.TieneNumero()); Console.WriteLine("P0ds983jkd83D".TieneNumero()); Console.Read(); } } }
Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en C# 3.0
119
Si ejecutamos este ejemplo, la respuesta sería: True False True
En la figura 3-7 podremos ver cómo el IntelliSense nos muestra el nuevo método estático generado y cómo lo distinguimos mediante un ícono nuevo.
Fig. 3-7. Métodos extensores definidos y su integración con el IntelliSense.
En ciertas circunstancias, nos encontramos ante una necesidad que no se resuelve, al menos de manera rápida y sencilla, con las funcionalidades que nos provee el .NET Framework. Y tal vez, aquella solución que implementamos para un proyecto la volvemos a codificar en otro, duplicando código y lidiando con problemas de escalabilidad y mantenimiento. En el ejemplo siguiente, agregamos una característica nueva a FileInfo (System.IO), que nos habilitará para copiar un archivo a un directorio destino, un directorio para resguardo y un parámetro para determinar si reemplazaremos o no el archivo en la carpeta de destino, en caso de tener uno con el mismo nombre: namespace Miempresa.Extensores { static class Extensores { public static void CopiarConDestinos(this System. IO.FileInfo archivo, string destArch, string destRes, bool reempplazar)
La utilización sería como la siguiente: namespace MetodosExtensores { class Program { static void Main(string[] args) { 6\VWHP,2)LOH,QIR¿OH QHZ6\VWHP IO.FileInfo(@"C:\Prueba.txt"); ¿OH&RSLDU&RQ'HVWLQRV#&?1XHYD&DUSHWD?3UXHEDW[W @"C:\CarpetaBackup\PruebaBackup.txt",true); } } }
Es importante aclarar que no debemos hacer un mal uso de los métodos extensores; verifiquemos primero si no disponemos de la funcionalidad que estamos buscando, ya implementada de forma original. Por ejemplo, no tendría sentido que agreguemos uno de estos métodos a string con la finalidad de retornar una cadena en mayúsculas, dado que ya se encuentra desarrollada: string.ToUpper. De todas formas, imaginemos por un momento que extendemos el tipo string con un método llamado Replace, que recibe dos parámetros –uno es un char que contiene el carácter que ha de ser reemplazado y el segundo es otro char con el dígito con el cual reemplazaremos– y que cuando vamos a Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en C# 3.0
121
utilizar este nuevo método, nos encontramos con que no está. El problema, aparentemente, reside en que ya existe uno con ese nombre; pero el problema, en realidad, es que posee la misma firma. Lo que ocurre es que, en un ámbito de trabajo, tiene mayor inferencia un método de instancia que un método extensor. Pero si de todas formas quisiéramos trabajar con el método extensor, podríamos llamarlo a través de su instancia estática como cualquier método. Ocurre algo parecido con los espacios de nombre, dado que los espacios de métodos extensores de menor profundidad poseen precedencia sobre los de mayor profundidad.
Expresiones Lambda Ahora que hemos visto las principales novedades y características de C# 2.0 y 3.0, quizá se torne complicado mostrar algunos ejemplos sin tener que mencionar LINQ, el cual será tratado más adelante. Dicho esto, nos adentraremos en los dos últimos temas: Expresiones Lambda y expresiones de consulta. Abordemos el tema de las expresiones Lambda: Éstas fueron incorporadas en C# 3.0 y nos van a permitir trabajar con delegados y métodos anónimos de una forma diferente de la que habíamos observado hasta ahora. Avanzaremos de a poco, para que su entendimiento sea lo más claro posible, e iremos aportando mayor complejidad a medida que comprendamos los conceptos. La sintaxis, a grandes rasgos, es parámetros de entrada => expresión/declaración, donde => es el operador lambda y cuya traducción programática sería algo como “va a“. Del lado izquierdo de este operador tendremos los parámetros de entrada, si es que los hay, y del lado derecho, nuestro bloque de declaración o expresión. Citamos el siguiente ejemplo, utilizando delegados para obtener el múltiplo de dos números: namespace ExpresionesLambda { class Program { delegate int Delegado(int a, int b); static void Main(string[] args) { Delegado miDel = new Delegado( delegate(int a, int b) { return a * b; }); Console.WriteLine("Resultado: " + miDel(4, 9)); Console.Read(); } } }
Visual Studio - Firtman, Natale
Alfaomega
122
3- C# 3.0
Hasta aquí nada extraño: Creamos nuestro delegado utilizando un método anónimo, tal como hemos visto hasta el momento. Ahora, tomando este mismo caso, vamos a escribirlo pero utilizando una expresión Lambda: namespace ExpresionesLambda { class Program { delegate int Delegado(int a, int b); static void Main(string[] args) { Delegado miDelLambda = (int c, int d) => c * d; Console.WriteLine("Resultado: " + miDelLambda (4, 9)); Console.Read(); } } }
Lo que hacemos es definir dos parámetros del tipo int y, luego del operador lambda, definimos nuestra expresión. Aquí disponemos también de la inferencia de tipos, dado que el compilador –inspeccionando la declaración del delegado– puede deducir con qué tipos estamos trabajando; por consiguiente, podríamos haber escrito de la siguiente manera, obteniendo el mismo resultado: Delegado miDelLambda = (c, d) => c * d;
De la manera que hemos codificado en el ejemplo de arriba, del lado derecho del operador tenemos una expresión que podría haber sido desarrollada mediante un bloque de declaración como el siguiente: Delegado miDelLambda = (c, d) => { return c * d; };
Supongamos ahora que simplemente vamos a trabajar con un solo valor como parámetro, donde el resultado será elevarlo al cuadrado. Podríamos, entonces, prescindir de los paréntesis en la declaración del lado izquierdo del operador lambda: OtroDelegado miDelLambda = n => Math.Pow(n,2);
Si no tuviéramos ningún parámetro como entrada, no escribiríamos nada entre los paréntesis: TuDelegado deleg = () => Console.WriteLine("Una prueba"); Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en C# 3.0
123
Predicados y Proyecciones Cuando el objetivo de una expresión es obtener un resultado del tipo booleano, para determinar si se cumple tal o cual condición –o una relación de pertenencia– estamos ante la presencia de un predicado: nro => nro > 10;
Ahora, cuando la expresión retorna un tipo diferente al del predicado, se dice que es una proyección: miTexto => miTexto.Length; Delegados Func(T, TResult) y Action(T).
Quizá hayan notado que en todos los ejemplos que hemos escrito, donde utilizábamos delegados debíamos crearlos de forma explícita: delegate int Delegado(int x); Delegado miDel = n => n * 2;
Disponemos ahora de la posibilidad de escribir menos código y hacerlo más legible mediante dos opciones: s
Func(T, TResultado): Básicamente, este delegado encapsula un método con un número limitado de parámetros y devuelve un valor del tipo TResult, evitando así tener que crear un delegado en forma explícita. Obviamente, la firma del método debe coincidir con la definida en el delegado. Por ejemplo, basándonos en el código anterior, donde devolvíamos el producto de dos números, sería: Func
Multiplicar = (int c, int d) => c * d; Console.WriteLine("Salida: " + Multiplicar(4, 9)); Console.Read();
s
Action(T): Es igual a Func
, con la diferencia que no retorna un valor: namespace ExpresionesLambda { class Program { static void Main(string[] args) {
En la figura 3-8 podemos apreciar, mediante .NET Reflector, cómo el compilador hace el trabajo por nosotros.
Fig. 3-8. .NET Reflector nos muestra cómo el compilador infiere los tipos y genera el delegado de forma implícita.
Expresiones de consulta Las expresiones de consulta poseen su pilar en todo lo visto anteriormente, es por eso que las hemos dejado para el final. Aquí utilizaremos expresiones Lambda, inicializadores de objetos, métodos extensores y mucho más. Como no podía ser de otra manera, este tema está íntimamente ligado con LINQ, dado que es la herramienta que utilizaremos para nuestras consultas más adelante.
Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en C# 3.0
125
Las expresiones de consulta nos van a permitir recuperar información de uno o más orígenes de datos, mediante una serie de instrucciones que luego manipularemos en nuestra aplicación. El resultado obtenido puede ser, además, una fuente de datos para otra consulta. Quienes trabajen con lenguajes SQL, notarán una gran similitud con ellos, aunque con algunas diferencias. Pero es importante destacar la posibilidad de realizar portabilidad de conocimientos, dado que nos ahorrará tiempos de aprendizaje en la adopción de este tipo de búsquedas. La primera diferencia que encontramos es que comienzan con la cláusula from en lugar de la clásica select, ubicándose ésta al final de la expresión. Para que podamos escribir nuestras consultas, el origen de los datos debe implementar IEnumerable
o IQueryable
. Veamos un simple ejemplo: namespace ExpresionesConsulta { class Program { static void Main(string[] args) { string[] arrPaises = { "Argentina", "Uruguay", "Alemania", "Canada", "Colombia" }; IEnumerable<string> varPaises = from pais in arrPaises where pais.StartsWith("A") orderby pais descending select pais; foreach (string pais in varPaises) { Console.WriteLine("Pais: " + pais); } Console.Read(); } } }
Resultado: Pais: Argentina Pais: Alemania
Visual Studio - Firtman, Natale
Alfaomega
126
3- C# 3.0
Lo primero que hacemos es declarar una variable (varPaises) que contenga el resultado de nuestra consulta y que, además, cumpla con la interface IEnumerable
, para luego poder recorrer y obtener los valores esperados. Luego, comenzaremos a escribir nuestra expresión: Seguido de from especificamos una variable, denominada variable de rango, que infiere el tipo de dato, en este caso string, del contexto actual. Utilizamos in para declarar cuál es nuestra colección que contiene los datos que han de ser evaluados. Notemos cómo hemos hecho para filtrar el origen mediante la cláusula where. En este caso vamos a obtener todos los países de arrPaises[] que comiencen con la letra A y especificaremos, también, que el resultado esté ordenado (mediante orderby descending) en forma descendente. Supongamos otro caso. Pensemos que tenemos nuestra clase Cliente con tres propiedades: Código, Denominación y Provincia. Y necesitamos obtener todos los clientes cuyo código de identificación es mayor que 89. Observemos el siguiente bloque de código para entender cómo podríamos hacerlo: namespace ExpresionesConsulta { class Cliente { public int Codigo { get; set; } public string Denominacion { get; set; } public string Provincia { get; set; } } class Program { static void Main(string[] args) { List
clientes = new List
{ new Cliente {Codigo=100, Denominacion="Pablo Ferreyra",Provincia="Catamarca"}, new Cliente {Codigo=89, Denominacion="Claudio Mess",Provincia="San Juan"}, new Cliente {Codigo=509, Denominacion="Diego Pacce",Provincia="Catamarca"}, new Cliente {Codigo=22, Denominacion="Nora Díaz",Provincia="Misiones"}, }; var listClientes = from cli in clientes where cli.Codigo > 90 select new { Nombre = cli.Denominacion, Prov = cli.Provincia }; foreach (var cliente in listClientes) {
Detengámonos en la definición de la consulta: ¿No notan algo extraño pero ya conocido? Sí, estamos utilizando tipos anónimos e inicializadores de objetos. Quizá, lo que muchos se estarán preguntando es cómo se interpreta esta especie de código SQL para que pueda ser ejecutado. Básicamente, C# traduce las sentencias de consulta en invocaciones de métodos de instancias o extensores, como por ejemplo Select, Where u OrderBy. Cada uno de ellos responde a un patrón de consultas y, mediante un mapeo entre sentencia y método, saben cómo deben procesar cada una de las instrucciones que hemos escrito. De hecho, podemos escribir nuestras consultas en notación de métodos y para muchos, quizá, les resulte más fácil o entendible hacerlos de esta manera. En el código que se presenta a continuación, vemos cómo sería escribir el ejemplo anterior pero con métodos: class Program { static void Main(string[] args) { List
clientes = new List
{ new Cliente {Codigo=100, Denominacion="Pablo Ferreyra",Provincia="Catamarca"}, new Cliente {Codigo=89, Denominacion="Claudio Mess",Provincia="San Juan"}, new Cliente {Codigo=509, Denominacion="Diego Pacce",Provincia="Catamarca"}, new Cliente {Codigo=22, Denominacion="Nora Díaz",Provincia="Misiones"}, }; var listClientesMetodos = clientes. Where(c => c.Codigo > 89). Select(c => new { Nombre = c.Denominacion, Prov = c.Provincia });
Existen aún más opciones para escribir nuestras consultas, pero las desarrollaremos en el capítulo de LINQ.
Alfaomega
Visual Studio - Firtman, Natale
129
Visual Basic 2008
4
Introducción Todos conocemos al viejo y querido Visual Basic, conocido también como VB. Originalmente, dueño de su propia plataforma y utilizado ampliamente en desarrollo de aplicaciones de escritorio bajo tecnología Microsoft, fue evolucionando de manera considerable hasta convertirse en un poderoso y flexible lenguaje de programación para realizar cualquier tipo de desarrollos en la actualidad. Todavía son miles los programadores Visual Basic 6.0 que no han evolucionado con las versiones .NET de la plataforma. Desde aquí, un incentivo enorme a aquellos que no lo han hecho. Este es el momento de evolucionar y no quedar una década atrás en el mundo del desarrollo.
La evolución en VB La primera versión del lenguaje fue creada en 1991 por Microsoft, a partir del todavía más antiguo lenguaje Basic, con el objetivo de ofrecer una plataforma de creación de aplicaciones gráficas para DOS y luego para el Windows, todavía incipiente. Durante diez años más, Visual Basic se convirtió en el lenguaje más utilizado para la creación de aplicaciones Win32, o aplicaciones ejecutables para la plataforma Windows. En ese sentido, Visual Basic 6.0 fue la plataforma más utilizada en el mercado, si la comparamos con Borland Delphi o C++. Desde el año 2001 Microsoft decidió suspender su plataforma Win32 para Windows y así ingresar con todas sus fuerzas a la plataforma .NET –con todas las ventajas que eso trajo–. A partir de la versión 7.0, conocida como Visual Basic.NET, Visual Studio - Firtman, Natale
Alfaomega
130
4- Visual Basic 2008
se decidió dar un corte fundamental en el mundo de la programación VB. El código de VB 7.0 no sería compatible con el de 6.0. Fue una decisión difícil, pero necesaria. Así, Visual Basic entró en la lista de lenguajes tipados, totalmente orientados a objetos y con las mismas posibilidades que lenguajes como C# o Java. De hecho, del viejo Visual Basic 6.0 de 1998 al actual Visual Basic queda poco en común, por lo que incluso para la eterna enciclopedia en línea Wikipedia son lenguajes distintos.
Fig. 4-1. Captura de pantalla de Microsoft Visual Basic 1.0 para DOS en 1991. No estamos tan lejos, ¿o sí?
Entonces, la vida del lenguaje que nosotros llamaremos Visual Basic, el moderno, comenzó con Visual Basic .NET (VB 7.0) en 2002, pasando por Visual Basic 2003 (VB 7.1), Visual Basic 2005 (VB 8.0), Visual Basic 2008 (VB 9.0) y el futuro Visual Basic VBx (VB 10.0).
Visual Basic .NET Si todavía queda algún programador rezagado de Visual Basic 6.0, daremos un repaso por las principales características del nuevo lenguaje desde la versión .NET: s
No genera más aplicaciones .EXE para Windows que requieran los famosos DLLs del runtime de Visual Basic.
Alfaomega
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
131
s
Compila para el CLR (Common Language Runtime), generando aplicaciones .EXE para plataforma .NET, aplicaciones Web para ASP.NET, aplicaciones móviles para .NET Compact Framework, aplicaciones Web para Silverlight y otros.
s
Es un lenguaje totalmente orientado a objetos.
s
Sólo funciona sobre el .NET Framework, incluyendo los tipos de datos de esta plataforma y al 100% de las clases y funcionalidades.
s
Las clases o paquetes compilados pueden interactuar con total compatibilidad con otros paquetes desarrollados en otros lenguajes .NET, incluyendo C#.
s
No existe más el tipo de datos Variant, reemplazado en su concepto similar por Object.
s
Son obligatorios los paréntesis en llamados a funciones y métodos.
s
No funcionan más los operadores Set y Let.
s
Los tipos de datos siguen vigentes, aunque se tratan de alias a las clases de .NET, por ejemplo, Integer estará representando a System.Int32.
s
Por compatibilidad se dejaron algunas funciones globales que son alias a llamadas a métodos del .NET Framework. No obstante, se recomienda utilizar la versión nueva.
s
Option Explicit está definido en On por defecto, lo que nos obliga a declarar todas las variables antes de usarlas.
Características incorporadas en .NET Framework 2.0 El .NET Framework 2.0 es considerado un cambio muy importante en la plataforma de Microsoft, en cuanto a soporte y tiempo de desarrollo. Visual Basic 2005 (VB 8) tampoco se ha quedado atrás en la implementación de estas novedades.
Genéricos Quizá para muchos, los genéricos sean una de las características más importantes agregadas en la versión 2.0. Si bien Visual Basic, comparado con C#, es un poco menos estricto en cuanto al cast de tipos de dato, la mala utilización de estos tipos de dato nos Visual Studio - Firtman, Natale
Alfaomega
132
4- Visual Basic 2008
provoca degradación de rendimiento, así como posibles excepciones en tiempo de ejecución. Recordemos que los genéricos nos van a permitir escribir, de forma fuertemente tipada, colecciones, clases y métodos tal como se lo hace en otros lenguajes como Java o C#. Disponemos ahora de un nuevo espacio de nombres: System.Collections.Generic que contiene varias interfaces y clases basadas en tipos genéricos, para poder generar nuestras colecciones inflexibles. En el espacio de nombres System tenemos IComparable(Of T) para crear métodos de comparación de un tipo definido. Como podemos observar, hay una nueva notación: (Of T). Éste es el tipo que vamos a utilizar como parámetro, por ejemplo Integer o alguna otra clase que puede ser también definida por nosotros. Utilizar la letra T es simplemente una convención, no quiere decir que siempre tengamos que escribirla, bien podríamos reemplazarla por la letra U si quisiéramos. De hecho, vamos a ver más adelante que, en ciertas circunstancias, es necesario identificar el argumento mediante diferentes letras. Veamos algo de código, que seguramente va a echar un poco de luz a este tema tan importante. En las siguientes líneas vamos a utilizar el tradicional ArrayList para definir una lista de nombres de personas:
'H¿QLPRVQXHVWUR$UUD\/LVW Dim listaNombres As New ArrayList()
&RPHQ]DPRVDDJUHJDUDOJXQRVQRPEUHV listaNombres.Add("Leonardo Natale") listaNombres.Add("Maximiliano Firtman") 'En tiempo de compilación, no obtenemos ningún error. listaNombres.Add(10) For Each Nombre In listaNombres Console.WriteLine(CStr(Nombre)) Next Console.Read()
Como podemos observar en este código, además de agregar dos nombres de personas, hemos agregado el valor 10, el cual al momento de compilar no devolvió ningún error. Cuando se ejecute la aplicación, obtendremos una excep-
Alfaomega
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
133
ción del tipo InvalidCastException, dado que no puede convertir un tipo de datos System.Int32 a System.String al utilizar el cast CStr. En realidad, en este caso nosotros no deberíamos agregar un valor del tipo Integer, dado que lo que estamos completando es una lista de nombres. Pero como ArrayList espera un valor del tipo Object al momento de compilar no obtenemos ninguna advertencia, dado que todo hereda de ese tipo de dato. Obviamente esto es un ejemplo muy sencillo de cómo una línea de código puede llegar a poner nuestro sistema inestable. Veamos ahora cómo podríamos escribir el mismo ejemplo pero utilizando Generics: Imports System.Collections.Generic;
'H¿QLPRVQXHVWUR/LVWIXHUWHPHQWHWLSDGRSDUDTXHDFHSWHGDWRV 'del tipo String. Se lee "lista de string" Dim listaNombresGenerics As New List(Of String) listaNombresGenerics.Add("Leonardo Natale") listaNombresGenerics.Add("Maximiliano Firtman") ' Error en tiempo de compilación listaNombresGenerics.Add(10) For Each Nombre As String In listaNombresGenerics {
De esta manera, al compilar obtendremos un error que nos informa que estamos insertando en la lista un tipo de dato (en este caso Integer) para el cual la lista no fue definida. Por defecto, al agregar un nuevo proyecto o una nueva clase, ya tenemos agregado el namespace para genéricos, por lo que nos evitaríamos tener que realizar: Imports System.Collections.Generic La siguiente tabla muestra algunas de las colecciones de la versión 1.1 y su par en Generics, utilizando la sintaxis de Visual Basic.
Visual Studio - Firtman, Natale
Alfaomega
134
4- Visual Basic 2008
.NET 1.x
.NET 2.0
ArrayList
List(Of T)
Hashtable
Dictionary(Of K,V)
SortedList
SortedList(Of K,V)
Stack
Stack(Of T)
Ahora bien, como dijimos anteriormente, podemos tomar ventaja de los genéricos cuando creamos nuestros métodos y clases. Veamos algunos ejemplos. Nuestra primera clase genérica En el siguiente código vamos a generar un proyecto de consola y agregaremos una nueva clase (ClaseGenerica.vb), que posee una propiedad llamada MyProperty que puede adoptar diversos tipos de argumentos en función del valor que se le asigne a (Of T): Namespace EjemplosGenerics Class ClaseGenerica(Of T) Private myVar As T 3XEOLF3URSHUW\0\3URSHUW\ $V7 Get 5HWXUQP\9DU End Get 6HW%\9DOYDOXH$V7 P\9DU YDOXH End Set End Property 3XEOLF5HDG2QO\3URSHUW\7LSR $V6WULQJ Get 5HWXUQ*HW7\SH7 7R6WULQJ End Get End Property End Class End Namespace
Para que nos ayude en nuestro estudio, definimos una propiedad denominada Tipo, que nos devolverá el tipo de argumento que posee (Of T) mediante la instrucción GetType.
Alfaomega
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
135
Utilizando clase genérica Ya tenemos generada nuestra clase genérica, observemos un ejemplo de cómo utilizarla:
,QVWDQFLDPRVQXHVWUDFODVH\FRPRWLSRGHYDORUOHSDVDPRVXQ ' string Dim claseString As New ClaseGenerica(Of String)
,QVWDQFLDPRVQXHVWUDFODVH\FRPRWLSRGHYDORUOHSDVDPRVXQ ' Single Dim claseSingle As New ClaseGenerica(Of Single)
(VWDEOHFHPRVYDORUHVSDUDQXHVWUDVSURSLHGDGHV FODVH6WULQJ0\3URSHUW\ (VWRHVXQDFDGHQDGHWH[WR claseFloat.MyProperty = 9.18F ' Enviamos a Consola valores y tipos Console.WriteLine("Valor de MyProperty de claseString es: {0}",_ claseString.MyProperty) Console.WriteLine("El tipo de T es: {0}", claseString.Tipo) Console.Write(vbCrLf) Console.WriteLine("Valor de MyProperty de claseSingle es: {0}",_ claseFloat.MyProperty) Console.WriteLine("El tipo de T es: {0}", claseFloat.Tipo) Console.Read()
Ejecutemos la aplicación y observemos el resultado (figura 4-2).
Fig. 4-2. Resultado de utilizar nuestra clase genérica. Visual Studio - Firtman, Natale
Alfaomega
136
4- Visual Basic 2008
Cuando instanciamos nuestras clases, simplemente reemplazamos (Of T) con el tipo de argumento en cuestión. En el ejemplo de arriba, trabajamos con String: Dim Clase String As ClaseGenerica(Of String) = New_ ClaseGenerica(Of String)
O la versión reducida: Dim Clase String As New ClaseGenerica(Of String)
Y luego con Single: Dim ClaseSingle As New ClaseGenerica(Of Single)
De esta manera, reutilizamos nuestro código simplemente asignando un tipo de argumento. Y no sólo eso, sino que, además, estamos proveyendo cierta seguridad a nuestra aplicación, dado que si hubiéramos escrito: &ODVH6LQJOH0\3URSHUW\ 8QWH[WRFXDOTXLHUD
al momento de compilar obtendríamos un error diciéndonos que no podemos convertir de un tipo String a un Single. Creando métodos genéricos Es posible crear métodos genéricos tanto en clases genéricas como no genéricas y pueden aceptar diversos valores en la firma del mismo: 3ULYDWH6XE0HWRGR*HQHULFR%\9DOYDU$V7%\5HIYDU$V8B ByVal var3 As V) &XHUSRGHOPpWRGR (QG6XE 3ULYDWH)XQFWLRQ2WUR0HWRGR*HQHULFR2I7 (ByVal var1 As T, ByVal var2 As T) As T &XHUSRGHOPpWRGR (QG)XQFWLRQ
Veamos un ejemplo: Class MiClase &UHDPRVQXHVWURPpWRGRJHQpULFR 3XEOLF)XQFWLRQ2EWHQHU0D\RU2I7$V,&RPSDUDEOH (ByVal var1 As T, ByVal var2 As T) As T If var1.CompareTo(var2) > 0 Then 5HWXUQYDU End If Alfaomega
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
137
5HWXUQYDU (QG)XQFWLRQ End Class
8WLOL]DQGRQXHVWURPpWRGR Dim c As New MiClase() Console.WriteLine(c.ObtenerMayor(-2, -3)) Console.WriteLine(c.ObtenerMayor(109, 474)) Console.WriteLine(c.ObtenerMayor(23.5, 87.1)) Console.WriteLine(c.ObtenerMayor("Flavio", "Pablo")) Console.Read()
Y el resultado será: -2 474 87,1 Pablo
Es importante que notemos que no hemos especificado el tipo de argumento al momento de llamar al método, esto se debe a que el compilador tiene la capacidad de poder inferir el tipo que se le está enviando. Entonces, logramos evitar el hecho de realizar varias sobrecargas para un método similar. Otro detalle del código anterior es la aparición de la cláusula As: 3XEOLF)XQFWLRQ2EWHQHU0D\RU2I7$V,&RPSDUDEOH Mediante la utilización de esta cláusula, lo que haremos será definir una limitación o restricción para el tipo de parámetro T y, en este caso, estaremos especificando que debe implementar la interface IComparable. Por consiguiente, si al llamar al método ObtenerMayor, el tipo de argumento de T que estamos fijando no implementa dicha interface, el compilador nos devolverá un error. Esta es otra forma de asegurar nuestro código. Generics es mucho más extenso que lo que aquí vimos, es realmente muy útil y nos va a facilitar mucho nuestra codificación. En el sitio de MSDN de Microsoft, vamos a encontrar muchos más ejemplos e información relevante que fueron agregados sobre el tema. Es imprescindible interiorizarse para sacar partido de Generics.
Clases Parciales Las clases parciales son un concepto simple, pero muy útil. Una clase parcial es aquella cuya definición está dividida, físicamente, en más de un archivo. Para el compilador el uso de la clase es exactamente igual. Sólo nos permite trabajar Visual Studio - Firtman, Natale
Alfaomega
138
4- Visual Basic 2008
de mejor forma cuando lo hacemos en capas o en trabajos en equipo, pudiendo tener bien separados ambos archivos. Todas las partes de la clase deben estar en el mismo lenguaje. En ASP.NET esto trajo una gran solución: Que una página Web de tipo Code-Behind pueda trabajar internamente como una sola clase dividida en dos archivos. La sintaxis incorpora el operador Partial: 3XEOLF3DUWLDO&ODVV1RPEUH End Class
Objeto My El nuevo namespace exclusivo de Visual Basic, My (Mi en español), permite acceder a distintas funcionalidades del .NET Framework de una forma más fácil y accesible. Pensemos en este objeto (ya instanciado) como directo a distintos puntos del árbol jerárquico de clases .NET. Por ejemplo, podemos leer el texto de un archivo de disco: 'LPWH[WRDV6WULQJ 0\&RPSXWHU)LOH6\VWHP5HDG$OO7H[W ("C:\Texto.txt")
Fig. 4-3. La estructura de objetos disponibles en My. Alfaomega
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
139
El a los distintos objetos disponibles está íntimamente relacionado con el tipo de proyecto (ASP, Windows Form, etc.). Objetos Disponibles Objeto
Descripción
My.Application
Obtiene propiedades y métodos sobre la aplicación en ejecución.
0\&RPSXWHU
Permite acceder al nombre y pantalla del equipo.
0\&RPSXWHU$XGLR
Permite acceder a la reproducción y propiedades de audio.
0\&RPSXWHU&OLSERDUG
Obtiene al portapapeles de Windows.
0\&RPSXWHU&ORFN
Obtiene al reloj del equipo.
0\&RPSXWHU FileSystem
Permite acceder al sistema de archivos, con funciones para leer, copiar, grabar archivos y carpetas.
0\&RPSXWHU,QIR
Accede a información de la memoria y sistema operativo.
0\&RPSXWHU.H\ERDUG
Accede a información del teclado y su uso.
0\&RPSXWHU0RXVH
Accede a información actual del mouse.
0\&RPSXWHU1HWZRUN
Accede a funciones y eventos relacionados con la red.
0\&RPSXWHU3RUWV
Permite acceder a puertos seriales del equipo.
0\&RPSXWHU5HJLVWU\
Obtiene al registro de Windows.
My.
Permite acceder a información del actualmente identificado.
0\5HTXHVW
En ASP.NET permite acceder al requerimiento de la petición.
My.Response
En ASP.NET permite acceder a la respuesta hacia el cliente.
My.Form
En Windows Forms permite acceder a funcionalidades respecto de los formularios y ventanas de la aplicación.
0\5HVRXUFHV
Permite acceder a los recursos de la aplicación (no disponible en ASP.NET.)
My.Settings
Permite acceder a información y utilidades respecto de la configuración de la aplicación (no disponible en ASP.NET).
My.WebServices
Permite acceder a las funcionalidades rápidas de servicios Web (no disponible en ASP.NET).
La siguiente tabla muestra la compatibilidad de los objetos, según el tipo de proyecto:
Sobrecarga de operadores Una de las funciones avanzadas más pedidas era la sobrecarga de operadores, así que, desde Visual Basic 2005, también es posible definir operaciones –como la suma o la negación– sobre clases creadas por nosotros mismos. Hagamos la clásica clase 1XPHUR&RPSOHMR, representando a los números complejos matemáticos: &ODVV1XPHUR&RPSOHMR &RQVWUXFWRU 3XEOLF6XE1HZ%\9DOUHDO$V'RXEOH%\9DOLPDJLQDULR$VB 'RXEOH x = real y = imaginario (QG6XE 3XEOLF2YHUULGHV)XQFWLRQ7R6WULQJ $V6WULQJ Dim s As String If y >= 0 Then s = x + " + " + y + "i" Else s = x + " - " + -1*y + "i" End If 5HWXUQV (QG)XQFWLRQ 3XEOLF3URSHUW\5HDO $V'RXEOH Get 5HWXUQ[ End Get
Alfaomega
Visual Studio - Firtman, Natale
Características incorporadas en .NET Framework 2.0
141
6HW%\9DO9DOXH$V'RXEOH [ 9DOXH End Set End Property 3XEOLF3URSHUW\,PDJLQDULR $V'RXEOH Get 5HWXUQ\ End Get 6HW%\9DO9DOXH$V'RXEOH \ 9DOXH End Set End Property 3ULYDWH[$V'RXEOH 3ULYDWH\$V'RXEOH End Class
Ahora queremos sumar estos números complejos sin necesidad de recurrir a un clásico método de instancia llamado 6XPD, que reciba otro complejo, o un método de clase (shared), que reciba ambos. 'Opcion 1 3XEOLF)XQFWLRQ6XPD%\9DO2WUR&RPSOHMR$V1XPHUR&RPSOHMR $VB 1XPHUR&RPSOHMR 'Opcion 2 3XEOLF6KDUHG)XQFWLRQ6XPD%\9DO&RPSOHMR$V1XPHUR&RPSOHMR %\9DO&RPSOHMR$V1XPHUR&RPSOHMR $V1XPHUR&RPSOHMR
Para utilizar los ejemplos anteriores, haríamos algo así: 'LP&RPSOHMR$V1HZ1XPHUR&RPSOHMR 'LP&RPSOHMR$V1HZ1XPHUR&RPSOHMR 'LP6XPD$V1XPHUR&RPSOHMR 'Para 6XPD 'Para 6XPD
la opción 1 &RPSOHMR6XPD&RPSOHMR la opción 2 1XPHUR&RPSOHMR6XPD&RPSOHMR&RPSOHMR
¿No sería más legible utilizar el siguiente código? 6XPD &RPSOHMR&RPSOHMR
Entonces, hagamos una sobrecarga de operadores como el siguiente ejemplo, dentro de la clase 1XPHUR&RPSOHMR: Visual Studio - Firtman, Natale
Otros cambios menores Operador IsNot Simplifica muchas expresiones lógicas y equivale a utilizar Not … Is, por ejemplo: If Not objeto Is Nothing then Se reemplaza por If objeto IsNot Nothing then Definición de Arrays Como ya sabemos, los vectores en .NET empiezan de la posición 0. En Visual Basic definir un vector producía mucha confusión si la definición daba la cantidad de elementos o el límite superior, entonces desde VB 8 es posible declarar un vector de la siguiente forma más clara: Dim vector(0 to 9) As String Instrucción Continue Indica en un bucle que continúe a la siguiente iteración.
Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en Visual Basic 2008
143
Tipos sin Signo Aparecen los tipos de dato ULong, UInteger y UShort, representando las clases sin signo de cada uno de los tipos de dato. Comentarios XML Esta es una opción que ya estaba disponible oficialmente en C# y ahora se incorpora a Visual Basic. Permite definir comentarios que serán de utilidad para la generación de la documentación oficial del proyecto. Un ejemplo de un comentario XML sería el siguiente (ubicándolo previo a la definición de un método). Además, esta información es utilizada por Intellisense para darle ayuda al cuando intenta utilizar el método:
VXPPDU\!
3URFHVDHOFOLFNGHXQXVXDULRHQHOERWyQ
VXPPDU\! ''' <param name="strNombre">Nombre del Boton
UHWXUQV!'HYXHOYHVLHVFRUUHFWRUHWXUQV!
Instrucción using Permite definir un bloque de código que utilizará algún objeto que queramos liberar al finalizar dicho bloque. Por ejemplo, las conexiones a bases de datos y la apertura de archivos o gráficos requieren de la apertura y liberación de recursos y son candidatos a usar esta instrucción. 8VLQJFRQH[LRQDV6TO&RQQHFWLRQ
$TXtYDHOFyGLJRTXHWUDEDMDFRQODFRQH[LyQ (QGXVLQJ
Operador TryCast Permite hacer un cast (moldeado) de un objeto hacia otra clase, pero que tiene la particularidad de devolver Nothing en caso de que el casting no se haya podido ejecutar. 'LPFDV&XDGUDGR 7U\&DVW¿JXUD&XDGUDGR
Incorporaciones en Visual Basic 2008 Nullable Types Si bien estaban disponibles desde Visual Basic 2005 utilizando genéricos, los tipos anulables, o Nullable Types, son tipos de datos que podemos permitirles ser nulos y que, hasta ahora, no tenían la posibilidad de serlo. Visual Studio - Firtman, Natale
Alfaomega
144
4- Visual Basic 2008
Por ejemplo, un entero no tenía forma de tener un valor nulo. Esto es muy importante para unir datos con bases de datos, dado que allí sí pueden existir campos con datos nulos. Desde VB 9 la sintaxis se volvió bastante simple; sólo se ingresa un símbolo ? –abreviatura del genérico 1XOODEOH2IWLSR ± al final del tipo de datos que se ha de utilizar: 'LP1XPHUR$V,QWHJHU" ,I1XPHUR+DV9DOXH7KHQ 7LHQHXQYDORU End If
La propiedad +DV9DOXH de cualquier tipo anulable devuelve tenga o no valor ingresado y *HW9DOXH2U'HIDXOW devuelve el valor o, si es nulo, un valor ingresado por parámetro.
Operador Ternario El operador ternario es un clásico de la programación en lenguajes derivados de C, como C# o Java. Desde Visual Basic 2008 también podremos utilizarlos con la siguiente sintaxis: 9DULDEOH ,IFRQGLFLRQYDORUGHUHWRUQRSRU7UXHB valor de retorno por False)
Esto nos permite evaluar una condición y devolver un valor u otro, según esta condición, evitando un clásico If a través de una expresión. Si bien VB tenía la función global IIf, en este último caso necesitábamos convertir el tipo de datos devueltos previamente.
Tipos Implícitos La nueva inclusión de tipos implícitos en Visual Basic, a partir de la instrucción Dim, nos va a permitir trabajar con variables cuyo tipo no es definido explícitamente –tal como lo hacíamos hasta ahora–, sino que el compilador será el responsable de deducir el tipo de la misma en función de lo que hayamos definido en el lado derecho de la inicialización. Así definíamos nuestras variables:
Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en Visual Basic 2008
145
$VLJQDFLyQH[SOtFLWD Dim valor As Integer = 1 Dim nombre As String = "Leonardo" 'LPQXPHUR$V6LQJOH ) 'LPDUU3URYLQFLDV$V$UUD\ 1HZ6WULQJ ^&DWDOXQLDB "Madrid", "Galicia"}
El mismo ejemplo con tipos implícitos:
$VLJQDFLyQLPSOtFLWD Dim valor = 1 Dim nombre = "Leonardo" 'LPQXPHUR ) 'LPDUU3URYLQFLDV 1HZ6WULQJ ^&DWDOXQLD0DGULG*DOLFLD`
El compilador deduce los tipos que no especificamos al utilizar la instrucción Dim. No hay que confundir la misma expresión en anteriores versiones de Visual Basic, donde el código también podía funcionar con Option Strict desactivado, pero redundaba en cuatro objetos de clase Object. Ahora, a los fines de compilación equivale a haberle definido explícitamente el tipo. La nueva opción Option Infer permite desactivar la inferencia de tipos y trabajar con el viejo modelo, aunque no es recomendable. Supongamos que definimos una variable, que implícitamente es del tipo Single, y luego le asignamos un texto; al momento de compilar obtendríamos un error: Dim miVariable = 4.8F PL9DULDEOH &XDOTXLHUWH[WR (UURU
Debido a que miVariable es del tipo Single, el compilador nos devolvería un error informándonos que, correctamente, no puede convertir un tipo String a Single. Otras cuestiones para tener en cuenta: s
Siempre debemos inicializar las variables: 'LPPL9DULDEOH 1RSXHGHLQIHULUHOWLSR
s
Dada la restricción anterior, se deben inicializar en la misma instrucción: Dim miVariable miVariable 1RSXHGHLQIHULUHOWLSR
s
No pueden establecerse como nulos: 'LPPL9DULDEOH 1RWKLQJ 1RSXHGHLQIHULUHOWLSR
Visual Studio - Firtman, Natale
Alfaomega
146
4- Visual Basic 2008
Inicializadores de objetos Una nueva funcionalidad ha sido agregada para ayudarnos al momento de tener que asignar un valor a campos, o propiedades accesibles, cuando creamos un objeto. Miremos como hacíamos antes para pasar algunos valores: &ODVV3URGXFWR 3XEOLF6XE1HZ (QG6XE Private _codigo As Integer Private _nombre As String Private _almacen As String 3XEOLF3URSHUW\&RGLJR $V,QWHJHU Get 5HWXUQBFRGLJR End Get 6HW%\9DOYDOXH$V,QWHJHU BFRGLJR YDOXH End Set End Property 3XEOLF3URSHUW\1RPEUH $V6WULQJ Get 5HWXUQBQRPEUH End Get 6HW%\9DOYDOXH$V6WULQJ BQRPEUH YDOXH End Set End Property 3XEOLF3URSHUW\$OPDFHQ $V6WULQJ Get 5HWXUQBDOPDFHQ End Get 6HW%\9DOYDOXH$V6WULQJ BDOPDFHQ YDOXH End Set End Property End Class 'LPSURG$V1HZ3URGXFWR
Otra forma sería pasando los valores por parámetro en el constructor: &ODVV3URGXFWR 3XEOLF6XE1HZ (QG6XE 3XEOLF6XE1HZ%\9DOFRGLJR$V,QWHJHU%\9DOQRPEUH$VB String) Me.Codigo = codigo Me.Nombre = nombre (QG6XE Private _codigo As Integer Private _nombre As String Private _almacen As String 3XEOLF3URSHUW\&RGLJR $V,QWHJHU Get 5HWXUQBFRGLJR End Get 6HW%\9DOYDOXH$V,QWHJHU BFRGLJR YDOXH End Set End Property 3XEOLF3URSHUW\1RPEUH $V6WULQJ Get 5HWXUQBQRPEUH End Get 6HW%\9DOYDOXH$V6WULQJ BQRPEUH YDOXH End Set End Property 3XEOLF3URSHUW\$OPDFHQ $V6WULQJ Get 5HWXUQBDOPDFHQ End Get 6HW%\9DOYDOXH$V6WULQJ BDOPDFHQ YDOXH End Set End Property End Class
Hasta aquí nada nuevo. Lo interesante es que, mediante inicializadores de objetos, vamos a poder definir nuestros valores en una misma instancia creacional sin tener que invocar a su método en forma explícita, permitiéndonos escribir líneas mucho más claras. Reutilizando el ejemplo anterior, su uso quedaría así: 'LPSURG$V1HZ3URGXFWR:LWK^1RPEUH $FHLWH9HJHWDOB .Almacen = "Madrid"}
¿Qué ventajas tenemos? En primer lugar, no necesitamos definir un constructor con parámetros. Por otro lado, podemos definir la cantidad de propiedades que se han de inicializar en cada momento y elegir el orden y cuáles vamos a definir. Por último, la instrucción With forma parte de una misma instrucción, por lo que puede ser utilizada en cualquier lugar donde pueda ir una expresión, como un 5HWXUQ o el envío de un parámetro sin necesidad de generar auxiliares. Trabajando con colecciones En C#, lenguaje donde también se han creado los inicializadores de objetos, existen los inicializadores de colecciones. Dicha posibilidad todavía no está disponible en Visual Basic, pero se podría emular fácilmente con la siguiente función: )XQFWLRQ&UHDU&ROHFFLRQ2I7 %\9DO3DUDP$UUD\LWHPV $V7 $VB List(Of T) 5HWXUQ1HZ/LVW2I7 LWHPV (QG)XQFWLRQ
Entonces, inicializamos listas de la siguiente manera: Dim lista = CrearColeccion( 1HZ3URGXFWR:LWK^1RPEUH $FHLWH9HJHWDO` 1HZ3URGXFWR:LWK^1RPEUH $FHLWXQDV` )
No obstante, los inicializadores de colecciones de C# son más potentes que nuestra simple función y esperamos tenerlos próximamente en Visual Basic. Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en Visual Basic 2008
149
Tipos Anónimos Ahora que hemos visto cómo funcionan los tipos implícitos y los inicializadores de objetos, vamos a estudiar otra nueva característica de Visual Basic 2008: Los tipos anónimos, que se basan principalmente en las dos características anteriores. Esta nueva característica nos va a permitir crear propiedades de modo implícito y asignarles un valor, pero que serán de sólo lectura. Básicamente, el compilador crea una clase del tipo anónimo con propiedades de un tipo que él va a inferir en función de nuestra declaración. Pero veamos un ejemplo sencillo: 'LPPRYLO 1HZ:LWK^0DUFD 1RNLD0RGHOR 1`
Notemos el uso de las palabras New With. ¿Nos es familiar, no? Claro que sí, estamos utilizando por un lados tipo anónimos y luego los inicializadores de objetos para asignarles valores a propiedades. ¿Pero si nunca creamos una clase con esas propiedades? Son propiedades que serán generadas por el compilador on the fly. Y es posible notar que en el código anterior nunca creamos explícitamente ni una clase, ni un tipo de propiedad, ni el nombre de la misma, así como tampoco sus s (Set y Get). Por cada objeto que creamos así se crea un tipo anónimo al compilar. El mismo es codificado en función del orden de las propiedades, sus tipos y cantidades que hayamos definido. Por deducción podemos afirmar que cuando se altere alguna de estas condiciones, el compilador de VB generará n clases de n tipos, según lo considere.
Fig. 4-4. Visual Studio nos provee de ayuda en Intellisense para las propiedades de nuestros tipos anónimos.
Métodos extensores Los métodos extensores nos brindarán la posibilidad de agregar métodos a tipos existentes, sin tener que derivar de ellos o modificar su implementación original. Muchas veces esto último no es posible, dado que quizá estemos interesados en Visual Studio - Firtman, Natale
Alfaomega
150
4- Visual Basic 2008
expandir las funcionalidades de un tipo del .NET Framework o de componentes de terceros. Crear métodos extensores es tan sencillo y fácil como crear cualquier otro método de instancia, pero con algunas pequeñas diferencias que deben tenerse en cuenta: s
Deben estar en un módulo.
s
Debe ser un 6XE o )XQFWLRQ
s
Deben tener al menos un parámetro.
s
La firma del método debe incluir el atributo Extension del espacio de nombres 6\VWHP5XQWLPH&RPSLOHU6HUYLFHV
s
El primer parámetro del método debe ser el tipo al que vamos a extender.
s
Cuando se ejecute, el primer parámetro pasa a ser la instancia del objeto.
Veamos un ejemplo simple, donde vamos a extender el tipo String: ,PSRUWV6\VWHP5XQWLPH&RPSLOHU6HUYLFHV 0RGXOH([WHQVRUHV <Extension()> _ 3XEOLF)XQFWLRQ7LHQH1XPHUR%\9DO[$V6WULQJ $V%RROHDQ 'LPUHJXODU([$V1HZ5HJH[>@ ,IUHJXODU([,V0DWFK[ 7KHQ 5HWXUQ7UXH End If 5HWXUQ)DOVH (QG)XQFWLRQ (QG0RGXOH
Básicamente, lo que hacemos es generar nuestro módulo Extensores y nuestro método 7LHQH1XPHUR. El objetivo de 7LHQH1XPHUR es devolver un tipo booleano, evaluando si la cadena de texto que se le envía como parámetro posee o no algún carácter comprendido entre 0 y 9. Notemos cómo el primer parámetro (x) define el tipo que queremos extender, en nuestro caso String. En el siguiente bloque de código, vemos cómo lo utilizamos: ' Importamos el espacio de nombres Imports MiApp.Extensores; 0RGXOH8VR0HWRGRV([WHQVRUHV
Si ejecutamos este ejemplo, la respuesta sería: 7UXH False 7UXH
En la figura 4-5 podemos ver cómo el IntelliSense nos muestra el nuevo método estático generado y cómo lo distinguimos mediante un ícono nuevo.
Fig. 4-5. Métodos extensores definidos y su integración con el IntelliSense.
Es importante aclarar que no debemos hacer un mal uso de los métodos extensores; verifiquemos primero si no disponemos de la funcionalidad que estamos buscando, ya implementada de forma original. Por ejemplo, no tendría sentido que agreguemos uno de estos métodos a String con la finalidad de retornar una cadena en mayúsculas, dado que ya se encuentra desarrollada: String.ToUpper. Visual Studio - Firtman, Natale
Alfaomega
152
4- Visual Basic 2008
De todas formas, imaginemos por un momento que extendemos el tipo String con un método llamado Replace, que recibe dos parámetros –uno es un char que contiene el carácter que ha de ser reemplazado y el segundo es otro char con el dígito con el cual reemplazaremos– y, cuando vamos a utilizar este nuevo método, nos encontramos con que no está. El problema reside en que ya existe uno con ese nombre. Sin embargo, el problema real es que posee la misma firma. Lo que ocurre es que en un ámbito de trabajo, tiene mayor inferencia un método de instancia que un método extensor. Pero si de todas formas quisiéramos trabajar con el método extensor, podríamos llamarlo a través de su instancia estática como cualquier método. Ocurre algo parecido con los espacios de nombre, dado que los nombres de métodos extensores de espacios de menor profundidad poseen precedencia sobre los de mayor profundidad.
Expresiones Lambda Ahora que hemos visto las principales novedades y características de Visual Basic 2005 y 2008, quizá se torne complicado mostrar algunos ejemplos, en lo que resta de este capítulo, sin tener que mencionar LINQ, el cual será tratado más adelante. Dicho esto, nos adentraremos en los dos últimos temas: Expresiones Lambda y expresiones de consulta. Abordemos primero el tema de las expresiones Lambda: Éstas fueron incorporadas en Visual Basic 2008 y nos van a permitir trabajar con delegados y métodos anónimos de una forma diferente a la que habíamos observado hasta ahora. Avanzaremos de a poco, para que su entendimiento sea lo más claro posible, e iremos aportando mayor complejidad a medida que comprendamos los conceptos. La sintaxis, a grandes rasgos, es )XQFWLRQSDUiPHWURV H[SUHVLyQ. Citamos el siguiente ejemplo, utilizando una expresión lambda, para calcular el área de un círculo: 'LPDUHD )XQFWLRQ%\9DOUDGLR$V'RXEOH 0DWK3, UDGLR UDGLR
Entonces, para usar la expresión hacemos: &RQVROH:ULWH/LQH(OiUHDGHXQFtUFXORGHGHUDGLRHVGHB + area(10))
El mundo de las expresiones Lambda es un poco más complejo y, en general, para su uso se utiliza más C# . No obstante, Visual Basic provee de la sintaxis para la mayoría de los casos y recomendamos la lectura del capítulo anterior para interiorizarse en este tema. Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en Visual Basic 2008
153
Métodos Parciales Los métodos parciales permiten definir un prototipo de método, pero que podrá o no implementarse en la clase. No debe confundirse con la herencia o con una interface, dado que los métodos parciales son una implementación de precompilador. Si el compilador detecta que el método parcial se definió, pero no se implementó en ningún archivo de la clase (recordemos el concepto de clases parciales anteriormente visto), entonces elimina todas las llamadas a ese método, como si nunca hubieran existido. Si, en cambio, el método se definió, entonces se deja la invocación. Recordemos que hablamos siempre de la misma clase y no de la herencia o polimorfismo. ¿Qué objetivo tienen los métodos parciales? Pueden ser útiles para métodos de depuración de código, para evitar utilizar interfaces y obligar a un a implementar métodos vacíos y se utiliza para algunos de los nuevos frameworks, como ser el LINQ T o SQL, que luego veremos con más detenimiento en este libro. El método sólo puede ser de tipo 6XE, debe incluir la cláusula Partial en su prototipo y no incluir ninguna cláusula en la implementación, si es que existiera; deben ser privados y pueden ser de instancia o de clase (Shared). En un archivo podemos tener: Partial Class Clase1 3XEOLF3DUWLDO6XE'HEXJ%\9DO7[W$V6WULQJ (QG6XE End Class
Y, opcionalmente, en otro archivo parcial de la misma clase podría estar la implementación: Partial Class Clase1 3XEOLF6XE'HEXJ%\9DO7[W$V6WULQJ ' Codigo a implementar (QG6XE End Class
Cualquiera de las llamadas a ese método existirá en el compilado solamente si el método fue implementado. Si no, se evitan y no se compilan.
Literales XML Uno de los grandes agregados de Visual Basic 2008, incluso de uso más sencillo que en C#, son los literales XML: Una simple y poderosa forma de definir elementos XML que podrán potenciarse más con LINQ. Visual Studio - Firtman, Natale
Alfaomega
154
4- Visual Basic 2008
Literal de elemento XML Para crear un elemento XML sólo debemos hacer: 'LPFXUVR$V;(OHPHQW B FXUVR! <nombre>Experto en ASP.NET 3.5 GXUDFLRQW\SH PHVHV!GXUDFLRQ! <Web pais="AR">www.itmaster.com.ar <Web pais="ES">www.itmaster.es :HESDLV 0;!ZZZLWPDVWHUFRPP[:HE! FXUVR!
Notemos que para definir el objeto de clase ;(OHPHQW, sólo necesitamos escribir el XML, sin comillas, sin instanciar ningún objeto, ni nada más complejo. Visual Studio reconocerá que se trata de un XML y lo tratará como tal, tabulando correctamente y dando los colores correctos a este tipo de documento. Notemos también que, una vez iniciado un literal XML, no necesitamos colocar el carácter de continuación de línea de Visual Basic ( _ ) hasta tanto finalice el XML válido. Literal de Documento XML El código anterior representa a un nodo XML de clase ;(OHPHQW. Si lo que queremos es crear un documento XML (que incluya cabecera, una posible transformación XSLT, nodos y otros atributos), el paso es similar pero utilizando ;'RFXPHQW: 'LP/LVWDGR&XUVRV$V;'RFXPHQW B FXUVRV! FXUVR! <nombre>Experto en ASP.NET 3.5 GXUDFLRQW\SH PHVHV!GXUDFLRQ! <Web pais="AR">www.itmaster.com.ar <Web pais="ES">www.itmaster.es :HESDLV 0;!ZZZLWPDVWHUFRPP[:HE! FXUVR! FXUVRV!
La clase ;'RFXPHQW tiene decenas de métodos útiles, como ser Load o Save, que permiten crear y guardar el XML desde y hacia archivos, URLs, strings y clases. La clase se encuentra en el espacio de nombres 6\VWHP;PO/LQT y recomendamos ampliamente revisar sus posibilidades desde MSDN o con la ayuda de Visual Studio.
Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en Visual Basic 2008
155
Expresiones Incrustadas Las expresiones incrustadas permiten incluir en el literal XML expresiones Visual Basic válidas, en lugar de solamente valores constantes. Por ejemplo: Dim URL = "www.itmaster" 'LPFXUVR$V;(OHPHQW B FXUVR! <nombre>Experto en ASP.NET 3.5 GXUDFLRQW\SH PHVHV!GXUDFLRQ! <Web pais="AR"><%= URL %>.com.ar <Web pais="ES"><%= URL %>.es :HESDLV 0;! 85/!FRPP[:HE! FXUVR!
Las expresiones incrustadas se pueden utilizar en el nombre de una etiqueta (donde se espera un objeto ;1DPH), en el nombre de un atributo (;1DPH), en valor de un atributo (cualquier objeto) o en el texto interior de un nodo (cualquier objeto). También se puede incrustar directamente un objeto ;$WWULEXWH, representando un atributo y su valor, o un ;(OHPHQW, representando el nodo raíz. Esto nos evita tener que generar nodos auxiliares o crearlos vacíos e ir incorporándoles contenido dinámico línea por línea. Con estas expresiones, rápidamente creamos contenido dinámico dentro de un archivo XML. Las expresiones incrustadas se pueden mezclar con expresiones LINQ, que nos permitirán, por ejemplo, insertar n cantidad de subnodos según alguna colección. Por ahora, sólo veremos un ejemplo rápido para comprender la idea. Supongamos que tenemos una lista de objetos, de clase Unidad Temática, cada uno con la propiedad Nombre: 'LPFXUVR B FXUVR! <nombre>Experto en ASP.NET 3.5 GXUDFLRQW\SH PHVHV!GXUDFLRQ! XQLGDGHV! <%= From Unidad In ListaUnidades _ 6HOHFWXQLGDG! 8QLGDG1RPEUH!XQLGDG!B %> XQLGDGHV! FXUVR!
Expresiones de consulta Las expresiones de consulta poseen su pilar en todo lo visto anteriormente, es por eso que lo hemos dejado para el final. Aquí utilizaremos expresiones Lambda, inicializadores de objetos, métodos extensores y mucho más. Como no podía Visual Studio - Firtman, Natale
Alfaomega
156
4- Visual Basic 2008
ser de otra manera, este tema está íntimamente ligado con LINQ, dado que es la herramienta que utilizaremos para nuestras consultas más adelante. Las expresiones de consulta nos van a permitir recuperar uno o más orígenes de datos y valores, mediante una serie de instrucciones que luego manipularemos en nuestra aplicación. El resultado obtenido puede ser, además, una fuente de datos para otra consulta. Quienes trabajen con lenguajes SQL notarán una gran similitud con ellos, aunque con algunas diferencias. Pero es importante destacar la posibilidad de realizar portabilidad de conocimientos, dado que nos ahorrará tiempos de aprendizaje y adopción de este tipo de búsquedas. La primera diferencia que encontramos es que comienzan con la cláusula From en lugar de la clásica Select, ubicándose ésta al final de la expresión. Para que podamos escribir nuestras consultas, el origen de los datos debe implementar ,(QXPHUDEOH2I7 o ,4XHU\DEOH2I7 . Veamos un ejemplo simple: 'LP$UU3DLVHV$V6WULQJ ^$UJHQWLQD8UXJXD\B "Alemania", "Canada", "Colombia"} Dim VarPaises = From Pais In ArrPaises _ Where Pais.StartsWith("A") _ Order By Pais Descending _ Select Pais For Each Pais As String In VarPaises Console.WriteLine("Pais: " & Pais) Next Console.Read()
Resultando: Pais: Argentina Pais: Alemania
Lo primero que hacemos es declarar una variable (VarPaises) que contiene el resultado de nuestra consulta y que, además, cumple con la interface ,(QXPHUDEOH2I7 , para luego poder recorrer y obtener los valores esperados. Luego, comenzaremos a escribir nuestra expresión: Seguido de From especificamos una variable, denominada variable de rango, que infiere el tipo de dato –en este caso 6WULQJ± del contexto actual. Utilizamos In para declarar cuál es nuestra colección que contiene los datos que serán evaluados. Notemos cómo hemos utilizado in para filtrar el origen mediante la cláusula Where. Alfaomega
Visual Studio - Firtman, Natale
Incorporaciones en Visual Basic 2008
157
En este caso, vamos a obtener todos los países de $UU3DLVHV>@ que comiencen con la letra A y especificaremos, también, que el resultado esté ordenado (mediante Order By … Descending) en forma descendente. Supongamos otro caso. Pensemos que tenemos nuestra clase Cliente con tres propiedades: Código, Denominación y Provincia. Y necesitamos obtener todos los clientes cuyo código de identificación es mayor que 89. Observemos el siguiente bloque de código para ver cómo podríamos hacerlo: Class Cliente Private _Codigo As Integer 3XEOLF3URSHUW\&RGLJR $V,QWHJHU Get 5HWXUQB&RGLJR End Get 6HW%\9DOYDOXH$V,QWHJHU B&RGLJR YDOXH End Set End Property Private _Denominacion As String 3XEOLF3URSHUW\'HQRPLQDFLRQ $V6WULQJ Get 5HWXUQB'HQRPLQDFLRQ End Get 6HW%\9DOYDOXH$V6WULQJ B'HQRPLQDFLRQ YDOXH End Set End Property Private _Provincia As String 3XEOLF3URSHUW\3URYLQFLD $V6WULQJ Get 5HWXUQB3URYLQFLD End Get 6HW%\9DOYDOXH$V6WULQJ B3URYLQFLD YDOXH End Set End Property End Class Dim clientes As New List(Of Cliente) clientes.Add(New Cliente With {.Codigo=100,_ Denominacion="Pablo Ferreyra"}) clientes.Add(New Cliente With {.Codigo=102,_ Denominacion="Gabriel Martinez"}) clientes.Add(New Cliente With {.Codigo=92,_ 'HQRPLQDFLRQ 0DULD$U]XHJD` Dim listClientes = From cli In clientes _ Where cli.Codigo > 90 _
Visual Studio - Firtman, Natale
Alfaomega
158
4- Visual Basic 2008
Select New With { _ .Nombre = cli.Nombre, .Codigo = cli.Codigo _ } For Each cliente In listClientes Console.WriteLine("Nombre: {0} Codigo: {1}",_ cliente.Nombre, cliente.Codigo) Next Console.Read()
Detengámonos en la definición de la consulta: ¿No notan algo extraño pero ya conocido? Sí, estamos utilizando tipos anónimos e inicializadores de objetos. Quizá lo que muchos se estarán preguntando es cómo se interpreta esta especie de código SQL para que pueda ser ejecutado. Básicamente, VB traduce las sentencias de consulta en invocaciones de métodos, de instancias o extensores, como por ejemplo Select, Where u OrderBy. Cada uno de ellos responde a un patrón de consultas y, mediante un mapeo entre sentencia-método, saben cómo deben procesar cada una de las instrucciones que hemos escrito. De hecho, podemos escribir nuestras consultas en notación de métodos y que para muchos resulte más fácil o entendible hacerlo de esta manera. Tenemos más opciones para escribir nuestras consultas, pero vamos a esperar al capítulo de LINQ para estudiarlas con detenimiento.
Alfaomega
Visual Studio - Firtman, Natale
Qué es
LINQ
159
5
Qué es LINQ es una nueva metodología para escribir consultas embebidas en nuestro código, de ahí su denominación: El acrónimo LINQ significa Consulta Integrada en el Lenguaje (en inglés, Language Integrated Query). El poder de LINQ está basado en todas las nuevas características que fueron incorporadas en los Frameworks 3.0 y 3.5 y en las últimas versiones de los lenguajes que vimos en capítulos anteriores. Con ejemplos sucesivos podremos comprender cómo LINQ agrega y extiende la funcionalidad de los lenguajes del .NET Framework. A lo largo de este capítulo iremos analizando los diversos espacios de nombres que tenemos, su sintaxis y cómo trabajar con LINQ.
Por qué se lo necesita Muchas veces cuando trabajamos sobre un proyecto tenemos que obtener y procesar información de diversas fuentes de datos, como por ejemplo un archivo XML, una colección de objetos en memoria o una base de datos. Para lograr ello utilizamos, dependiendo de cuál sea el origen de los datos, diferentes lenguajes. Cuando necesitamos consultar una tabla de una base de datos Microsoft SQL Server, escribimos nuestras consultas en T-SQL en nuestra capa de a datos para obtener, por ejemplo, un DataSet y luego procesarlo según nuestra necesidad. Visual Studio - Firtman, Natale
Alfaomega
160
5- Linq
Hay veces en las que trabajar con archivos XML se torna un poco molesto y siempre pensamos o esperamos alguna solución un poco más práctica, en lugar de tener que emplear XPath o XQuery. LINQ nos habilitará para escribir consultas independientemente de la fuente de información, manipulando el resultado de forma holgada y eficiente, proveyéndonos de una capa de abstracción extremadamente útil. Con LINQ podremos aprovechar todo el potencial de Visual Studio gracias al IntelliSense, que es un soporte para hacer depuración y verificación de errores en tiempo de compilación sobre cualquier origen de datos. En este capítulo repasaremos los principales proveedores de LINQ: s
LINQ a Objetos.
s
LINQ a XML.
s
LINQ a SQL.
s
LINQ a Dataset.
En la figura 5-1 podemos ver un esquema bastante ilustrativo de LINQ.
Fig. 5-1. Esquema de LINQ con lenguajes y expresiones.
En el día a día cuando trabajamos con consultas, inconscientes o no, hacemos una pequeña receta, de forma casi involuntaria: s
Determinamos cuál es la fuente de datos (un archivo, una base de datos).
s
Escribimos nuestra consulta que filtrará y modelará los datos.
Alfaomega
Visual Studio - Firtman, Natale
Sintaxis
s
161
Obtenemos el resultado y hacemos algo con ellos, por ejemplo asignarlos como un datasource de un componente para su representación gráfica.
Lo interesante es que utilizar LINQ no escapa a este pequeño listado y lo podemos aplicar a cualquier fuente de datos.
Sintaxis Para escribir nuestras consultas en LINQ utilizaremos muchos de los conocimientos estudiados sobre los lenguajes C# y VB. En los capítulos 3 y 4 vimos lo que son las expresiones de consulta, una manera de escribir muy similar al lenguaje SQL. Ésta no es la única forma, tenemos también la posibilidad de realizarlas mediante llamadas a métodos y, de hecho, en muchas circunstancias las tendremos que hacer con ellas, dado que no siempre podremos obtener de forma rápida y sin tanto código el dato que necesitamos mediante las expresiones de consulta.
Sintaxis de métodos Los operadores de consultas estándar son métodos (se encuentran dentro del espacio de nombres System.Linq) que nos van a ampliar el espectro de funcionalidades comparados con las expresiones de consulta, brindándonos la posibilidad de realizar operaciones como agregaciones, filtrados y ordenamiento, entre otros. Muchos de ellos tienen su representación en expresiones de consulta, como por ejemplo select u orderby, aunque como contracara hay métodos, como Average, que no poseen su mapeado en expresiones de consulta. Escribir consultas a fuentes de datos como expresiones de métodos no va a implicar una mejora de rendimiento, pero sí de usabilidad y de escalabilidad. Generalmente se recomienda escribirlas como expresiones de consulta, dado que son más fáciles de leer. Éstos poseen funcionalidad sobre objetos que implementan las interfaces IEnumerable
e IQueryable
, definiéndose como métodos de extensión. Lo interesante es que podemos anidarlos sobre una misma consulta para lograr profundidad y complejidad, donde el resultado del primer orden de la secuencia es la entrada para el segundo orden y así sucesivamente. Veamos un ejemplo con expresiones de consulta en C# y Visual Basic y el mismo pero hecho mediante métodos:
Visual Studio - Firtman, Natale
Alfaomega
162
5- Linq
int[] arrInts = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; // Como expresión de consulta var resultInts = from n in arrInts where n > 2 && n < 8 select n; // Mediante métodos var resultInts2 = arrInts .Where(n => n > 2 && n <8 ) .Select(n=>n);
Dim arrInts As Integer() = {1, 2, 3, 4, 5, 6, _ 7, 8, 9} ‘ Como expresión de consulta Dim resultInts = From n In arrInts _ Where n > 2 AndAlso n < 8 _ Select n ‘ Mediante métodos Dim resultInts2 = arrInts.Where(Function(n) n > 2 AndAlso n < 8).Select(Function(n) n)
Ahora supongamos que deseamos obtener la suma de todos los números del arreglo; utilizando métodos, podríamos escribirlo en tan solo una línea: var resultInts3 = arrInts.Sum(); Dim resultInts3 = arrInts.Sum()
Sum es un método extensor que calculará la suma de todos los números que tenemos en la colección arrInts. En la figura 5-2 podemos apreciar las diferentes opciones que nos propone el IntelliSense. A partir de ahora, en este capítulo utilizaremos sintaxis en C# y, en algunos casos, aclararemos diferencias con Visual Basic; pero más allá de las diferencias clásicas entre ambos lenguajes, las consultas LINQ son casi idénticas entre ambos.
Alfaomega
Visual Studio - Firtman, Natale
Sintaxis
163
Fig. 5-2. Funcionalidades al trabajar con consultas de sintaxis de métodos.
En la siguiente tabla observamos algunos de los métodos más usados: Método
Descripción
Average()
Calcula el promedio de una secuencia de valores.
Count()
Devuelve la cantidad de elementos en la colección.
Sum()
Calcula la suma de los elementos de la secuencia.
Max()
Retorna el valor máximo
Min()
Retorna el valor mínimo.
Skip()
Devuelve los elementos posteriores de una secuencia cuya posición es superior a n.
ToArray()
Crea una matriz a partir de un objeto IEnumerable
.
Union()
Proporciona la unión de elementos de dos secuencias
Where()
Filtra los valores de los elementos en función de un predicado.
Sintaxis de Consulta Repasando los capítulos anteriores, la sintaxis de consulta nos permite ejecutar LINQ de manera más simple y legible que con la sintaxis de método. La sintaxis de consulta comienza con el operador from, indicando el origen de datos, y una variable que hace los efectos de elemento de prueba sobre la colección origen y,
Visual Studio - Firtman, Natale
Alfaomega
164
5- Linq
como mínimo obligatorio, una sentencia select indicando el formato de salida de nuestra consulta. Así, la expresión de consulta LINQ más simple tendría la siguiente forma: // En C# var resultado = from o in origen select o; ‘ En Visual Basic Dim resultado = From o In Origen Select o;
La consulta anterior nos entrega exactamente la misma colección origen sin cambios. Siempre empezaremos una consulta con el operador from y la terminaremos con select.
Operadores Operadores de selección También son llamados operadores de proyección. Permiten definir una salida en particular respecto de la fuente de datos y otros operadores que hayan afectado a la consulta. El operador más utilizado es Select, que indica el formato de salida. Veamos algunos ejemplos de uso: 'H¿QLPRVXQDOLVWDGHHMHPSOR var Lista = new List
(); // Devolviendo la misma colección de origen var resultado = from Elemento in Lista select Elemento; // Devolviendo solo un atributo de la colección var resultado = from Elemento in Lista select Elemento.Atributo // Devolviendo una transformación sobre un atributo de la colección var resultado = from Elemento in Lista select Transformacion(Elemento.Atributo); // Creando un tipo anónimo para devolver más atributos // pero no todos var resultado = from Elemento in Lista select new {Elemento.Atributo1, Elemento.Atributo2};
Alfaomega
Visual Studio - Firtman, Natale
Sintaxis
165
// Idem anterior, pero cambiando nombres var resultado = from Elemento in Lista select new {Id=Elemento.Atributo1, Nombre=Elemento.Atributo2} // Seleccionando de dos origenes. SelectMany var resultado = from e1 in Lista, e2 in Lista2 select new {e1, e2}
Operadores de Restricción El operador Where permite filtrar el origen de datos según una o más condiciones lógicas. 'HYROYLHQGRODPLVPDFROHFFLyQGHRULJHQ¿OWUDGD var resultado = from Elemento in Lista where condicion_logica select Elemento;
La condición lógica puede incluir expresiones combinadas con And (&&) u Or (!!) y, también, el uso de métodos del framework, supeditado a que el proveedor LINQ que estemos utilizando (objetos, XML, SQL, etc.) haya implementado ese método. Por ejemplo, podremos utilizar una sentencia del estilo para filtrar clientes por nombre: // Trae quienes comiencen con A, a o á var Consulta = from c in Clientes where c.ToUpper().StartsWith(“A”) || c.StartsWith(“á”) select c; // Trae quienes contengan S.A. var Consulta = from c in Clientes where c.Contains(“S.A.”) select c;
El equivalente en Visual Basic sería: ‘ Trae quienes comiencen con A, a o á Dim Consulta = From c In Clientes _ Where c.ToUpper().StartsWith(“A”) _ OrElse c.StartsWith(“á”) _ Select c
Visual Studio - Firtman, Natale
Alfaomega
166
5- Linq
‘ Trae quienes contengan S.A. Dim Consulta = From c In Clientes _ Where c.Contains(“S.A.”) _ Select c
Por ejemplo, si en el código anterior utilizamos LINQ To Objects, siendo Clientes una colección del .NET Framework, entonces se utilizarán los métodos de la clase String. Si Clientes fuera una colección traída desde una base de datos SQL a través de LINQ To SQL, el código anterior se traduciría en una sentencia SQL, utilizando funciones T-SQL para proveer de la funcionalidad (por ejemplo, el operador LIKE al utilizar Contains). Esta traducción a T-SQL se realiza de manera automática y eficiente por el proveedor LINQ, que viene ya incluido en el framework. Operadores de Ordenación Como su nombre lo indica, cambian el orden de salida de los elementos de la colección, que previamente pudo o no ser filtrada por una condición Where. El operador principal es OrderBy y aquí vemos algunos ejemplos: // Ordena por un atributo nombre ascendente, luego de // seleccionar los habilitados var Consulta = from c in Clientes where c.EstaHabilitado orderby c.Nombre select c; // Si el elemento de la colección es ordenable, se puede // utilizar directamente var Consulta = from c in Clientes orderby c select c; // Ordenamos descendiente var Consulta = from c in Clientes orderby c.Deuda descending select c; // Ordenamos por dos atributos var Consulta = from c in Clientes orderby c.FechaAlta descending, c.Nombre select c; 0LVPRHMHPSORDQWHULRUSHURXWLOL]DQGRVLQWD[LVGHPpWRGRV var Consulta = Clientes.OrderByDescending(c=>c.FechaAlta). ThenBy(c=>c.Nombre);
Alfaomega
Visual Studio - Firtman, Natale
Sintaxis
167
Y en Visual Basic: ‘ Ordena por un atributo nombre ascendente, luego de ‘ seleccionar los habilitados Dim Consulta = From c In Clientes _ Where c.EstaHabilitado _ Order By c.Nombre _ Select c ‘ Si el elemento de la colección es ordenable, se puede ‘ utilizar directamente Dim Consulta = From c In Clientes _ Order By c _ Select c ‘ Ordenamos descendiente Dim Consulta = From c In Clientes _ Order By c.Deuda Descending _ Select c ‘ Ordenamos por dos atributos Dim Consulta = From c In Clientes _ Order By c.FechaAlta Descending, c.Nombre _ Select c µ0LVPRHMHPSORDQWHULRUSHURXWLOL]DQGRVLQWD[LVGHPpWRGRV Dim Consulta = Clientes.OrderByDescending(Function(c) c.FechaAlta).ThenBy(Function(c) c.Nombre)
Operadores de Agrupación Permiten generar grupos de elementos a partir de una agrupación dada. Por ejemplo: string[] Paises = { “Argentina”, “Argelia”, “España”, “Estonia”, “Uruguay” }; var AgrupadosPorLetra = from p in Paises group p by p[0] into g select new { Letra = g.Key, Paises = g };
En el ejemplo anterior estaríamos generando una colección donde cada elemento posee un atributo letra, que será un carácter, y un atributo Paises, que será una colección de strings de los países que comienzan con esa letra. Los grupos pueden complicarse lo que uno desee.
Visual Studio - Firtman, Natale
Alfaomega
168
5- Linq
Operadores de Elemento Estos operadores se aplican sobre una colección o consulta LINQ y devuelven un elemento de dicha colección. Algunos de los operadores son: First, que devuelve el primer elemento; FirstOrDefault, que devuelve el primer elemento o, si no lo tuviera, el valor por defecto para el tipo de datos de la colección; ElementAt, que devuelve un elemento en particular por índice y Single, que devuelve el primer elemento y da una excepción si la colección tuviera más de uno. Operadores de Agregación Los operadores en esta categoría se aplican a una consulta completa y ofrecen alguna funcionalidad de cálculo sobre la lista. Los operadores son: Count, Sum, Min, Max, Average y Fold. Dado un maestro de clientes, queremos obtener el total de cada uno de ellos agrupados por país. Para ello utilizaremos el método extensor Count: class Cliente { public int Codigo { get; set; } public string Denominacion { get; set; } public string Pais { get; set; } } class Program { static void Main(string[] args) { List
maestroClientes = new List
{ new Cliente {Codigo=501,Denominacion=”Carlos Mess”,Pais=”Colombia”}, new Cliente {Codigo=400,Denominacion=”Rodolfo Ibarra”,Pais=”Argentina”}, new Cliente {Codigo=874,Denominacion=”Carla Pietra”,Pais=”Chile”}, new Cliente {Codigo=372,Denominacion=”Lucia Galan”,Pais=”Colombia”}, new Cliente {Codigo=274,Denominacion=”Patricio Miguenz”,Pais=”Argentina”}, }; var resumenCli = from cli in maestroClientes orderby cli.Pais group cli by cli.Pais into clientes select new {Pais = clientes.Key, Cant = clientes.Count()};
Operadores de concatenación Aquí vamos a ver cómo concatenar dos colecciones, mediante el método Concat, para luego concatenar una tercera. Es un ejemplo sencillo de anidación de métodos. string[] paisesNorte = {“Canada”,”EEUU”}; string[] paisesSur = {“Argentina”, “Chile”, “Uruguay”}; var paises = paisesNorte.Concat(paisesSur) .Concat(new[]{“Brasil”});
Resultado de este bloque de código: Pais: Pais: Pais: Pais: Pais: Pais:
Canada EEUU Argentina Chile Uruguay Brasil
Operadores de división Estos operadores permiten particionar o paginar la salida, ya sea por alguna condición fija (Take, Skip) o dinámica (TakeWhile, SkipWhile). Así, por ejemplo, si queremos los diez primeros elementos de una colección, podemos indicar: var Lista10 = Lista.Take(10);
Y si queremos los siguientes diez, entonces hacemos: var ListaSiguientes10 = Lista.Skip(10).Take(10);
TakeWhile podemos utilizarlo como una especie de corte de control, que nos habilitará para recorrer una colección hasta que se cumpla una condición (en este ejemplo, hasta que el código del cliente sea menor que 800). Visual Studio - Firtman, Natale
Alfaomega
170
5- Linq
List
maestroClientes = new List
{ new Cliente {Codigo=501,Denominacion=”Carlos Mess”, Pais=”Colombia”}, new Cliente {Codigo=400,Denominacion=”Rodolfo Ibarra”, Pais=”Argentina”}, new Cliente {Codigo=874,Denominacion=”Carla Pietra”, Pais=”Chile”}, new Cliente {Codigo=372,Denominacion=”Lucia Galan”, Pais=”Colombia”}, new Cliente {Codigo=274,Denominacion=”Patricio Miguenz”, Pais=”Argentina”}, }; var misClientes = maestroClientes.TakeWhile(n => n.Codigo < 800);
El mismo ejemplo en Visual Basic nos quedaría: Dim maestroClientes As New List(Of Cliente) maestroClientes.Add(New Cliente With { .Codigo=501, .Denominacion=”Carlos Mess”, .Pais=”Colombia” }) ‘Resto de los clientes Dim misClientes = maestroClientes.TakeWhile(Function(n) n.Codigo < 800)
Operadores de Conjuntos Estos operadores permiten trabajar con operaciones de conjuntos matemáticos sobre dos colecciones de datos. Entre los operadores encontraremos Distinct (sin repeticiones), Union (unión), Intersect (intersección) y Except (excepción). En estas dos demostraciones, trabajaremos con dos listas cuyos elementos son marcas de autos. En el ejemplo 1, utilizaremos Union para obtener un conjunto de ambas colecciones sin duplicidad de elementos. En el segundo, mediante Intersect tendremos como resultado sólo los ítems que se encuentren tanto en la listaA como en la listaB. string[] listaA = { “Suabru”, “Honda”, “Audi”, “BMW” }; string[] listaB = { “Porsche”, “BMW”, “Ferrari”, “Honda” }; (MHPSOR var listaAB = listaA.Union(listaB);
Alfaomega
Visual Studio - Firtman, Natale
Sintaxis
171
(MHPSOR var listaAIntersecB = listaA.Intersect(listaB); foreach (var item in listaAB) { Console.WriteLine(“listaAUnionB - Marca: {0}”, item); } foreach (var item in listaAIntersecB) { Console.WriteLine(“listaAIntersecB - Marca: {0}”, item); } Console.Read();
Otros operadores Existen otros operadores que escapan al ámbito de este libro, tales como operadores de combinación (MRLQ), de igualdad, generacionales y de cuantificación, que tienen menos uso que los mencionados anteriormente. Se recomienda interiorizarnos y echarle un vistazo a todos los métodos que disponemos para sacar el mayor provecho a LINQ. Un buen punto de partida es el sitio de desarrolladores de Microsoft, el MSDN (msdn.microsoft.com).
Combinando las consultas A medida que construyamos consultas, nos vamos a dar cuenta de que sería bueno poder utilizar algunas características de expresiones de consulta y otras de expresiones de método en forma conjunta. Pues bien, eso es posible. Es decir que podremos utilizar todo el potencial de ambas para procesar una fuente de datos y obtener el resultado que esperamos. Retomando el ejemplo anterior, podríamos generar una consulta donde obtengamos la suma de todos los elementos de la colección arrInts mayores que 2 y menores que 8: int[] arrInts = { 1, 2, 3, 4, 5, 6, 7, 8, 9 }; var resultInts = (from n in arrInts where n > 2 && n < 8 select n).Sum(); Console.Write(resultInts);
Visual Studio - Firtman, Natale
Alfaomega
172
5- Linq
Notemos que, a diferencia de los ejemplos anteriores, estamos obteniendo un único valor, no una colección IEnumerable. Es por ello que no necesitamos escribir una instrucción foreach para poder imprimir la respuesta. Veamos el mismo ejemplo en VB: Dim arrInts As Integer() = {1, 2, 3, 4, 5, 6, _ 7, 8, 9} Dim resultInts = (From n In arrInts _ Where n > 2 AndAlso n < 8 _ Select n).Sum() Console.Write(resultInts)
Ejecución de consultas Es importante que entendamos en qué momento nuestras consultas son procesadas para obtener la información que deseamos. Muchas veces este concepto suele prestarse a confusión cuando trabajamos con LINQ, es por eso que en este apartado vamos a desmitificar algunas cuestiones. Para explicar cómo funcionan, utilicemos un ejemplo: List
arrInt = new List
{ 2, 3, 4, 5, 6, 7, 8, 9 }; var resultInt = from n in arrInt where n > 5 select n; foreach (var item in resultInt) { Console.WriteLine(item); } Console.Read();
El código anterior dará como respuesta los números comprendidos entre 6 y 9, inclusive. Quizá, es posible presuponer que nuestra consulta será procesada cuando finalice la sentencia con el select n. La realidad es que no, simplemente lo que estamos haciendo es declarar una variable (resultInt) en memoria que contiene una suma de instrucciones, las cuales serán ejecutadas cuando recorramos la colección, es decir, cuando comencemos a ejecutar el bloque de código encabezado por foreach, solicitando el primer elemento. A esto se lo conoce como ejecución diferida. Tomando el ejemplo anterior, agreguemos ahora una línea para comprobar el funcionamiento de las ejecuciones diferidas:
Alfaomega
Visual Studio - Firtman, Natale
Sintaxis
173
List
arrInt = new List
{ 2, 3, 4, 5, 6, 7, 8, 9 }; var resultInt = from n in arrInt where n > 5 select n; // Agregamos otros valores arrInt.AddRange(new[] {10, 20, 30, 40}); foreach (var item in resultInt) { Console.WriteLine(item); } Console.Read();
Lo que hicimos fue agregar cuatro valores más a la lista después de la representación de nuestra consulta. Si ejecutamos este código tendremos como salida los números comprendidos entre 6 y 9, inclusive, más los cuatro elementos que incorporamos. Esto nos va a permitir ir construyendo la consulta en diferentes etapas para luego ejecutarla, así como también la posibilidad de volver a utilizarla, dado que, como ya mencionamos, nuestra variable no contiene los datos, sino la sintaxis de la consulta. Deberemos tener cuidado cuando trabajamos con variables externas que pueden ser modificadas, dado que podrían causar una salida no esperada por nosotros (como vimos en el ejemplo anterior, que arrInt alteró el número de elementos de su colección). Observemos ahora otra pequeña modificación al ejemplo con el cual estamos trabajando: List
arrInt = new List
{ 2, 3, 4, 5, 6, 7, 8, 9 }; var resultInt = (from n in arrInt where n > 5 select n).Sum(); Console.WriteLine(resultInt); Console.Read();
Aquí estamos forzando una ejecución inmediata, dado que, para poder realizar la suma, el método Sum debe iterar por todos los elementos resultantes de la consulta. Si por el contrario, necesitáramos ejecutar de forma inmediata nuestra consulta para tener en memoria los resultados como una colección, podríamos reemplazar Sum por ToList o ToArray: Visual Studio - Firtman, Natale
Alfaomega
174
5- Linq
List
arrInt = new List
{2, 3, 4, 5, 6, 7, 8, 9 }; var resultInt = (from n in arrInt where n > 5 select n).ToList(); foreach (var item in resultInt) { Console.WriteLine(item); } Console.Read();
Tipos de expresiones LINQ posee diversos proveedores para poder trabajar con diferentes fuentes de datos. Repasaremos los principales, analizando diferentes ejemplos y cómo lograr productividad en cada uno de los escenarios donde lo implementaremos.
LINQ a Objetos Una de las opciones que tenemos es utilizar objetos en memoria enumerables como fuentes de datos. De hecho, la mayoría de los ejemplos que vimos hasta aquí fueron realizados con LINQ a Objetos (LINQ to Objects). Mediante LINQ a Objetos podremos hacer consultas sobre objetos que implementen IEnumerable
o su correspondiente interface no genérica, ya sean de tipos provistos por el .Net Framework o creadas por nosotros mismos. Por definición, cualquier colección que implemente la interface IEnumerable o IEnumerable
se la denomina secuencia, es decir que si tenemos una lista del tipo Cliente, estaríamos ante la presencia de una secuencia de clientes. Tengamos en mente que estas secuencias luego pueden ser fuentes de datos para algún componente de un formulario Windows o un formulario Web, como veremos a continuación en algunos ejemplos. Ejemplo 1 Supongamos que tenemos un supermercado con dos depósitos, A y B, cada uno con productos segmentados por rubros, y queremos hacer una aplicación Windows que nos permita obtener un listado de artículos cuyo stock sea superior que cero en función del rubro seleccionado.
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
175
Lo primero que haremos será generar un proyecto de aplicación de formularios Windows y dejaremos sus propiedades por defecto. Al formulario agregaremos los siguientes componentes: s
Dos Labels.
s
Un ListBox con la propiedad nombre: lstProductos (contendrá los productos).
s
Un ComboBox con la propiedad nombre: cmbRubro (aquí tendremos los rubros).
En la figura 5-3 podemos apreciar su distribución:
Fig. 5-3. Diseño del formulario Windows.
Agregaremos al proyecto una nueva clase, llamada Producto.cs, con tres propiedades que representarán el nombre, el rubro (al que pertenece el producto) y el stock: class Producto { public string Nombre { get; set; } public int Stock { get; set; } public string Rubro { get; set; } }
En la ventana de código de nuestro formulario, declararemos dos listas del tipo Producto que van a representar el inventario de cada depósito: Visual Studio - Firtman, Natale
Alfaomega
176
5- Linq
public partial class Form1 : Form { // Listado de productos de cada depósito List
inventarioA, inventarioB; public Form1() { InitializeComponent(); } }
Crearemos un método ArmarInventarios(), donde poblaremos las listas con algunos datos de ejemplos: private void ArmarInventarios() { inventarioA = new List
{ new Producto{Nombre=”Aceite Girasol”, Rubro=”Aceites”,Stock=10}, new Producto{Nombre=”Mostaza”,Rubro=”Aderezos”, Stock=12}, new Producto{Nombre=”Aceite Oliva”, Rubro=”Aceites”,Stock=0}, new Producto{Nombre=”Mayonesa”,Rubro=”Aderezos”, Stock=56}, new Producto{Nombre=”Gaseeosa Cola”, Rubro=”Bebidas”,Stock=43}, new Producto{Nombre=”Agua sin gas”, Rubro=”Bebidas”,Stock=42}, }; inventarioB = new List
{ new Producto{Nombre=”Resma A4”, Rubro=”Librería”,Stock=23}, new Producto{Nombre=”Lápiz 2H”, Rubro=”Librería”,Stock=7}, new Producto{Nombre=”Jabón Neutro”, Rubro=”Limpieza”,Stock=91}, new Producto{Nombre=”Detergente”, Rubro=”Limpieza”,Stock=5}, new Producto{Nombre=”Leche”,Rubro=”Lácteos”, Stock=0}, new Producto{Nombre=”Agua sin gas”, Rubro=”Bebidas”,Stock=11}, new Producto{Nombre=”Queso”,Rubro=”Lácteos”, Stock=0}, }; }
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
177
Este método será invocado desde el constructor de Form1.cs para tener los datos listos para realizar consultas. Primero necesitamos obtener una colección de rubros de ambos inventarios de los depósitos. Para ello, escribiremos el método ObtenerRubros, que tendrá como tipo de retorno un IEnumerable<string> donde codificaremos nuestra consulta LINQ: public IEnumerable<string> ObtenerRubros() { var rubros = inventarioA.Concat(inventarioB).Select(n => n.Rubro).Distinct(); return rubros; }
Notemos que hemos utilizado el método extensor Distinct, el cual evitará tener duplicidad de rubros. Nuestro próximo paso será enlazar el componente cmbRubro con el resultado del método ObtenerRubros. Para ello, generaremos el método PoblarRubros: public void PoblarRubros() { cmbRubro.DataSource = ObtenerRubros().ToList(); }
Básicamente, lo que hacemos es llamar al ObtenerRubros adicionándole el método extensor ToList, debido a que la propiedad DataSource del combobox espera un valor que cumpla con la interface IList, mientras que el retorno de ObtenerRubros es un IEnumerable<>. Obviamente, tanto PoblarRubros como ObtenerRubros podrían haber sido escritos en el mismo método, pero la idea es separarlos para una mejor comprensión del ejemplo. Ahora nos falta conseguir el listado de productos según un rubro especificado: public IEnumerable<string> ObtenerProductos(string rubro) { var invTotalStock = inventarioA.Union(inventarioB). Where(n=>n.Stock>0); var productos = (from producto in invTotalStock where producto.Rubro == rubro orderby producto.Nombre select producto.Nombre).Distinct(); return productos; }
Visual Studio - Firtman, Natale
Alfaomega
178
5- Linq
ObtenerProductos necesita como parámetro el rubro por el cual filtrar, es por ello que lo declaramos en la firma del mismo. Si observamos la consulta, notaremos que hemos combinado expresiones de consulta y expresiones con métodos. Gracias a Distinct tendremos un listado de productos únicos, sin tener en la secuencia nombres duplicados. En cierta manera, necesitamos actualizar el combobox que contiene el listado de productos en dos circunstancias: Al cargar el formulario y cuando el cambia de rubro. Es por eso que generamos el método Actualizar: public void Actualizar() { lstProductos.DataSource = ObtenerProductos(cmbRubro.SelectedItem.ToString()).ToList(); }
Simplemente asignamos la propiedad DataSource de nuestro listbox al método ObtenerProductos, enviándole como parámetro de entrada el rubro seleccionado. Al igual que en el caso anterior, el datasource espera un valor que implemente IList. Como mencionamos, es necesario que al cambiar de rubro se actualice el listado de productos. Para lograrlo simplemente en el evento SelectedIndexChanged de nuestro componente cmbRubro, invocaremos al método Actualizar: SULYDWHYRLGFPE5XEURB6HOHFWHG,QGH[&KDQJHGREMHFWVHQGHU EventArgs e) { Actualizar(); }
Ya tenemos todo listo. Ahora es cuestión de ordenar las cosas. En el constructor del formulario llamaremos a los métodos ArmarInventarios, PoblarRubros y Actualizar: public Form1() { InitializeComponent(); ArmarInventarios(); PoblarRubros(); Actualizar(); }
De esta forma, lograremos que al instanciarse el formulario Windows se armen los inventarios con los datos de pruebas; además, poblamos el combobox
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
179
con los rubros de productos de ambos depósitos y, por último, actualizamos lstProductos. Si ejecutamos la aplicación, tendríamos algo como lo siguiente (figura5-4):
Fig. 5-4. Ejecutando el ejemplo notaremos que no hay rubros duplicados.
El código completo aquí: Form1.cs: public partial class Form1 : Form { List
inventarioA, inventarioB; public Form1() { InitializeComponent(); ArmarInventarios(); PoblarRubros(); Actualizar(); } private void ArmarInventarios() { inventarioA = new List
{ new Producto{Nombre=”Aceite Girasol”, Rubro=”Aceites”,Stock=10}, new Producto{Nombre=”Mostaza”,Rubro=”Aderesos”, Stock=12},
Visual Studio - Firtman, Natale
Alfaomega
180
5- Linq
new Producto{Nombre=”Aceite Oliva”, Rubro=”Aceites”,Stock=0}, new Producto{Nombre=”Mayonesa”,Rubro=”Aderesos”, Stock=56}, new Producto{Nombre=”Gaseeosa Cola”, Rubro=”Bebidas”,Stock=43}, new Producto{Nombre=”Agua sin gas”, Rubro=”Bebidas”,Stock=42}, }; inventarioB = new List
{ new Producto{Nombre=”Resma A4”, Rubro=”Librería”,Stock=23}, new Producto{Nombre=”Lápiz 2H”, Rubro=”Librería”,Stock=7}, new Producto{Nombre=”Jabón Neutro”, Rubro=”Limpieza”,Stock=91}, new Producto{Nombre=”Detergente”,Rubro=”Limpieza”, Stock=5}, new Producto{Nombre=”Leche”,Rubro=”Lácteos”, Stock=0}, new Producto{Nombre=”Agua sin gas”, Rubro=”Bebidas”,Stock=11}, new Producto{Nombre=”Queso”,Rubro=”Lácteos”, Stock=0}, }; } public void PoblarRubros() { cmbRubro.DataSource = ObtenerRubros().ToList(); } /// <summary> /// Actualiza el listado de productos que tienen stock /// public void Actualizar() { lstProductos.DataSource = ObtenerProductos(cmbRubro. SelectedItem.ToString()).ToList(); } /// <summary> /// Devuelve todos los rubros de ambos inventarios /// ///
IEnumerable<string>
public IEnumerable<string> ObtenerRubros() {
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
181
var rubros = inventarioA.Concat(inventarioB). Select(n => n.Rubro).Distinct(); return rubros; } /// <summary> /// Devuelve productos que poseen stock > 0 de ambos /// inventarios /// /// <param name=”rubro”>El rubro seleccionado en lstRubros ///
IEnumerable<string>
public IEnumerable<string> ObtenerProductos(string rubro) { var invTotalStock = inventarioA.Union(inventarioB). Where(n=>n.Stock>0); var productos = (from producto in invTotalStock where producto.Rubro == rubro orderby producto.Nombre select producto.Nombre).Distinct(); return productos; } SULYDWHYRLGFPE5XEURB6HOHFWHG,QGH[&KDQJHGREMHFW sender, EventArgs e) { Actualizar(); } }
LINQ a XML Como todos sabemos, XML fue adoptado desde hace ya varios años como un estándar y nos ha ayudado a ordenar nuestros datos de forma excelente. Hoy en día, está presente en diversos ámbitos como archivos de configuración, es utilizado como repositorio de datos, en servicios Web, como medio de intercambio de información entre sistemas dispares, etc. Pero trabajar con XML, independientemente del analizador/traductor (más conocido como parser), muchas veces resultaba bastante incómodo, tanto para la construcción de un modelo basado en XML, como para realizar consultas empleando la API del W3C DOM XML. Muchos hemos utilizado XPath, XQuery o alguna API desarrollada por terceros para minimizar el código necesario en nuestros proyectos.
Visual Studio - Firtman, Natale
Alfaomega
182
5- Linq
Disponemos ahora de un proveedor LINQ a XML, una nueva API con un modelo de objetos, que nos permitirá trabajar con XML de forma mucha más rápida y sencilla, tanto para escribir nuestras consultas como para la manipulación de datos jerárquicos. Notaremos que, luego de trabajar con esta interface de programación, nuestro código será más intuitivo y cómodo si lo comparamos con el anterior. Hay un número importante de clases que utilizaremos, que se encuentran en el espacio de nombres System.Xml.Linq y en System.Xml.Schema, pero veamos primero en la figura 5-5 la estructura jerárquica de clases.
Fig. 5-5. Jerarquía de clases en LINQ a XML.
Repasemos entonces las novedades que tiene esta nueva API para los desarrolladores, para luego ir directamente a ver un ejemplo. Construcciones funcionales Se denomina construcción funcional a la posibilidad de crear objetos y su instanciación con valores en una misma declaración, gracias a la implementación de métodos sobrecargados para lograr una estructura de árbol XML. Veamos un ejemplo en C#: XElement Celulares = new XElement(“Celulares”, new XElement(“Marcas”, new XElement(“Motorola”,
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
183
new XAttribute(“ID”, 1), new XElement(“Modelo”, “V3i”), new XElement(“Modelo”, “U3”), new XElement(“Modelo”, “V400P”)), new XElement(“Nokia”, new XAttribute(“ID”, 2), new XElement(“Modelo”, “N95”), new XElement(“Modelo”, “6800”), new XElement(“Modelo”, “6620”))));
El resultado sería: xml =
<Marcas> <Motorola ID=”1”> <Modelo>V3i <Modelo>U3 <Modelo>V400P
<Modelo>N95 <Modelo>6800 <Modelo>6620
Notemos la utilización de los métodos anidados para lograr nuestro cometido y la facilidad con la que podemos interpretar el esquema, realmente mucho más sencillo. Esto implica además cierta facilidad para reutilizar el código, simplemente copiando y pegando podremos extender los datos muy claramente. Recordemos que en Visual Basic el mismo código es más simple: Dim Celulares As XElement =
<Marcas> <Motorola ID=”1”> <Modelo>V3i <Modelo>U3 <Modelo>V400P
<Modelo>N95 <Modelo>6800 <Modelo>6620
Visual Studio - Firtman, Natale
Alfaomega
184
5- Linq
Centricidad en los elementos Antes de utilizar la nueva API de LINQ a XML, para generar un documento XML debíamos generar primero un objeto XmlDocument para luego sí anexarle los del tipo XmlElement: XmlDocument xDocCelulares = new XmlDocument(); XmlElement xElemCelulares = xDocCelulares. CreateElement(“Celulares”);
En Visual Basic: Dim xDocCelulares As New XmlDocument() Dim xElemCelulares = xDocCelulares.CreateElement(“Celulares”)
Con la nueva API disponemos de otra opción. Ahora podremos generar esos objetos sin crear primeramente un XmlDocument, tal como hemos visto en el ejemplo de construcciones funcionales, donde nunca hemos instanciado un XmlDocument y obtuvimos el mismo resultado. Espacio de nombres y prefijos Otra de las ventajas de utilizar LINQ a XML consiste en no tener que declarar explícitamente un prefijo para los espacios de nombres. Debido a que este alias puede adoptar diferentes valores a lo largo del árbol del XML, muchas veces puede prestarse a confusión, obteniendo el valor de un nodo no deseado o, quizá, una excepción. Al procesar un documento, la API resuelve cada prefijo a su correspondiente URI, habilitándonos a acceder a través de éste. De todas maneras, si lo deseamos aún, tendremos soporte para continuar trabajando mediante los alias. Si corremos y depuramos el ejemplo de los celulares, notaremos que el elemento Celulares tiene el valor NameSpace = {} y veremos que LocalName = Celulares. NameSpace adopta ese valor porque nunca definimos un espacio de nombre. Para asignar uno, tenemos varios caminos: XNamespace miNS = “http://www.miempresa.mobi”; XElement Celulares = new XElement(miNS + “Celulares”, new XElement(miNS + “Marcas”, new XElement(miNS + “Motorola”, …
En el código anterior definimos nuestro espacio de nombres y en el constructor de XElement lo asignamos. Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
185
Si visualizamos en el depurador de Visual Studio, el valor de NameSpace para Celulares es { http://www.miempresa.mobi }. Otra forma de hacerlo es mediante nombre extendido, que responde a la forma {espacio de nombre}nombre local: XElement Celulares = new XElement(“{http://www.miempresa.mobi}Celulares”, new XElement(“{http://www.miempresa.mobi}Marcas”, new XElement(“{http://www.miempresa.mobi}Motorola”,
La respuesta de ambos ejemplos sería:
<Marcas> <Motorola ID=”1”> <Modelo>V3i <Modelo>U3 <Modelo>V400P
<Modelo>N95 <Modelo>6800 <Modelo>6620
Accediendo a los valores Se ha mejorado sustancialmente el a elementos y valores en LINQ a XML. Con la API anterior debíamos crear, por ejemplo, objetos del tipo XmlNode para recuperar el valor de un nodo en particular y escribir mucho más código cuando el árbol de los datos era complejo y necesitábamos recorrerlo. Ahora podremos obtener los valores de diversas maneras: Mediante el método ToString, la propiedad Value o realizando un cast a un tipo de dato al cual pueda ser convertido: XElement xmlAuto = new XElement(“Marca”, “Subaru”); // Implícitamente llama al método ToString() Console.WriteLine(xmlAutos); Console.WriteLine(xmlAutos.Value); Console.WriteLine((string)xmlAutos); XElement xmlPersona = new XElement(“Edad”, 29); Console.WriteLine(xmlPersonas); Console.WriteLine(xmlPersonas.Value); Console.WriteLine((int)xmlPersonas); Console.Read();
Visual Studio - Firtman, Natale
Alfaomega
186
5- Linq
La impresión en pantalla sería: <Marca>Subaru Subaru Subaru <Edad>29 29 29
Métodos para operar en XML En el siguiente cuadro presentaremos algunos de los métodos más utilizados en el día a día para realizar consultas y modificaciones a nuestras estructuras XML. Método
Descripción
XContainer.Element
Devuelve el primer nodo hijo, especificando el nombre como argumento.
XContainer.Elements
Retorna una colección IEnumerable<XElement> de elementos hijos. Posee sobrecarga.
XContainer. Descendants
Devuelve una colección de elementos descendientes. Sobrecargado.
XNode.Remove
Remueve el nodo de su padre.
XNode.Ancestors
Retorna los ancestros de un elemento determinado. Posee sobrecarga.
XNode. ElementsBeforeSelf
Devuelve una colección de elementos relacionados que se encuentran antes de un nodo. Sobrecargado.
XNode. ElementsAfterSelf
Devuelve una colección de elementos relacionados que se encuentran después de un nodo. Sobrecargado.
XElement. AncestorsAndSelf
Retorna una colección de elementos ancestros incluyendo éste. Posee sobrecarga.
XElement. DescendantsAndSelf
Retorna una colección de elementos descendientes incluyendo éste. Posee sobrecarga.
XNode.ReplaceWith
Reemplaza un nodo por el contenido especificado.
XContainer.Add
Agrega el contenido especificado como elementos hijos de un XContainer.
Nota: XContainer representa a un nodo, que a su vez puede contener otros nodos. Sus métodos y propiedades son de gran utilidad al trabajar con LINQ a XML; es por ello que es importante revisar la documentación que tenemos en el sitio MSDN de Microsoft para conocer todo su potencial.
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
187
Basándonos en el ejemplo de los celulares, supongamos que deseamos obtener la lista de modelos que pertenecen a la marca Motorola. El código sería algo así: IEnumerable<XElement> celMotorola = Celulares. Descendants(“Motorola”).Elements(“Modelo”); foreach (var cel in celMotorola) { Console.WriteLine((string)cel); } Console.Read();
Respuesta: V3i V3 V400P
Veamos un ejemplo de cómo utilizar LINQ a XML en un proyecto Windows. Pensemos que tenemos el inventario de nuestra empresa en un archivo XML como el siguiente:
<producto ID=”1”>
MEM51201
<descripcion>Memoria 512 DDR
Memorias
<deposito>Deposito-A
A1
<stock>1000
<producto ID=”2”>
MEM100001
<descripcion>Memoria 1GB DDR2
Memorias
<deposito>Deposito-A
A2
<stock>290
<producto ID=”3”>
LCD19005
<descripcion>LCD 19 Pulgadas
Monitores
<deposito>Deposito-B
B1
<stock>178
Tenemos dos depósitos y otras tantas ubicaciones donde se encuentra almacenada nuestra mercadería. La idea es armar una aplicación que nos permita filtrar por depósito, poder llevar el stock a cero y realizar una exportación de las modificaciones realizadas. Comencemos creando un proyecto de formularios Windows y arrastraremos algunos controles como muestra la figura 5-6:
Fig. 5-6. Diseño de la aplicación de inventarios.
El nombre de la grilla es dataGridView1, el correspondiente al botón “Borrar Stock” es btnBorrar y el del botón “Exportar” es btnExportar. Lo primero que haremos será definir una variable XDocument en nuestra clase form1.cs: XDocument xmlInventario;
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
189
Crearemos un método para cargar nuestro archivo XML, en donde invocaremos al método Load de XDocument: public void CargarInventario() { xmlInventario = XDocument.Load(@”C:\Inventarios\Inventario. xml”); }
Como paso siguiente, debemos poblar al combo de depósitos con los existentes en el archivo; para ello generamos otro método: public void CargarComboDepositos() { var xDeposito = xmlInventario.Descendants(“producto”). Elements(“deposito”).Select(n=> n.Value).Distinct(); foreach (var item in xDeposito) { cmbDeposito.Items.Add(item); } cmbDeposito.SelectedIndex = 0; }
Notemos cómo mediante los métodos extensores vamos armando nuestra ruta en la jerarquía del XML para conseguir el dato buscado. Luego forzamos a seleccionar el primer índice en el combo, para que al cargar el formulario haya uno seleccionado. Nuestra tarea siguiente será la de armar un método que recibe como parámetro el depósito por el cual filtrar, para mostrar el resultado de nuestra consulta: public void ActualizarGrilla(string deposito) { var xProductos = from prod in xmlInventario. Descendants(“producto”) where prod.Element(“deposito”).Value == deposito select new { ID = (int)prod.Attribute(“ID”), Código = (string)prod.Element(“codigo”), Descripción = (string)prod. Element(“descripcion”), Categoría = (string)prod. Element(“categoria”),
Visual Studio - Firtman, Natale
Alfaomega
190
5- Linq
Stock = (int)prod.Element(“stock”), Depósito = (string)prod.Element(“deposito”), Ubicación = (string)prod.Element(“ubicacion”) }; // Cantidad de ítems lblRegistros.Text = xProductos.Count().ToString(); // Cantidad de categorías de productos lblCategorias.Text = xProductos.Where(n => n.Categoría.Length > 0).GroupBy(n => n.Categoría).Count().ToString(); // Cantidad de ubicaciones en función del depósito lblUbicaciones.Text = xProductos.Where(n => n.Ubicación.Length > 0).GroupBy(n => n.Ubicación).Count().ToString(); dataGridView1.DataSource = xProductos.ToArray(); }
Aquí, además, estamos actualizando las etiquetas correspondientes a las cantidades de productos encontrados, categorías y ubicaciones. Sabemos que cuando el seleccione un depósito en el combo, debemos actualizar nuestra grilla. Para ello, si hacemos doble clic sobre el componente cmbDeposito, Visual Studio generará el evento asociado por defecto, en este caso SelectedIndexedChanged:
SULYDWHYRLGFPE'HSRVLWRB6HOHFWHG,QGH[&KDQJHGREMHFWVHQGHU EventArgs e) { // Actualizamos la grilla y enviamos como parámetro el // depósito seleccionado ActualizarGrilla(cmbDeposito.SelectedItem.ToString()); }
Otro requerimiento que tenemos es que, al iniciar el formulario, se cargue el archivo XML en memoria y luego se carguen los depósitos en el combo. Para lograrlo, simplemente debemos llamar a los métodos CargarInventario y CargarComboDepositos desde el constructor: public Form1() { InitializeComponent(); CargarInventario(); CargarComboDepositos(); }
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
191
El botón Borrar tiene por finalidad llevar el stock de productos que se encuentren en nuestra grilla a cero. Para tal operación, creamos un método al que le pasaremos el depósito en cuestión y, mediante la instrucción foreach, utilizamos el método ReplaceWith de XElement: public void BorrarStock(string deposito) { var borrarStock = xmlInventario.Descendants(“producto”). Where(n => n.Element(“deposito”).Value == deposito). Elements(“stock”); foreach (XElement item in borrarStock) { item.ReplaceWith(new XElement(“stock”,0)); } }
En el evento clic del botón realizamos la invocación al mismo y al método ActualizarGrilla para actualizar los datos representados: SULYDWHYRLGEWQ%RUUDUB&OLFNREMHFWVHQGHU(YHQW$UJVH { BorrarStock(cmbDeposito.SelectedItem.ToString()); // Actualizamos la grilla ActualizarGrilla(cmbDeposito.SelectedItem.ToString()); }
Realizar la exportación del objeto en memoria xmlInventario a un archivo XML es tan sencillo como llamar al método Save de XDocument. public void Exportar() { string archivo = “InventarioExportado.xml”; try { xmlInventario.Save(@”C:\Inventarios\Exportados\” + archivo); } catch (Exception ex) { MessageBox.Show(ex.Message,”Se detectó un error!!”, MessageBoxButtons.OK,MessageBoxIcon.Exclamation); return; } MessageBox.Show(“Exportación exitosa”, “Exportar”, MessageBoxButtons.OK, MessageBoxIcon.Asterisk); }
Visual Studio - Firtman, Natale
Alfaomega
192
5- Linq
Y el evento clic del botón Exportar quedaría así: SULYDWHYRLGEWQ([SRUWDUB&OLFNREMHFWVHQGHU(YHQW$UJVH { Exportar(); }
En nuestro caso hemos especificado la ruta de forma fija. Otra opción podría haber sido pedirle al que seleccione la carpeta de destino mediante un componente FolderBrowserDialog. Si ejecutamos la aplicación (figura 5-7), veremos cómo al seleccionar un depósito los datos son actualizados en la grilla. Otra prueba interesante es llevar a cero el stock de algunos productos, exportar a un archivo XML y, con el Internet Explorer u otro aplicativo, ver cómo fueron impactadas esas modificaciones en el archivo de destino.
Fig. 5-7. Ejecutando nuestra aplicación.
A continuación, el código completo:
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
using using using using using using using using using using
QDPHVSDFH(MHPSOR:LQ)RUP { public partial class Form1 : Form { XDocument xmlInventario; public Form1() { InitializeComponent(); CargarInventario(); CargarComboDepositos(); } public void CargarInventario() { xmlInventario = XDocument.Load(@”C:\Inventarios\ Inventario.xml”); } public void CargarComboDepositos() { var xDeposito = xmlInventario. Descendants(“producto”).Elements(“deposito”).Select(n=> n.Value).Distinct(); foreach (var item in xDeposito) { cmbDeposito.Items.Add(item); } cmbDeposito.SelectedIndex = 0; } public void ActualizarGrilla(string deposito) { var xProductos = from prod in xmlInventario. Descendants(“producto”) where prod.Element(“deposito”).Value == deposito select new { ID = (int)prod.Attribute(“ID”), Código = (string)prod.Element(“codigo”),
LINQ a SQL Al igual que LINQ a XML, disponemos de una nueva API en el .Net Framework 3.5 para utilizar LINQ en bases de datos relacionales SQL Server 2000, 2005 y 2008, SQL Express y SQL Server Compact 3.5 Si necesitamos trabajar con otras marcas de bases de datos, podemos crear nuestro propio proveedor. Lo que LINQ a SQL nos ofrece es un modelo de objetos que se mapean a la base de datos física, proveyéndonos de una delgada aislación para realizar operaciones de consultas y modificaciones sobre las diversas tablas y vistas del sistema. Mediante esta API podremos utilizar procedimientos almacenados y funciones definidas por el de forma transparente, siempre como un modelo de objetos. Entidades de clases La base fundamental de LINQ a SQL son las clases de entidades, en donde cada tabla o vista de nuestra base de datos será representada en nuestro modelo de objetos como una clase, que a su vez posee tantas propiedades como columnas exista en su correspondiente tabla o vista.
Visual Studio - Firtman, Natale
Alfaomega
196
5- Linq
Las relaciones claves primarias - claves foráneas son mapeadas como asociaciones, donde una entidad padre contiene una colección de clases de entidades hijas y éstas mantienen una referencia a su padre. Básicamente, hay cuatro formas de generar estas clases: %
Generando a mano las entidades mediante una serie de atributos (espacio de nombres utilizado para ello: System.Data.Linq.Mapping).
%
A través de un archivo externo con formato XML.
%
Mediante la herramienta de consola SqlMetal.exe (viene con el Visual Studio 2008).
%
Utilizando el diseñador gráfico de relaciones de objetos integrado en el Visual Studio 2008.
Nosotros haremos uso de la última opción, dado que es la más intuitiva y fácil de comprender en nuestro primer o y, quizá, sea la que emplearemos más comúnmente en el día a día. Antes habíamos mencionado que podemos utilizar LINQ a SQL con la base de datos SQL Server Compact 3.5. Pero, al momento de escribir este libro, el diseñador de Visual Studio no soporta esta caracteristica, por ende deberemos hacer uso de SqlMetal.exe para crear nuestras clases de entidades. El DataContext Es una clase fundamental en este tema, al igual que las clases de entidades, dado que provee importantes funciones tales como la conexión a la base de datos, la istración de los cambios que ocurran en nuestro modelo de objetos, persistencia y seguimiento de modificaciones. Además, es quien traduce en lenguaje T-SQL las consultas y operaciones que hagamos sobre las entidades. Posee un constructor sobrecargado para enviar como parámetro, entre otras cosas, un objeto SqlConnection o un string con la cadena correspondiente. Una ventaja que ofrece trabajar con el diseñador relacional de objetos es que crea y configura por nosotros el datacontext. En la figura 5.8 tenemos una representación de los mapeos entre el modelo de objetos y el modelo relacional. Nada mejor para comprender un tema que analizar un ejemplo. Para esta demostración utilizaremos la base de datos Northwind. Para aquellos que tengan instalado SQL Server 2005, la pueden descargar del sitio de descargas de Microsoft y correr un script muy sencillo que la generará muy rápidamente. Crearemos un proyecto Web y dispondremos algunos controles en el formulario, tal como muestra la figura 5-9.
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
197
Fig. 5-8. Mapeos entre objeto y modelo relacional.
Fig. 5-9. Distribución de componentes.
Nuestra intención es obtener un listado de pedidos de empresas argentinas con sus respectivos vendedores; para ello, nos basaremos principalmente en tres tablas : Orders (pedidos), Customers (clientes) y Employees (empleados o vendedores). Luego de generado el proyecto, utilizaremos el diseñador relacional de objetos para generar nuestro modelo de objetos, que se mapearán a las tablas antes mencionadas. Posicionados sobre el proyecto agregaremos un nuevo ítem, bajo la categoría Datos, seleccionaremos la plantilla Clases de LINQ a SQL y le asignaremos como nombre Northwind.dbml, tal como muestra la figura 5-10.
Visual Studio - Firtman, Natale
Alfaomega
198
5- Linq
Fig. 5-10. Agregando nuestro archivo dbml.
Éste nos mostrará la pantalla del diseñador, que se encargará, entre otras cosas, de generar nuestra clase DataContext, cuyo nombre por defecto es nombre-archivo-dbmlDataContext (figura 5-11).
Fig. 5-11. Vista del diseñador relacional de objetos.
Nuestro próximo paso es agregar las tablas mencionadas a la superficie del diseñador. Para esto, debemos tener una conexión ya generada a nuestro servidor SQL en el explorador de servidores. En este punto es donde se realizan los mapeos y asociaciones, las tablas son traducidas en clases y sus columnas en propiedades, mientras que las relaciones clave primaria–clave foránea son Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
199
representadas en asociaciones. Desde aquí podemos realizar modificaciones en los nombres y configurar el comportamiento para inserciones, actualizaciones y borrado. En la figura 5-12 se muestra este punto.
Fig. 5-12. Mapeos a clases y asociaciones de las tablas y sus relaciones PK-FK .
Vayamos ahora a la vista de código de la clase Default.aspx.cs. Debemos construir una consulta que recolecte los datos que precisamos y sean enlazados al control GridView: public void MostrarPedidos() { // Intanciamos nuestra clase DataContext ya generada por el // diseñador NorthwindDataContext dc = new NorthwindDataContext(); // Consulta var pedidos = from p in dc.Orders MRLQFLQGF&XVWRPHUVRQS&XVWRPHU,'HTXDOV c.CustomerID MRLQHLQGF(PSOR\HHVRQS(PSOR\HH,'HTXDOV e.EmployeeID where c.Country == “Argentina” orderby p.OrderID select new { Pedido = p.OrderID, Empresa = c.CompanyName, o = c.Name,
Invocaremos este método desde el Page_Load de la página y desde el evento Click del botón Mostrar Pedidos. Si ejecutamos la aplicación, tendremos en pantalla el listado de pedidos correspondiente a clientes residentes en Argentina. Tomemos como caso el pedido número 10 409 (figura 5-13), cuyo vendedor es Janet Leverling con código de vendedor 3.
Fig. 5-13. Pedidos que cumplen nuestro criterio de búsqueda.
Con LINQ a SQL no sólo podremos hacer consultas, sino también inserciones, actualizaciones y borrado desde el modelo de objetos, para luego impactar en la base de datos relacional. En el código siguiente vemos cómo crear e insertar un vendedor:
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
201
public void InsertarVendedor() { NorthwindDataContext dc = new NorthwindDataContext(); Employees employee = new Employees(); 'DPRVYDORUDODVSURSLHGDGHVGHOREMHWR(PSOR\HHV employee.FirstName = “Pablo”; employee.LastName = “Rodriguez”; employee.Title = “Vendedor”; dc.Employees.InsertOnSubmit(employee); try { dc.SubmitChanges(); } catch (Exception ex) { // Loguear ex.Message } }
Notemos la llamada al método SubmitChanges del datacontext. Aquí es donde persisten todas las modificaciones pendientes de realizar a la base de datos. Es muy importante tener en cuenta que podemos tener conflictos y errores al ejecutar esta línea; por ello, debemos ejecutarla dentro de un bloque Try-Catch y manejar cualquier excepción que pudiera ser elevada. En el evento Click del botón Crear Vendedor invocamos este método. Al ejecutar la solución y hacer clic sobre dicho componente, podremos ver en la base de datos este nuevo registro (figura 5.14). Tomemos nota del ID que posee: 10.
Fig. 5-14. Nuevo vendedor insertado con LINQ.
Visual Studio - Firtman, Natale
Alfaomega
202
5- Linq
Nuestra intención será reemplazar al vendedor que posee el pedido 10 409 por el nuevo generado: public void ActualizarPedido() { NorthwindDataContext dc = new NorthwindDataContext(); // Buscamos el pedido Orders pedido = dc.Orders.Single(n => n.OrderID == 10409); pedido.EmployeeID = 10; try { dc.SubmitChanges(); } catch (Exception ex) { // Loguear ex.Message } }
Desde el evento Click del botón Actualizar Pedido llamaremos a este método. Si corremos la solución, presionamos este botón y luego hacemos clic en Mostrar Pedidos, podemos observar que han cambiado los valores de CodigoVendedor y Vendedor por el nuevo creado en el paso anterior. Como último paso, restauraremos el vendedor original del pedido y procederemos a borrar de la tabla el creado por nosotros: public void BorrarVendedor() { NorthwindDataContext dc = new NorthwindDataContext(); Orders pedido = dc.Orders.Single(n => n.OrderID == 10409); pedido.EmployeeID = 3; try { dc.SubmitChanges(); } catch (Exception ex) { // Loguear ex.Message } Employees vendedor = dc.Employees.Single(n=> n.EmployeeID==10); dc.Employees.DeleteOnSubmit(vendedor); try { dc.SubmitChanges();
El primer paso es cargar al vendedor anterior y luego borrar el generado. Si lo hubiésemos hecho al revés, obtendríamos un error, dado que entre Orders y Employees hay una relación PK-FK. Este método debe ser invocado desde el evento Click del botón Borrar Vendedor. En tiempo de ejecución, al presionar el botón notaremos que ahora el pedido 10 409 tiene su valor original y si ejecutamos una consulta a la tabla Employees veremos que no tenemos más este registro. Ejecutar un procedimiento almacenado o una función es tan sencillo como llamar a un método. El siguiente código es de aplicación de consola, utilizando el mismo DataContext del ejemplo anterior. class Program { static void Main(string[] args) { NorthwindDataContext dc = new NorthwindDataContext(); (QYLDPRVDFRQVRODODVHQWHQFLD764/HMHFXWDGD dc.Log = Console.Out; // El procedimiento almacenado es mapeado como un método. // Devuelve los 10 productos más caros var Productos = dc.Ten_Most_Expensive_Products(); foreach (var item in Productos) { Console.WriteLine(item.TenMostExpensiveProducts + “-” + item.UnitPrice); } Console.Read(); } }
LinqDataSource Es un control para formularios Web que lo podemos usar para conectarnos a orígenes de datos, ya sea una base de datos o una colección en memoria, y enlazarlo a controles como GridView, ListView o DropDownList, entre otros. Dejaremos su uso para el capítulo de ASP.NET. Visual Studio - Firtman, Natale
Alfaomega
204
5- Linq
LINQ a DataSet Como su nombre lo indica, es el proveedor que nos permitirá realizar consultas LINQ sobre datasets, utilizando la misma sintaxis de consulta tal como lo hemos hecho hasta aquí. Para soportar esto, se han agregado métodos extensores en las clases DataRowExtension y DataTableExtension, entre otras. Podremos emplear LINQ tanto para dataset tipado como no tipado, aunque con algunas pequeñas diferencias que veremos luego. Lo importante de todo esto, es que en la mayoría de las aplicaciones se trabajan con datasets y, al programar en .Net Framework 3.5, podremos aprovechar toda la potencia de LINQ para la manipulación de datos de diversos orígenes a los cuales ADO.Net nos permita conectar. Para realizar consultas debemos trabajar con colecciones; es por eso que disponemos del método AsEnumerable, el cual sabemos que pertenece a DataTableExtension, que devolverá una tabla como una secuencia de DataRow ( IEnumerable
). DataSet no tipado Cuando nos encontramos frente a un dataset no tipado, sabemos que la operatoria de obtener y asignar valores requiere algunos pasos extra como producto de la ausencia de un archivo de esquemas, el cual muchas veces se traduce en errores en tiempo de ejecución. Se han agregado dos métodos extensores dentro de la clase estática DataRowExtension: Field
y SetField
y, como se imaginarán por su significado en ingles, los utilizaremos para recuperar y asignar, respectivamente, valores en un DataRow (clase que representa una fila en un DataTable). Sin entrar en mayores detalles, Field
resuelve algunos problemas cotidianos, dado que posee compatibilidad con valores null y soluciona un inconveniente cuando trabajamos con comparaciones entre filas, debido a un proceso conocido como boxing. Es importante que tengamos en cuenta que los tipos que reemplazaremos en
deben coincidir con el valor esperado en función de la columna que se esté tratando, caso contrario vamos a obtener una excepción InvalidCastException. A continuación vamos a ver un ejemplo donde utilizamos la base de datos Northwind, puntualmente las tablas Products y Categories, para obtener un listado de productos que pertenecen a una categoría puntual, que luego será enlazado a un DataGridView por medio de un bindingsource:
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
205
public void CargarDataSet() { // Listado de productos string strQueryProductos = “SELECT * FROM PRODUCTS”; // Listado de categorías string strQueryCategories = “SELECT * FROM CATEGORIES”; // Conexión a la base de datos string strConn = “Data Source=LAPTOPXP;Initial Catalog=Northwind;Trusted_Connection=yes”; // DataSet y carga de registros en los DataTable DataSet ds = new DataSet(“Inventario”); SqlDataAdapter da = new SqlDataAdapter(strQueryProductos, strConn); da.Fill(ds, “Productos”); da.SelectCommand.CommandText = strQueryCategories; da.Fill(ds, “Categorias”);
var inventario = from p in ds.Tables[“Productos”].AsEnumerable() MRLQFLQGV7DEOHV>³&DWHJRULDV´@$V(QXPHUDEOH on p.Field
(“CategoryID”) equals c.Field
(“CategoryID”) where c.Field<string>(“CategoryName”) == “Condiments” select new { Producto = p.Field<string>(“ProductName”), Categoria = c.Field<string>(“CategoryName”), PrecioUnidad = p.Field<decimal>(“UnitPrice”), Stock = p.Field<short>(“UnitsInStock”) }; bindingSourceInventario.DataSource = inventario; // Cantidad de registros encontrados string CantidadRegistros = inventario.Count().ToString(); }
Notemos de qué manera invocamos el método AsEnumerable de cada DataTable, para obtener un IEnumerable
y poder confeccionar nuestra consulta, y cómo cada método Field
tiene representado el tipo de dato en relación con la columna especificada. Supongamos que quisiéramos actualizar el precio unitario de todos los productos de la tabla, utilizaríamos SetField
de la siguiente manera: Visual Studio - Firtman, Natale
DataSet tipados Aquí la historia cambia. Escribiremos menos código y será mucho más intuitivo al no tener que utilizar AsEnumerable, Field o SetField, producto de que los objetos DataTable fuertemente tipados derivan de la clase TypedTableBase
que implementa la interface IEnumerable
. Observemos el siguiente ejemplo: public void CargarDataSetTipado() { string strQueryProductos = “SELECT * FROM PRODUCTS”; string strQueryCategories = “SELECT * FROM CATEGORIES”; string strConn = “Data Source=LAPTOPXP;Initial Catalog=Northwind;Trusted_Connection=yes”; // DataSet tipado ProductosDataSet Inventario = new ProductosDataSet(); SqlDataAdapter da = new SqlDataAdapter(strQueryProductos, strConn); da.Fill(Inventario, “Products”); da.SelectCommand.CommandText = strQueryCategories; da.Fill(Inventario, “Categories”); var inventarioSinFiltrar = from p in Inventario.Products MRLQFLQ,QYHQWDULR&DWHJRULHV on p.CategoryID equals c.CategoryID select new { Producto = p.ProductName, Categoria = c.CategoryName, PrecioUnidad = p.UnitPrice, Stock = p.UnitsInStock }; bindingSourceInventario.DataSource = inventarioSinFiltrar; string CantidadRegistros= inventarioSinFiltrar.Count(). ToString(); }
Alfaomega
Visual Studio - Firtman, Natale
Tipos de expresiones
207
Lo beneficioso de trabajar con datasets tipados es que podremos llamar directamente a las tablas y a sus columnas ayudados por el IntelliSense. CopyToDataTable Este método extensor nos habilitará para poder generar un DataTable desde otro ya existente, o desde una consulta, de la siguiente forma: public void CopiarTabla() { string strQueryProductos = “SELECT * FROM PRODUCTS”; string strConn = “Data Source=LAPTOPXP;Initial Catalog=Northwind;Trusted_Connection=yes”; ProductosDataSet Inventario = new ProductosDataSet(); SqlDataAdapter da = new SqlDataAdapter(strQueryProductos, strConn); da.Fill(Inventario, “Products”); var inventario = from p in Inventario.Products where p.ProductID > 10 select p; // Generando una tabla desde una ya existente DataTable tablaDuplicada = Inventario.Products. CopyToDataTable(); // Generando una tabla desde una consulta DataTable tablaDesdeConsulta = inventario.CopyToDataTable();
Lo interesante es que mediante este operador podremos obtener copias de tablas, donde los registros resultantes sean el producto de algún filtro en una consulta u operaciones de agregación o agrupación, entre otras. En el ejemplo estamos enlazando el bindingsource a la tabla generada a través de la consulta, pero también podríamos haber asignado tablaDuplicada. DataRowComparer Mediante esta clase podremos realizar comparaciones entre objetos DataRow basados en los valores de cada uno de ellos y no por referencia. Es muy utilizado cuando empleamos algunas de las operaciones de comparación, como Union o Distinct, donde emplean valuaciones por referencia y no por valor. Esto muVisual Studio - Firtman, Natale
Alfaomega
208
5- Linq
chas veces origina inconvenientes, dado que uno como desarrollador inconscientemente espera que la comparativa sea realizada por el valor específico que tiene el objeto y no por su posición en memoria. Pensemos que tenemos dos tablas, una con el inventario total de productos de nuestro depósito y otra de productos discontinuados en general. Si quisiéramos obtener el listado de ítems que ya no se fabrican (discontinuados) que figuran en el inventario general, podríamos escribir el código que figura a continuación: var resultado = tablaDiscontinuados. Intersect(tablaInventario, DataRowComparer.Default);
Alfaomega
Visual Studio - Firtman, Natale
209
ASP.NET
6
Historia Ya todos sabemos de qué se trata ASP.NET, la plataforma de desarrollo Web de Microsoft. También sabemos que, en los últimos años, el desarrollo de aplicaciones Web fue aumentando considerablemente respecto de los desarrollos para escritorio, fomentando cada vez más el uso del navegador Web como plataforma para ejecutar nuestras aplicaciones. Tecnologías Web 2.0, como AJAX, han fomentado más la plataforma Web, así como el concepto de servicios en la nube (cloud computing), donde nuestras aplicaciones residen en servidores virtuales istrados de manera sencilla y escalable sin límite. Esta evolución del mercado Web ha traído muchos cambios en la industria, hasta la aparición de Google en el mercado de los navegadores con su Google Chrome. Es por eso que no sorprende ver que ASP.NET es la plataforma con más cambios dentro del ambiente .NET, desde sus primeras versiones, y sigue evolucionando a un ritmo, a veces, alarmante. Pasado Así es que evolucionamos del antepasado, creado en 1996, ASP (del inglés, Active Server Pages, que significa Páginas Activas del Servidor) a la nueva y revolucionaria plataforma ASP.NET 1.0, en enero de 2002. Con sus ventajas y desventajas, la versión 1.1 solucionó algunos errores y nos ayudó con algunas cuestiones básicas de la plataforma. Pero fue con la versión ASP.NET 2.0, junto con Visual Studio 2005 y .NET Framework 2.0, que la plataforma dio un salto cuantitativo en el mercado de desarrollo Web. Todavía cuesta creer que haya un gran porcentaje de desarro-
Visual Studio - Firtman, Natale
Alfaomega
210
6- ASP.NET
lladores que sigan trabajando con ASP Clásico o ASP.NET 1.1. Es por eso que haremos un repaso de las características fundamentales que ASP.NET 2.0 agregó y que son la base de las últimas incorporaciones a la plataforma. Con la salida del Framework 3.0, ASP no ofreció nada distintivo y es por eso que se considera que ASP.NET 3.0 no existió. Siguió siendo ASP.NET 2.0, que se ejecutaba sobre el Framework 3.0. Los únicos agregados menores fueron, además del cambio de framework y sus novedades, la posibilidad de publicar y consumir servicios WCF (Windows Communication Foundation) así como de identificarse con Windows CardSpace. Entre la versión 2.0 y la siguiente versión oficial, se fueron lanzando algunos nuevos frameworks y productos por separado, como ser el excelente framework ASP.NET AJAX –el que detallaremos en el siguiente capítulo–. También se comenzó a trabajar en otros proyectos paralelos, en versiones preliminares, como ser Dynamic Data (Datos Dinámicos), Dynamic Languages (Lenguajes Dinámicos), ASP.NET MVC, entre otros. Presente La siguiente versión oficial fue ASP.NET 3.5. Esta versión fue lanzada junto con el .NET Framework 3.5 y Visual Studio 2008 y, además de incorporar algunos frameworks que estaban separados, como ASP.NET AJAX, incorporó soporte de LINQ, algunos nuevos controles de datos y soporte de WCF para generación de contenido en formato Web –como ser RSS o JSON–. Casi por primera vez en la historia de ASP, un Service Pack (SP) incorporó realmente notorias mejoras en la plataforma. Así, apenas algunos meses después de la salida de 3.5, se publicó ASP.NET 3.5 SP1, que es la última versión disponible mientras este libro es editado, junto con Visual Studio 2008 SP1 y .NET Framework 3.5 SP1. Esta última versión incorpora a la plataforma, oficialmente, el modelo Dynamic Data, soporte de navegación con el botón de Atrás para el framework de AJAX y algunas otras mejoras –como soporte de datos para SQL Server 2008–. Futuro El futuro de ASP.NET sigue con marcha firme y, al momento de editar este libro, ASP.NET 4.0 será la futura plataforma. Incorporará a todo lo anterior: Los frameworks actualmente aislados, como ser ASP.NET MVC, URL Routing y las plantillas AJAX (AJAX Templates), las cuales analizaremos más adelante.
Alfaomega
Visual Studio - Firtman, Natale
Proyectos ASP.NET
211
Proyectos ASP.NET Diferencias en Visual Studio Desde la versión 2008 SP1 de Visual Studio y Visual Web Developer, el entorno nos permite trabajar con dos modalidades: Modalidad Proyecto Aplicación Web (Web Application Project) o Sitio Web (Web Site). Para versiones anteriores de Visual Studio, es posible descargar un agregado gratuito para poder tener estos dos tipos de modalidad.
Fig. 6-1. En Visual Web Developer encontramos las dos metodologías de trabajo para ASP.NET.
Un sitio ASP.NET con modalidad Proyecto Aplicación Web trabaja con una solución de Visual Studio, al estilo de aplicaciones Windows Forms o aplicaciones ASP en Visual Studio 2003. Esto implica tener, por un lado, los archivos de la solución y del proyecto y por otro lado, los archivos que se han de publicar en el sitio Web (.aspx, entre otros). Este modelo, además utiliza una precompilación del código .NET (C# o Visual Basic), por lo que para cualquier cambio en el proyecto es necesario regenerarlo y publicar el DLL modificado. Un sitio ASP.NET con modalidad Sitio Web no es más que una carpeta en el disco con los archivos necesarios para el funcionamiento, como si se tratara del servidor Web en forma directa. Esto incluye .aspx, .aspx.cs, .aspx.vb, .dll y todo el código sin precompilar en la carpeta App_Code. Visual Studio - Firtman, Natale
Alfaomega
212
6- ASP.NET
Cualquier cambio en el proyecto, basta con cambiar el archivo en cuestión y refrescar la página en el navegador. El motor de ASP.NET detectará el cambio y realizará la compilación en el momento. Recordemos que este modelo no significa que las páginas no serán compiladas, con la consecuente mejora de rendimiento. El modelo no precompila, sino que lo hace en el momento que considera que la versión compilada que posee no es válida.
Tipos de Aplicaciones Web Si tenemos en cuenta ASP.NET 3.5 SP1, junto con los frameworks definitivos que se pueden descargar por separado y que serán parte integral de ASP.NET 4.0, al iniciar un proyecto nuevo en la plataforma ASP.NET tendremos que elegir qué modelo de trabajo vamos a utilizar. Veamos un repaso de cada uno de ellos. Formularios Web Los formularios Web, o Web Forms en inglés, son el clásico modo de trabajo de ASP.NET, desde sus primeras versiones. Estamos hablando de archivos .aspx, que disponen de PostBacks para interactuar con el , y de los controles Web que dispongamos en cada una de las páginas. Cada ASPX referencia a una funcionalidad o formulario de nuestro sitio Web y es una URL (dirección Web) válida dentro del sitio. Podemos vincular cada formulario Web a través de hipervínculos (links) y utilizar los más de sesenta controles Web disponibles en el framework. En este tipo de proyectos, seguiremos utilizando el arrastrar y soltar componentes en Visual Studio y configurarlos en el asistente o en la ventana de propiedades. Podemos capturar eventos, ya sea de la página (como el conocido Page_Load) o de cada uno de los controles (como el Click de un botón). ASP.NET AJAX Estos proyectos utilizan el nuevo framework ASP.NET AJAX, incorporado en ASP. NET 3.5, o como producto aparte para la versión 2.0 en www.asp.net/ajax. Sin adelantarnos demasiado al próximo capítulo, podemos mencionar que la interacción con el cambia respecto de los clásicos formularios Web y mucha funcionalidad se realizará sobre la misma página o URL, ofreciendo cambios dinámicos y parciales en la aplicación Web, según la interacción del . Esta metodología de trabajo implica utilizar los nuevos controles de AJAX en los formuarios Web, utilizar la nueva librería de cliente JavaScript para AJAX –y su integración con la conocida librería jQuery– y/o los controles ricos conocidos como AJAX Control Toolkit, que repasaremos más adelante en el libro.
Alfaomega
Visual Studio - Firtman, Natale
Proyectos ASP.NET
213
ASP.NET MVC Propuesto como un modelo alternativo (no como competencia) de los formularios Web, actualmente está disponible como una descarga adicional desde www.asp.net/mvc y será incorporado en ASP.NET 4.0 como parte de la plataforma. Este modelo no utiliza formularios Web para una aplicación. El modo de trabajo sigue el patrón de diseño Modelo Vista Controlador (en inglés, Model View Controller) y utiliza una metodología de trabajo totalmente diferente a la que conocemos, que es de utilidad para algunos tipos de proyectos Web y es muy sencillo realizar las pruebas (testing) sobre ellas. Dejaremos para más adelante la explicación detallada y los ejemplos de esta nueva modalidad. Datos Dinámicos El framework de Dynamic Data (Datos Dinámicos en español) fue incorporado a la plataforma en la versión 3.5 SP1 y está disponible como descarga adicional para las versiones anteriores en www.asp.net/dynamicdata. Un proyecto iniciado de este tipo permite crear todo un sistema ABMC (Alta, Baja, Modificación y Consulta), también conocido en inglés como CRUD (Create, Read, Update and Delete, es decir, Crear, Leer, Actualizar y Eliminar). Esto implica crear un sitio totalmente funcional que trabaje sobre un modelo de datos LINQ To SQL o Entity Framework, sin necesitar de código ni trabajo para su funcionamiento. Más adelante nos adentraremos en este excelente nuevo tipo de proyecto ASP.NET. Web Forms
AJAX Web Forms
ASP.NET MVC
Dynamic Data
Web Services
ASP Framework .NET Framework Common Language Runtime Fig. 6-2. Arquitectura de los tipos de proyecto en ASP.NET.
Visual Studio - Firtman, Natale
Alfaomega
214
6- ASP.NET
Fundamentos Podemos decir que los creadores de la plataforma han escuchado a los desarrolladores y han tenido en cuenta muchos de los comentarios y pedidos de los programadores luego de la versión ASP.NET 1.1.
Controles Web Se ha mantenido la compatibilidad de todos los controles Web de la versión 1.0, junto con sus propiedades y métodos. Sin embargo, se han agregado unos cincuenta controles nuevos que, dada la gran cantidad, han tenido que ser agrupados en categorías. Los controles nuevos que tenemos disponibles desde 2.0 son los siguientes: s
Cambios en el objeto Page El objeto Page, que referencia a toda página Web en una aplicación ASP.NET, ha sufrido muchas mejoras. Además de todas las características, atributos y métodos que tenía en la versión 1.x, ahora disponemos de MasterPages, que nos permitirá realizar herencia visual en WebForms; Themes y Skins, que nos permitirán mantener una interfaz gráfica de coherente en todo el sitio Web; y Localization, para mejorar el manejo de información localizada. Ahora es posible que la etiqueta HTML lleve runat=”server” y así podamos definir, por ejemplo, el título de una página desde código con Page.Title El ciclo de vida del objeto Page es similar al siguiente, marcando cuáles son los nuevos eventos más importantes: Evento
Descripción
PreInit (nuevo)
Se ejecuta antes de iniciar todo el proceso de ejecución de la página.
Init
Inicia el proceso.
InitComplete
Se ejecuta cuando el proceso de inicialización finaliza.
PreLoad
Se ejecuta antes de que la información de estado sea leída.
Load
Se ejecuta luego de leer la información de estado.
LoadComplete
Se ejecuta al finalizar la carga y ejecución de la página.
PreRender
Antes de comenzar a renderizar (generar el HTML) de la página. Luego de PreRender y de que todos los controles hijos hayan sido creados. Último evento antes de cerrar la ejecución.
PreRenderComplete UnLoad
Servicios Los servicios y APIs disponibles al programador también han evolucionado. Ahora disponemos de hip para la istración de s registrados en nuestro sitio Web, istración de roles para el nivel de s, SiteMaps para la istración de la estructura del sitio y 3UR¿OH para la personalización del perfil de cada .
Modelo de Trabajo Para organizar mejor el trabajo, ahora existen distintas carpetas opcionales destinadas a tareas particulares. Recordemos que si estamos trabajando en un sitio Web y no en un Proyecto de Aplicacion Web, cualquier archivo en la carpeta es parte del proyecto. Visual Studio - Firtman, Natale
Alfaomega
216
6- ASP.NET
La estructura de cualquier sitio Web ASP.NET podría ser la siguiente: Carpeta
Función
\
Carpeta raíz y ubicación de los archivos.
\bin
Contiene todos los assemblies precompilados.
\App_code
Contiene código fuente en archivos .vb o .cs que ASP.NET compila dinámicamente ante cualquier cambio.
\App_GlobalResources
Contiene archivos de recursos (.jpg, .gif, .resx, .xsd) que ASP. NET compila dinámicamente. Son globales a toda la aplicación.
\App_LocalResources
Contiene archivos de recursos (.jpg, .gif, .resx, .xsd) que ASP.NET compila dinámicamente. Pueden ser aplicados a distintas subcarpetas de la aplicación.
\App_Themes
Almacena subcarpetas por cada tema, donde estarán los skins disponibles.
\App_WebReferences
Es un contenedor para archivos relativos a Web Services, como proxies y esquemas.
\App_Data
Carpeta reservada para incluir archivos de datos, como Bases de Access. Ya posee los permisos necesarios.
\App_Browsers
Definición de navegadores Web para que ASP.NET los reconozca y detecte sus capacidades.
Fig. 6-3. En ASP.NET existe una estructura de directorios bien definida para algunas funciones propias de la plataforma. Alfaomega
Visual Studio - Firtman, Natale
Fundamentos
217
Las extensiones más comunes de archivos disponibles en cualquier aplicación ASP.NET son las siguientes: Extensión
Función
.aspx
Contiene Formularios Web.
.ascx
Contiene Controles Web Personalizados.
.ashx
HTTP Handlers.
.asix
Imagen Dinámica.
.asmx
Contiene definición de un Web Service.
.master
Contiene una Página Maestra.
.skin
Contiene información sobre un Skin para Themes.
.resx .resource FRQ¿J
Contiene archivos de recursos.
.sitemap
Contiene un mapa jerárquico XML representando la estructura del sitio.
.edmx
Modelo Entity Framework.
.xsd
DataSet.
.svc
Servicio de Datos de ADO.NET o WCF.
.dbml
Modelo de datos de LINQ To SQL.
.js
Archivo JavaScript. Incluye controles para ASP.NET AJAX,
Contiene archivos de recursos. Es un archivo de configuración XML.
Fig. 6-4. Al crear un nuevo archivo en Visual Studio, en un proyecto ASP.NET, encontramos diversas opciones. Visual Studio - Firtman, Natale
Alfaomega
218
6- ASP.NET
En ASP.NET podemos trabajar con dos modelos: Code-Inside (también llamado Code-Beside) y Code-Behind. En el primero de ellos, el código de servidor está incluido en el archivo .aspx dentro de etiquetas <script runat=”server”>. En Code-Behind, el código de servidor está en un archivo separado (generalmente, .aspx.vb o .aspx.cs) y en Visual Studio este modelo es el más cómodo y más utilizado de los dos.
Fig. 6-5. En Visual Web Developer o Visual Studio al crear un nuevo ítem Webform, estamos trabajando en modo Code-Inside, a no ser que seleccionemos la casilla “Colocar el codigo en un archivo independiente”.
Gracias a las clases parciales, una página ASPX en Code-Behind termina siendo una clase parcial definida en dos archivos .aspx y .aspx.cs, por ejemplo. La ventaja es que ya no hace falta tener información duplicada acerca de los controles Web en ambos archivos para garantizar que todo funcionará correctamente. Es así que, al usar Code-Behind, en C# los archivos quedarían separados de la siguiente forma: CodeBehind.aspx <%@ Page Language="C#" AutoEventWireup="true" CodeFile="CodeBehind.aspx.cs" Inherits="CodeBehind" %>
Alfaomega
Visual Studio - Firtman, Natale
Fundamentos
219
CodeBehind.aspx.cs public partial class CodeBehind : System.Web.UI.Page { }
De esta forma podemos ver que utiliza el concepto de clases parciales visto anteriormente, lo que define que, en realidad, el .aspx y el .aspx.cs (o .aspx.vb) son la misma clase, una expresada en lenguaje C# (o VB) y la otra en XML, que sabemos que termina definiendo objetos disponibles e instanciados en la página.
Configuración Primero analicemos :HEFRQ¿Jpara ver las configuraciones más utilizadas que actualmente soporta y, luego, veremos la nueva interfaz de configuración que nos provee ASP.NET. Sección
Descripción
DQRQ\PRXV,GHQWL¿FDWLRQ
Permite controlar cómo se istran los s anónimos en Profile.
authentication
Controla la autenticación de s en la aplicación.
authorization
Controla la autorización de a distintas URLs según los privilegios del .
browserCaps
Definición de capacidades de browsers.
caching
Control de Caché en ASP.NET.
compilation
Opciones de compilación.
customErrors
Permite personalizar los mensajes de error http.
globalization
Definición para seteos de globalización.
healthMonitoring
Monitorea el servidor por eventos excepcionales y define cómo actuar ante ellos.
hostingEnvironment
Define propiedades del entorno del servidor.
httpCookies
Cómo ASP.NET controla las cookies en funciones propias de la plataforma
httpModules
Configura módulos HTTP.
httpHandlers
Responsable de mapear URLs a clases que implementen IHttpHandler.
httpRuntime
Configura el entorno de ejecución.
identity
Define la identidad de Windows que se ha de usar.
machineKey
Guarda la clave de encriptación.
hip
Configura proveedores para el sistema de membresía.
Visual Studio - Firtman, Natale
Alfaomega
220
6- ASP.NET
mobileControls
Configura los controles móviles y adaptadores.
pages
Definiciones para las páginas ASP, como opciones por defecto en las directivas.
processModel
Configura el modelo de proceso de ASP en IIS.
SUR¿OH
Define opciones para el sistema de Perfiles de ASP.
roles
Configura el de roles.
sessionPageState
Configura cómo el estado de sesión se utiliza para el viewstate en equipos móviles.
sessionState
Define el módulo de sesión.
sitemap
Configura información sobre el sistema de navegación.
trace
Servicio de Trace.
trust
Define el nivel de confianza que se ha de utilizar al ejecutar aplicaciones ASP.NET.
urlMappings
Configura mapeos URL para el sistema de navegación.
WebParts
Define el sistema de Web Parts.
WebServices
Configura los servicios Web.
xhtml11Conformance
Controla el nivel de uso de los estándares XHTML en los controles Web.
Asistentes de configuración Además de configurar manualmente el sitio Web, editando asimismo el archivo :HEFRQ¿J, ASP.NET trae consigo otros métodos más fáciles de utilizar y definir la configuración de un sitio Web. El primero de ellos es la consola de istración ASP.NET MMC (Microsoft Management Console Snapin). Esta consola se integra a la del Internet Information Server. Para utilizarla, los pasos son los siguientes: s Abrir la consulta del Internet Information Server en de Control, Herramientas istrativas, Servicios de Internet Information Server. s
Elegir la carpeta o aplicación que se ha de configurar.
s
Seleccionar Propiedades.
s
Seleccionar la sección ASP.NET.
s
Seleccionado Editar Configuración, se nos abrirá una nueva ventana para inspeccionar y modificar distintas opciones propias del :HEFRQ¿J en una interfaz más fácil.
Alfaomega
Visual Studio - Firtman, Natale
Fundamentos
221
Fig. 6-6. Una de las opciones para configurar la aplicación Web sin tener que editar a mano el archivo :HEFRQ¿J es a través de la consola ASP.NET MMC Snapin.
Otra opción es utilizar el Web Application istration Tool, que es una interfaz Web para configurar todo lo referente a la aplicación ASP.NET. Para utilizarlo, desde Visual Web Developer Express o Visual Studio (en un proyecto Web), en el menú Sitio Web encontraremos la opción &RQ¿JXUDFLyQGH$631(7. Esta opción nos abrirá una página Web en el mismo explorador Web del entorno.
Fig. 6-7. Aquí vemos a Visual Web Developer con el Web, donde podremos configurar ASP.NET desde la misma interfaz en el proyecto Web que tenemos abierto. Visual Studio - Firtman, Natale
Alfaomega
222
6- ASP.NET
Desde aquí podremos configurar: s s s s s s
Autenticación, autorización y seguridad de las aplicaciones. Un servidor SMTP para envío de e-mails. istrar variables de servidor. istrar s y roles habilitados en el sitio Web. Definir páginas personalizadas de error. Dejar off-line una aplicación temporalmente.
Muchas de estas acciones requieren que se haya definido una base de datos SQL Server para trabajar. En estos casos, automáticamente la plataforma creará todas las estructuras que necesita para cada opción. Existe una utilidad de línea de comandos, llamada aspnet_regiis, que permite instalar ASP.NET en IIS, instalarlo en una aplicación específica o cambiar la versión de .NET utilizada. Con aspnet_regiis -? obtenemos todos los comandos.
Fig. 6-8. En la sección aplicación del sistema de configuración, podemos dejar offline la aplicación, cambiar datos de debugging y definir opciones de configuración.
Pequeños cambios, grandes soluciones Ahora veamos pequeños cambios que han incorporado al desarrollo de aplicaciones Web y que nos permitirán solucionar rápidamente muchas cosas. Alfaomega
Visual Studio - Firtman, Natale
Fundamentos
223
ControlState El famoso ViewState, el código que permite almacenar valores y el estado de los controles entre Postbacks, sigue vigente en ASP.NET; pero dado que en repetidas oportunidades genera muchos kilobytes de información, numerosos desarrolladores lo desactivan. Asimismo, existe cierta información crítica para el buen funcionamiento de la plataforma ASP.NET, que se necesita igualmente mantener. Por ello, han separado esta información crítica en otro objeto llamado ControlState, que no puede desactivarse. Foco Comenzamos con la posibilidad de definir dónde queremos que el foco del cursor se pose al comenzar la aplicación o luego de un Postback. De esta forma, cuando un ingresa mal algún dato, podemos automáticamente ubicar el cursor en el lugar necesario para que haga el arreglo. Para ello, todos los controles tienen el método Focus. Siguiendo con el tema del foco de los controles, ahora la etiqueta
Alfaomega
Visual Studio - Firtman, Natale
Fundamentos
225
Pagina2.aspx <%@ Page Language="VB" %> <%@ PreviousPageType VirtualPath="~/Pagina1.aspx" %> <script runat="server"> Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) 9HUL¿FDPRVTXHH[LVWDODSiJLQDSUHYLD If PreviousPage IsNot Nothing Then 9HUL¿FDPRVTXHKD\DVLGRXQSRVWEDFNFUX]DGR 'Si es falso, puede ser un Server.Transfer If PreviousPage.IsCrossPagePostBack Then Response.Write("PostBack") 3RGHPRVXVDUDWULEXWRV\PpWRGRVGHODSiJLQD 'anterior VyORVLORVKHPRVGHFODUDGRFRPRS~EOLFRV Else Response.Write("Viene de Server.Transfer") End If Else Response.Write("Ingreso Directo") End If End Sub
Aquí vemos que el objeto Page tiene una nueva propiedad (de clase Page también) y es PreviousPage, que tiene una instancia sólo si ha existido un postback cruzado o si el ha llegado a la página por una sentencia Server.Transfer. Para saber si se trata de un postback, existe la propiedad IsCrossPagePostback. Veamos como importante que la segunda página tiene una nueva directiva de página, PreviousPageType. Si no se la coloca, el postback cruzado funcionará igual, pero perderemos todo soporte de Intellisense y debugging en Visual Studio, dado que no sabrá a qué página refiere el objeto PreviousPage. Si tenemos varias páginas que hacen un postback cruzado, podemos saber cuál es consultando las propiedades de PreviousPage, como AppRelativeVirtualPath. Si quisiéramos entonces acceder a datos o controles de la primera página, deberíamos haber colocado en Pagina1.aspx el siguiente código Visual Basic (igual sería en C#): <script runat="server"> Public ReadOnly Property txtNombre() As TextBox Get 'Devuelve la instancia del control Visual Studio - Firtman, Natale
Alfaomega
226
6- ASP.NET
Return txtNombre End Get End Property
Y en la página destino, entonces, podríamos acceder al texto que ingresó el en la inicial, utilizando: PreviousPage.txtNombre.Text
Directorio raíz de la aplicación En cualquier propiedad de cualquier etiqueta donde necesitemos referenciar a un archivo de nuestra aplicación, podemos utilizar el símbolo ~ para definir el directorio raíz, como ser PostBackUrl=”~/Pagina2.aspx”. Validación en Grupos En ASP.NET 1.x podíamos tener distintos controles de ingreso y distintos controles de validación, que verificaran que los datos de ingreso concuerden con ciertas reglas. Ahora bien, supongamos que en un mismo formulario Web, tenemos un cuadro de búsqueda y, al mismo tiempo, un formulario de ingreso de datos. Si todo tiene validación, al querer hacer una búsqueda nos va a pedir que también los controles del formulario de ingreso de datos pasen la validación. Por eso, desde ASP.NET 2.0 ahora tenemos grupos de validación. De esta forma, cada control de tipo botón ahora tiene la propiedad ValidationGroup, que permite definir un nombre de grupo. Esta propiedad también debe ser definida, al mismo tiempo, en el control de validación; de esta forma, cada control de validación actuará solamente si el grupo al que pertenece es el mismo que el grupo al que pertenece el botón que el ha ingresado. Veamos un ejemplo:
Todos los controles de lista ahora tienen algunas propiedades nuevas y muy útiles a la hora de desarrollar aplicaciones. La primera de ellas es AppendDataBoundItems, que puede tener los valores True o False. Por defecto, esta propiedad está apagada y, en caso de que tengamos ítems definidos por código aspx y un enlace a datos, sólo quedarán visibles los que se enlacen con el origen de datos. Si encendemos esta propiedad, entonces los ítems del origen de datos se anexarán al final de los ítems definidos en forma estática.
Visual Studio - Firtman, Natale
Alfaomega
254
6- ASP.NET
Fig. 6-21. Aquí vemos en acción el nuevo control BulletedList. Podemos usarlo para mostrar información estática o para generar vínculos automáticamente.
Esta opción es muy útil para, por ejemplo, crear un DropDownList de opciones obtenidas de alguna tabla de la base de datos y poner una opción inicial que diga Seleccione de la lista. De esta forma, esta última opción se agrega como un ítem estático declarado por etiqueta y el resto se incorpora desde un origen de datos. Otra propiedad importante incorporada a todos los controles de lista es DataTextFormatString, que permite definir un patrón para mostrar los textos de los ítems. Por ejemplo, si queremos que todos los ítems tengan un guión delante y no tener que agregarlo en la base de datos, podemos definir esta propiedad con “- {0}”.
GridView La evolución del DataGrid Todos hemos utilizado la grilla en ASP.NET 1.x y lo hemos visto como el “gran control” para mostrar datos de una consulta. Todo era muy bonito hasta que Alfaomega
Visual Studio - Firtman, Natale
Trabajando con Datos
255
empezábamos a pedir algunas funciones extras, que son necesarias en la funcionalidad de una grilla de datos, como paginación u ordenamiento de columnas. Si bien el control las soportaba, teníamos que escribir siempre el mismo código de servidor para efectuar realmente estas operaciones. Por ello, empezaron a salir clones del DataGrid como controles personalizados gratuitos y pagos, que tenían mucha mayor funcionalidad sin escribir código. Además, si intentábamos habilitar el modo edición, realmente era muy complejo tomar los datos para escribir la consulta de actualización. Por todo esto, apareció un nuevo control en ASP.NET 2.0, que está vigente todavía: GridView. Este control ofrece, en principio, la misma funcionalidad que el DataGrid y mucho más. No sólo tenemos ahora ordenamiento por columnas y paginación automática, sin escribir ni una sola línea de código, sino que también tendremos opción a editar registros y toda la actualización se realizará automáticamente en el origen de datos, sea una base de datos SQL, un origen XML o un objeto de negocios. El control Básicamente, el control muestra en pantalla una grilla de datos, donde cada fila es un registro del origen de datos y cada columna, un miembro, campo o atributo. Veamos las propiedades más útiles de este control: Propiedad
Descripción
AllowPaging
Permite activar (True) el sistema de paginación de datos automático.
AllowSorting
Permite activar (True) el sistema de ordenamiento de columnas automático.
AutoGenerateColumns
Define si las columnas son creadas automáticamente con todos los campos del origen de datos (True) o en forma manual (False).
AutoGenerateDeleteButton
Define si automáticamente se genera una columna con un link que elimina la fila.
AutoGenerateEditButton
Define si automáticamente se genera una columna con un link que le da la posibilidad de editar la fila al .
AutoGenerateSelectButton
Define si automáticamente se genera una columna con un link que le permite seleccionar una fila al para alguna acción.
EmptyDataText
Una función muy solicitada, permite definir un texto que se mostrará cuando no haya registros disponibles en el origen de datos.
Visual Studio - Firtman, Natale
Alfaomega
256
6- ASP.NET
EnablePagingAndSortingCallbacks
Si se activa esta opción (True) la paginación y el reordenamiento se realizará utilizando Callbacks (tecnología conocida como AJAX).
SortDirection
Es una propiedad de sólo lectura que permite saber si el ordenamiento actual es ascendente o descendente.
SortExpression
Permite saber la expresión de ordenamiento actual.
SummaryViewColumn
Define el nombre de la columna que será utilizada como link para ver el resto de las columnas cuando el control se ve en un dispositivo móvil.
Paginación Si activamos la propiedad EnablePaging, automáticamente tendremos funcionando el sistema de paginación (si el origen de datos lo permite), repartiendo los resultados en n cantidad de páginas y mostrando la cantidad de registros por página definida en PageSize. Las opciones para definir el tipo de paginación que vamos a querer son varias y las podemos resumir en la siguiente tabla: Propiedad
Descripción
Mode
Permite definir Numeric, que muestra los números de páginas, NextPrevious (con link a página anterior y siguiente), NextPreviousFirstLast y NumericFirstLast, que incorporan links a la primera y última página.
PageButtonCount
Cantidad máxima de páginas que muestra en estado Numeric.
Position
Permite tener la fila de paginación abajo (Bottom), arriba (Top) o en ambas posiciones (TopAndBottom).
FirstPageText
Define el texto que se ha de usar en el link para la Primera Página.
FirstPageImageUrl
Define una imagen que se usará como link para la Primera Página.
LastPageText
Define el texto que se ha de usar en el link para la Última Página.
LastPageImageUrl
Define una imagen que se usará como link para la Última Página.
NextPageText
Define el texto que se ha de usar en el link para la Próxima Página.
NextPageImageUrl
Define una imagen que se usará como link para la Próxima Página.
PreviousPageText
Define el texto que se ha de usar en el link para la Página Anterior.
PreviousPageImageUrl Define una imagen que se usará como link para la Página Anterior.
Alfaomega
Visual Studio - Firtman, Natale
Trabajando con Datos
257
Fig. 6-22. Activar la paginación permite tener en pocos segundos, y sin tener que escribir ningún código VB ni C#, nuestro GridView paginado, utilizando LINQ u otro origen.
Ordenamiento Activar el ordenamiento de columnas es tan simple como poner en True la propiedad EnableSorting. Sin escribir ningún código adicional, ahora en los títulos de las columnas tenemos un link para solicitar ordenar por esa columna. Automáticamente, si el hace clic sobre la columna por la cual actualmente están ordenados los datos, el orden se invertirá. Edición y Eliminación Al activar las opciones de edición y eliminación de datos, o al insertar campos de tipo botón manualmente, podremos darle al la opción de editar los datos (todo el proceso se realiza en forma automática) o de eliminar registros. Para ello, necesitamos tener definidos los UpdateCommand (o UpdateMethod en ObjectDataSource) y DeleteCommand con los parámetros incluidos. Los parámetros deben tener exactamente el mismo nombre que los campos para actualizar, para que todo funcione correcta y automáticamente. Visual Studio - Firtman, Natale
Alfaomega
258
6- ASP.NET
Fig. 6-23. Al habilitar el modo edición, automáticamente ASP.NET reemplazará los textos del registro actual por TextBox o Checkboxs, según el tipo de datos para actualizar.
Opciones Avanzadas El mundo del GridView merece mucho más espacio que el que podemos dedicarle en este libro, pero mencionemos que existen dos plantillas (templates) disponibles para definir: EmptyDataTemplate, para cuando no hay datos en el origen y PagerTemplate, para definir una fila de paginación manualmente. Existen decenas de propiedades y subetiquetas que podemos definir para darle diseño visual a nuestra GridView según nuestra conveniencia. Si queremos definir las columnas manualmente, seguro que la mejor opción será utilizar el editor de columnas de Visual Studio, donde podremos definir columnas de los siguientes tipos: BoundField para campos de base de datos, ButtonField para crear un comando, CheckBoxField para campos de tipo lógico o booleano, CommandField para comandos específicos –como Edit, Delete y Select–, HyperLinkField que permite definir vínculos hacia otras páginas, enviando como parámetros uno o varios campos de la fila, ImageField que permite mostrar una imagen, ya sea tomando el archivo o la Alfaomega
Visual Studio - Firtman, Natale
Trabajando con Datos
259
URL del mismo desde un campo de datos y TemplateField para definir un contenido personalizado con código HTML y código de enlace. Con el nuevo Framework 3.5 SP1 y los controles dinámicos de datos, también podremos utilizar los nuevos controles DynamicControl y DynamicValidator, que veremos más adelante. Para definir las columnas debemos desactivar la opción AutoGenerateColumns y definir las columnas dentro de la colección columns, como en el siguiente ejemplo:
DetailsView ¿Qué es? Algo que seguramente nos estaremos preguntando es: ¿Por qué GridView todavía no tiene la opción de insertar un nuevo registro? Para resolver este tema, y también para trabajar con un registro a la vez en pantalla, existe DetailsView o Vista Detalle. La vista detalle es una tabla de dos columnas, donde en la primera tenemos los nombres de los campos y en la segunda, los valores. En el caso de que habilitemos el modo edición, la segunda columna se convertirá en controles de ingreso, que serán utilizados para guardar el registro. Para todos los procesos de actualización en GridView o DetailsView es primordial definir la propiedad DataKeyNames, que incluye todos los campos o atributos (separados por coma) que son parte de la clave primaria.
Visual Studio - Firtman, Natale
Alfaomega
260
6- ASP.NET
Fig. 6-24. DetailsView nos permitirá trabajar con un solo registro a la vez, teniendo todas las opciones disponibles en un GridView.
Sintaxis Su sintaxis es similar a un GridView, por ejemplo:
Alfaomega
Visual Studio - Firtman, Natale
Trabajando con Datos
261
Notemos que en lugar de
ahora tenemos
y el resto es muy parecido. Si queremos definir el nombre que aparece en la primera columna de cada campo, debemos definir el HeaderText de cada uno de ellos. A los ya conocidos y mencionados AutoGenerateEditButton y AutoGenerateDeleteButton, ahora sí se nos incorpora un AutoGenerateInsertButton, que permite ingresar un nuevo registro a la base de datos. Y el AutoGenerateRows le dice al control que automáticamente utilice todos los campos de la base de datos, en caso de definirlo en True. La propiedad DefaultMode indica en qué modo se iniciará el control apenas se inicia la primera vez, si en modo normal de lectura (ReadOnly), en modo edición (Edit) o en modo inserción de nuevo registro (Insert). Combinando GridView, un DataSource parametrizado y un DetailsView, podemos generar un sistema de maestro detalle, donde al seleccionar un registro de la grilla podamos verlo completo y editarlo en detalle. Paginación Analizando el control Web, también tenemos la opción AllowPaging y nos preguntamos: ¿Paginación en un DetailsView? Sí. Y su función es poder navegar entre los registros, siempre de a uno por vez. Así, podemos ir pasando de un registro a otro y, si activamos EnablePagingCallback, el cambio de un registro a otro se realizará sin refrescar la página. Aunque luego veremos que el framework ASP.NET AJAX nos ayudará en esta tarea.
FormView La vista de formulario o FormView es similar a DetailsView, con la diferencia de que todo lo que se muestra lo debemos definir nosotros en plantilla y no muestra nada por su cuenta. Así, FormView posee las siguientes plantillas que debemos editar: ItemTemplate, EditItemTemplate, InsertItemTemplate. Por defecto, si no editamos las plantillas el control está vacío y no dibuja nada. Al insertar un control de este tipo en Visual Studio, debemos recordar que haciendo clic derecho en Editar Plantillas, podremos elegir qué plantilla queremos editar en cada momento. Además, al seleccionar un origen de datos para utilizar, Visual Studio y VWD automáticamente generan código en todas las plantillas para simplificarnos la tarea. Recordemos que en este control debemos crear todo el formulario nosotros mismos y nos permitirá crear un formulario de ingreso y edición de datos más complejo, con DropDownLists, y más columnas, aprovechando mejor la página que si usáramos DetailsView (que siempre lo hace en dos columnas). Visual Studio - Firtman, Natale
Alfaomega
262
6- ASP.NET
Fig. 6-25. Con FormView podemos crear formularios de trabajo, con datos más complejos y personalizados al diseño que queremos darle al ingreso.
Un FormView queda definido por la siguiente sintaxis
<EditItemTemplate>
ListView El control Vista de Lista (List View), fue incorporado en ASP.NET 3.5 y permite, además de todas las funciones automáticas de alto nivel del GridView –como edición, eliminación, ordenación, paginación e inserción–, un completo control sobre el código HTML y CSS, que se genera al navegador, eliminando mucho código no estándar, o considerado intrusivo por mucha gente, que el GridView y el DetailsView generan. Los pasos para trabajar con este control son, primero diseñar el HTML que queremos utilizar y luego utilizarlo en las más de diez plantillas que posee el control.
Alfaomega
Visual Studio - Firtman, Natale
Trabajando con Datos
263
Fig. 6-26. Con ListView se pueden generar vistas mucho más atractivas y menos matriciales que con GridView.
Plantillas Básicas Las dos plantillas básicas de un ListView son LayoutTemplate (plantilla de disposición) e ItemTemplate (plantilla de elemento). La primera define el diseño del contenedor completo de la lista, colocando un control, generalmente un PlaceHolder, donde queremos colocar los elementos. El PlaceHolder se utiliza en lugar de otros controles para no generar código HTML de más. Y la plantilla de elemento define cómo se dibujará cada uno de los elementos dentro del PlaceHolder. También la plantilla EmptyDataTemplate define el contenido para cuando no existen datos en el origen. El control que usemos en LayoutTemplate debe tener obligatoriamente el ID= ”itemPlaceholder”, salvo que definamos la propiedad del ListView ItemPlaceHolderID, definiendo de esta manera el identificador del control. Veamos un ejemplo:
Visual Studio - Firtman, Natale
Alfaomega
264
6- ASP.NET
Clientes k1o2q
<%# Eval("NombreCliente") %>
<EmptyDataTemplate>
No hay clientes
Paginando resultados Para mostrar solo n cantidad de elementos en un ListView y lograr una navegación en páginas, existe el también nuevo control DataPager, que debemos insertarlo en cualquier lugar de la página ASP.NET donde queramos que la paginación aparezca y definir la propiedad PagedControlID como el id del ListView y PageSize con el tamaño de elementos por página. Para que el paginador agregue controles de navegación, es necesario agregar campos. Los campos incluidos con el framework son los siguientes: NextPreviousPagerField, que permite navegar una página a la vez con siguiente y anterior, NumeriagerField, que permite al seleccionar una página a la vez y TemplatePagerField, que nos deja la posibilidad de definir el diseño del navegador de páginas. Cada uno de estos controles de navegación tiene propiedades para definir cómo se visualizan y son simples de comprender analizando la Ventana de Propiedades. Entonces, por ejemplo, podríamos paginar el anterior ListView con el siguiente código:
Alfaomega
Visual Studio - Firtman, Natale
Trabajando con Datos
265
Plantillas Avanzadas El ListView también tiene otras plantillas, a saber: ItemSeparatorTemplate, que define el código que se ha de utilizar entre cada elemento; SelectedItemTemplate, que define el diseño que se ha de utilizar cuando el elemento está seleccionado (a través de la propiedad SelectedIndex); AlternatingItemTemplate,que define el diseño para las filas pares (siendo ItemTemplate el de las impares); EditItemTemplate, para un elemento en edición e InsertItemTemplate, para un elemento que se estaría insertando en el origen de datos. Trabajando con grupos Otra gran y, a veces, un poco confusa funcionalidad del ListView es la posibilidad de agrupar los elementos. Los grupos permiten agrupar los elementos cada n cantidad y definir algún separador de grupo más una cabecera particular. No se han visto grandes usos de esta opción, pero un buen ejemplo es en un foro de discusión, donde podemos colocar una publicidad cada cinco mensajes o noticias, o hacer un diseño en tablas en tres columnas. Para ello, el ListView posee el atributo GroupItemCount, que define cada cuántos elementos se trabajará con un nuevo grupo y las plantillas GroupTemplate, para el diseño de cada grupo, GroupSeparatorTemplate, para el separador entre grupos y EmptyItemTemplate, que define el diseño del grupo para cuando el mismo no tiene elementos. Cuando trabajamos con grupos, el LayoutTemplate no tendrá enlazado un ItemTemplate a través de id=”itemPlaceholder”, sino que el Layout estará enlazado al GroupTemplate a través del id=”groupPlaceholder” y la plantilla de grupo enlazada a la de elemento. Veamos entonces un ejemplo, mostrando los datos en una tabla de tres columnas:
Visual Studio - Firtman, Natale
Alfaomega
266
6- ASP.NET
<%# Eval("Nombre") %> 5tr6p
Controles de Visualización ImageMap Este control permite incluir una imagen (por ello posee todas las mismas propiedades que el control Image), con la diferencia de que podemos definir zonas sobre la imagen que actuarán en forma distinta según se cliqueen. Estas zonas podrán generar un postback o dirigir al a una URL específica por cada una. Por ejemplo, podemos tener un mapa geográfico y permitirle al hacer clic en una zona y nosotros saber en qué zona han cliqueado. Este control posee la misma estructura que Image con las propiedades HotSpotMode y una colección de HotSpots. El modo (HotSpotMode) puede ser PostBack, Navigate o Inactive. El primer modo permite que cada zona al ser cliqueada genere un postback al servidor, el segundo que sea dirigido a una URL y el último no reacciona ante los clics del (puede ser usado para ejecutar código de cliente). Existen tres tipos de HotSpot (zonas): Círculos (CircleHotSpot), rectángulos (RectangleHotSpot) y polígonos (PolygonHotSpot). Cada HotSpot tiene a su vez propiedades generales, como su propio HotSpotMode (es posible definir algunas zonas PostBack y otras Navigate), AlternateText, para el texto alternativo que se ha de mostrar, NavigateUrl y Target, con la URL a donde dirigir al (sólo modo Navigate) y PostBackValue con un valor que luego consultaremos para saber qué zona se ha cliqueado (sólo modo PostBack). Luego, las zonas circulares tienen las propiedades X, Y y Radius (radio del círculo) para la ubicación y tamaño de la zona, las zonas rectangulares tienen las propiedades Bottom, Top, Left y Right y los polígonos una colección de coordenadas de vértices en Coordinates. Veamos un ejemplo:
Alfaomega
Visual Studio - Firtman, Natale
Trabajando con Datos
267
HiddenField Este control permite guardar datos en la página Web que no serán visibles al . Se puede utilizar para guardar datos cuando el ViewState está apagado, por ejemplo. Hay que tener cuidado con guardar aquí datos privados, dado que no tiene seguridad ni encriptación y la información puede ser vista por el , revisando el código fuente HTML. El control tiene una sola propiedad útil, Value, que permite acceder desde código de servidor a lectura y escritura del valor que guarda. Lo que guarda siempre es un objeto String. La sintaxis es muy simple y solamente es:
Literal Este control es muy simple y viene heredado de ASP.NET 1.x Mobile Controls, ya obsoleto. Lo único que hace el control es enviar un texto dado en la propiedad Text directamente al navegador, pudiendo incluir código HTML, Javascript o cualquier cosa que queramos enviar. No tiene opciones de formato y no agrega ninguna etiqueta antes de enviarlo –a diferencia de Label, que incorpora una etiqueta span y opciones de formato–. La únicas propiedades disponibles son Visible (ya conocida) y Mode, que permite elegir entre tres modos: Transform, Through y Encode. La primera opción y, por defecto, envía el texto como está, si ingresa un navegador XHTML y lo transforma a código compatible cuando entra un navegador de un teléfono celular u otro dispositivo que no soporte alguna etiqueta. La segunda opción envía el texto y códigos como se definan y la última hace un encoding para que pueda ser visto correctamente por cualquier navegador. Por ejemplo, si usamos la opción Transform, el siguiente texto <mietiqueta>hola será transformado a “hola” cuando ingresa un navegador WML o que no soporta etiquetas del estilo. La propiedad Visual Studio - Firtman, Natale
Alfaomega
268
6- ASP.NET
Text puede ser definida declarativamente, por código de servidor, o implícitamente entre la etiqueta de apertura y cierre. Un ejemplo, podría ser:
Este es el
texto a enviar
File Este control representa el proceso de publicación de archivos por parte del hacia el servidor. En ASP.NET 1.x estaba cubierto por un control HTML, que convertíamos a control de servidor, pero no era exactamente un control Web. El control File no tiene ninguna propiedad particular y con sólo arrastrar el componente, o definir su ID, ya lo tenemos funcionando:
Lo importante en este caso es poder guardar el archivo en el disco una vez que el hace un (publicación) del archivo al servidor. Para ello, luego de algún evento de postback donde estemos recibiendo el archivo (por ejemplo, el clic de un botón), debemos acceder al control mediante su nombre y, con el método SaveAs(rutacompleta), podremos guardarlo en algún lugar del servidor. Otras propiedades útiles son FileName, que define el nombre que tenía el archivo en el equipo del , y PostedFile, que nos da a un objeto con información del archivo, como ser tipo de archivo (PostedFile.ContentType) o tamaño (PostedFile.ContentLength). Wizard Un Wizard, o Asistente, nos representa un clásico asistente de los entornos de escritorio, que le permite al realizar una acción dividida en n pasos y puede ir pasando entre todos ellos hasta finalizar y confirmar la operación. Podemos pensar en un asistente como una zona en una página Web, que posee distintas vistas según en qué paso del asistente el se encuentre. Apenas ingrese el se encontrará en el Paso 1 y podrá, así, continuar al Paso 2 y luego al 3 hasta llegar al último paso donde podrá Finalizar la 2SHUDFLyQ. Este control es muy útil para crear formularios de ingreso de datos que, por organización conceptual y por cantidad de controles, es preferible agruparlos en distintas pantallas.
Alfaomega
Visual Studio - Firtman, Natale
Trabajando con Datos
269
Fig. 6-27. Con un Wizard una operación larga se torna más fácil de seguir por el , al dividirla en n cantidad de pasos.
Cuando el cambia de página (hacia adelante o hacia atrás), se genera un postback en el servidor y podemos capturar el evento ActiveStepChanged cuando se cambia un paso (step). Antes de ver en detalle las propiedades y eventos del control, analicemos su estructura. Existe un paso inicial, que contiene los controles propios de ese paso del asistente, y un botón de Siguiente (Next). Luego, existen pasos intermedios (la cantidad que querramos ubicar), que poseen navegación en ambas direcciones ($WUiV y Adelante). Por último, existe un paso final, que posee la opción de volver al paso anterior (Previous) o finalizar la operación. Un Wizard visualmente se divide en tres porciones: La SideBar (barra lateral), el contenido y la barra de botones. En la primera se ubican todos los pasos disponibles en el asistente para guía y seguimiento del . En el contenido, podemos ubicar todo el contenido HTML, controles Web o código que necesitemos en cada paso y, por último, en la barra de botones se ubican los botones $WUiV, Siguiente, Finalizar o Cancelar, según el paso en el que nos encontremos.
Visual Studio - Firtman, Natale
Alfaomega
270
6- ASP.NET
El botón de Cancelar es opcional y podemos activarlo con la propiedad DisplayCancelButton que, por defecto, está desactivada.
Fig. 6-28. Así se ve un Wizard de sólo 2 pasos en Visual Studio. Notemos la ubicación de la SideBar y de la barra de botones.
Veamos los eventos de servidor que podremos capturar en un Wizard: Evento
Descripción
ActiveStepChanged
Cuando se cambia de paso, no importa el método (secuencial o aleatorio).
CancelButtonClick
Cuando se presiona el botón de Cancelar en cualquier paso.
FinishButtonClick
El evento seguramente más utilizado, capturamos el momento en que el dio el consentimiento de la finalización de la operación.
NextButtonClick
Cuando el se mueve a un siguiente paso.
PreviousButtonClick
Cuando el se mueve a un paso anterior.
SideBarButtonClick
Cuando el cambia de paso seleccionándolo de la SideBar.
El control Wizard tiene muchas propiedades y estilos que podemos definir para cada zona y cada botón. Veamos las propiedades más utiles: Atributo HeaderText
Descripción Define el texto que se ha de utilizar como cabecera del Wizard.
DisplayCancelButton
Es el índice del paso que está actualmente mostrándose. Si no queremos dirigir al a una URL cuando cancela. Define si aparece o no el botón de Cancelar.
DisplaySideBar
Define si aparece o no la barra lateral.
FinishDestinationPageUrl
Si queremos dirigir al a una URL cuando finaliza el proceso.
ActiveStepIndex CancelDestinationPageUrl
Además, existen algunas propiedades relativas a todos los tipos de botones que serán ubicados en el asistente. Así, existen las propiedades (boton) ButtonImageUrl, que indica el nombre de la imagen que queremos que tenga Alfaomega
Visual Studio - Firtman, Natale
Trabajando con Datos
271
el botón en lugar de un botón textual, (boton)ButtonText, que es el texto que queremos que tenga el botón y (boton)ButtonType, que permite elegir el tipo de botón para utilizar entre Button, Image o Link. En las propiedades antes citadas, (boton) puede ser reemplazado por alguno de los siguientes: Botón
Descripción
Cancel
Es el botón de Cancelar Proceso.
FinishComplete
Es el botón de Finalizar Proceso.
FinishPrevious
Es el botón de Anterior que se ubica en el último paso.
StartNext
Es el botón de Siguiente que se ubica en el primer paso.
StepNext
Es el botón de Siguiente que se ubica en los pasos intermedios.
StepPrevious
Es el botón Anterior que se ubica en los pasos intermedios.
Cuando utilizamos VS o VWD para crear el asistente, automáticamente tendremos, como Smart Tasks, la posibilidad de elegir qué paso queremos editar, agregar un nuevo paso al asistente o convertir un paso a una plantilla (donde nosotros tendremos que crear todos los controles).
Fig. 6-29. En Visual Web Developer tenemos Smart Tasks para trabajar fácilmente con Wizards en nuestra página Web.
Un asistente, a su vez, tiene una colección de WizardStep, que corresponden a cada paso del asistente y que pueden ser declarados en el archivo aspx en forma de etiquetas con el contenido de cada paso dentro. Existen dos tipos de Steps: WizardStep, como estuvimos viendo, y TemplatedWizardStep, que es un paso basado en plantillas, para tareas avanzadas. Cada paso del asistente tiene algunas propiedades de utilidad, como Title, que define el título del paso (que se utiliza en la SideBar), AllowReturn, que permite definir si permitimos al volver al paso una vez que lo hizo y StepType, que define el tipo de paso que es (Auto, Complete, Finish, Start o Step). Visual Studio - Firtman, Natale
Alfaomega
272
6- ASP.NET
Veamos entonces un wizard con tres pasos, dos de ingreso de datos y un último de confirmación de la información ingresada: <%@ Page Language="C#" %> <script runat="server"> protected void Wizard1_ActiveStepChanged(object sender, EventArgs e) { if (Wizard1.ActiveStepIndex == 2) { (VWR\HQHO~OWLPRSDVRDFWXDOL]RODEHOV lblNombre.Text = txtNombre.Text; lblDireccion.Text = txtDireccion.Text; } }
Fig. 6-30. Aquí vemos nuestro Wizard en acción, utilizando un autoformato de Visual Studio para darle un mejor aspecto. Vemos claramente las zonas diferenciadas.
MultiView Un control MultiView podría verse como una simplificación del Wizard (o un Wizard una especialización del MultiView). Una vista múltiple es un control que permite tener distintas vistas (View) posibles, pero sólo una de ellas puede Visual Studio - Firtman, Natale
Alfaomega
274
6- ASP.NET
estar viéndose en un momento determinado. Desde el código de servidor, podemos cambiar la vista según algún evento que haya ocurrido. Así, por ejemplo, podemos querer mostrar información sobre un producto y tenemos distintas vistas que el puede seleccionar, los datos comerciales, la ficha técnica, las fotos o el precio. Cada una de estas opciones podría ser una vista distinta. La propiedad ActiveViewIndex permite cambiar la vista actual. Veamos entonces cómo quedaría la sintaxis:
Este es el contenido de la Vista1
Este es el contenido de la Vista2
Multiview1 View2 View1
Fig. 6-31. Así se ve un MultiView en Visual Studio o VWD. Recordemos que debemos insertar un MultiView y luego un View por cada vista distinta que querramos tener.
Substitution Este control no genera ningún código visual al y sirve para sustituir información en páginas que están utilizando caché. Sabemos que si utilizamos caché de páginas, por defecto, toda la página es cacheada, pero ¿qué sucede si tenemos alguna pequeña porción de información que sí debe ser actualizada por cada solicitud, sin perder el caché de todo el resto? Allí aparece el control Substitution, que automáticamente llama ante cada petición a algún método que nosotros definamos en la propiedad MethodName y utiliza la información que dicho método devuelve para incluirla en la página Web como información dinámica, que sí se actualiza a pesar del caché. El método debe ser estático (static en C#, Shared en VB), recibir un objeto HttpContext como parámetro y devolver un string con el texto que se ha de ubicar donde ahora está el control Substitution. Veamos un ejemplo donde incluimos la hora por medio de la sustitución y donde la incluimos por medio del código de renderización, directamente en la página, y veremos que una se actualiza y la otra, por dos minutos (120 segundos), quedará siempre igual: Alfaomega
Visual Studio - Firtman, Natale
Controles de Navegación
275
<%@ Page Language="VB" %> <%@ OutputCache Duration="120" VaryByParam="None" %> <script runat="server"> Public Shared Function DarFecha(ByVal context As_ HttpContext) As String Return DateTime.Now.ToString() End Function
Localize Este es un nuevo control que extiende a Literal y nos permite definir un texto que será localizado según el idioma o ubicación geográfica del , simplificando esta tarea. Para ello, debemos utilizar las herramientas de recursos de localización que ASP.NET nos provee.
Controles de Navegación Ya habíamos visto el control SiteMapDataSource y el que nos daba un origen de datos al archivo Web.sitemap. Ahora veremos en qué controles podremos utilizar este origen de datos y también el origen de XML
TreeView El control vista de árbol, o TreeView, es uno de los controles ricos más útiles de los incorporados en ASP.NET. Nos permite visualizar una estructura jerárquica en un formato de árbol, pudiendo configurar fácilmente la apariencia visual e incluyendo varios formatos visuales muy útiles, como viñetas, os de MSN, flechas, tablas de contenido, árboles de carpetas, etc.
Visual Studio - Firtman, Natale
Alfaomega
276
6- ASP.NET
El origen de datos debe ser jerárquico, como en el caso de XmlDataSource o SiteMapDataSource. El podrá abrir o cerrar la rama de cada árbol haciendo clic en la imagen que acompaña a cada nodo y esta opción puede ser desactivada mediante ShowExpandCollapse. Con ShowLines podemos habilitar o deshabilitar que se vean líneas conectoras entre todos los nodos. Automáticamente, el control genera código de cliente para poder dar la funcionalidad de abrir o cerrar nodos, pero si el cliente no soporta esta opción se realiza mediante postbacks. La propiedad ExpandDepth nos permite definir con qué profundidad vamos a mostrar el árbol la primera vez, pudiendo dar un número o definiendo FullyExpand para mostrar todo el árbol abierto.
Fig. 6-32. Aquí vemos un TreeView obteniendo datos de un archivo XML y utilizando un formato gráfico conocido para todos los s de Windows.
Alfaomega
Visual Studio - Firtman, Natale
Controles de Navegación
277
Fig. 6-33. Como vemos, el puede expandir o contraer cada rama de la estructura sin mayor problema.
Una opción muy interesante es la posibilidad de activar casillas de verificación CheckBoxs para los nodos. De esta forma, el podrá seleccionar uno o más nodos y así, luego desde el código de servidor, podremos recorrer la estructura para saber qué seleccionó. Esta opción se puede activar de distintas formas en ShowCheckBoxes.
Fig. 6-34. Podemos activar la opción de que el pueda seleccionar nodos, pudiendo elegir todos los nodos, las ramas o la raíz. Visual Studio - Firtman, Natale
Alfaomega
278
6- ASP.NET
También podremos definir los nodos en forma estática (que no obtengan los datos de un origen de datos), a través del editor de nodos que ofrece Visual Studio. Un código de ejemplo, sería el siguiente:
Fig. 6-35. Editando nodos en forma estática podremos crear una estructura y definir opciones particulares para cada nodo. Alfaomega
Visual Studio - Firtman, Natale
Controles de Navegación
279
SiteMapPath Este es un control muy fácil de utilizar y extremadamente útil para cualquier sitio Web. El mismo expone el camino absoluto que ha seguido el hasta llegar a la página Web, comenzando desde la Home Page. Este comportamiento es también conocido como “Migajas de Pan”, en alusión al cuento de Hansell y Gretel. Si estamos en la página principal, por ejemplo veremos el siguiente texto: Home Page
Ahora, si entramos a la sección Productos y de ahí a Monitores, tendríamos: Home Page > Productos > Monitores
Y mejor todavía, Home Page y Productos tendrán un link para volver a cada una de esas páginas. Sin escribir nada de código. Lo único que necesitamos es tener el archivo Web.sitemap con la estructura del sitio y, automáticamente, ASP.NET hará todo el trabajo de este control. Podremos definir estilos distintos para el nodo principal (Root Node), para los nodos intermedios (Parent Node) y para el nodo actual (Current Node). Algunas propiedades interesantes para mencionar son: PathDirection, con la opción de seleccionar que se muestre desde la home hacia la página actual (RootToCurrent) o al revés (CurrentToRoot); PathSeparator, para definir el carácter que separará cada nodo (por defecto >); RenderCurrentNodeAsLink, que permite definir si la página actual donde está el tendrá link o no; ParentLevelsDisplayed, con el número máximo de nodos para mostrar (o -1 para todos) y SiteMapProvider, en el caso de que usemos un proveedor desarrollado por nosotros para crear la estructura.
MenU Este control genera menús de navegación desplegables para instalar en cualquier página Web. Las opciones del menú las podemos obtener automáticamente del archivo Web.sitemap, de un archivo XML o podemos definirlas, en forma estática, con el editor de nodos o vía código de servidor. Automáticamente ASP.NET generará todo el código JavaScript para que el control funcione correctamente en todos los navegadores. Este control tiene decenas de propiedades dignas de ser estudiadas por separado para lograr los efectos visuales que queremos en nuestro menú de navegación, pero seguramente nuestra opción principal será Orientation, pudiendo elegir un menú Horizontal o un menú Vertical. Visual Studio - Firtman, Natale
Alfaomega
280
6- ASP.NET
Fig. 6-36. Aquí vemos nuestro control en acción dentro de la estructura de un sitio Web. Fácilmente podemos adaptar el diseño del control a nuestro sitio.
Fig. 6-37. En Visual Studio o Visual Web Developer podremos tener una vista previa de cómo se verá nuestro menú dentro de la página.
Alfaomega
Visual Studio - Firtman, Natale
Identificación de s
281
Si bien desde el archivo Web.sitemap o en cada opción (con NavigateUrl) podemos tener definidas las URLs a dónde dirigir al , podemos capturar el evento de servidor MenuItemClick para realizar alguna acción o saber qué opción eligió el recibiendo por parámetro el Item seleccionado, cuando no definimos ninguna URL por defecto en una opción.
Identificación de s Introducción Ya en ASP.NET 1.x existía el concepto de autenticación y autorización en páginas Web ASP.NET, que permitían que ciertas páginas en nuestro sitio estén restringidas solamente a s registrados, a algún en particular o a un que pertenezca a un rol. Los conceptos se mantienen y seguimos teniendo autenticación basada en Windows, basada en formularios y basada en Microsoft port, agregando soporte para Windows Cardspace. Autenticación por Formularios El concepto sigue siendo el mismo, pudiendo definir en un archivo :HEFRQ¿J la seguridad que se ha de aplicar en distintas carpetas, por ejemplo: FRQ¿JXUDWLRQ! <system.Web>
Visual Studio - Firtman, Natale
Alfaomega
282
6- ASP.NET
<deny s="?" />
FRQ¿JXUation>
En este archivo estamos denegando a todos los s anónimos (no autenticados) que intenten ingresar a la carpeta actual. Desde ASP.NET 2.0 aparecen algunas opciones adicionales a la autenticación por formularios, como ser cookieless, que permite guardar los datos del en la URL, enableCrossAppRedirects, que permite pasar la actual autenticación del hacia otra aplicación .NET y requiere SSL para obligar a identificarse mediante este método. Recordemos que con Url indicamos a dónde dirigir al cuando intenta ingresar a una página privada.
Podemos definir s diferenciados por roles, como por ejemplo:
<deny s="*" />
Y recordemos que en .aspx, cuando verificamos que el y la contraseña son válidos, debemos invocar al siguiente método para que el sistema registre la autenticación: FormsAuthentication.RedirectFromPage(nombre_, cookie_pesistente)
Sin embargo, en breve veremos que ahora ya ni necesitaremos hacer esto con los nuevos controles disponibles.
hip Clase hip La clase hip pertenece al namespace System.Web.Security y provee de métodos para crear y almacenar información de s del sistema (que son de clase hip). Esta clase, junto con un proveedor de datos, puede ocuparse por su cuenta de istrar los s (no Alfaomega
Visual Studio - Firtman, Natale
Identificación de s
283
necesitamos crear esta tabla en nuestra base de datos siquiera, salvo que lo necesitemos), para validar las credenciales de ingreso y para trabajar con la contraseña. Se pueden crear s desde código de servidor, a través de la clase hip, por ejemplo con el método Create, que tiene distintas sobrecargas para enviarles más o menos datos. Por ejemplo, hip.Create("", "contraseña", "email");
Otra opción es utilizar el Web de ASP.NET, que nos permitirá crear y istrar los s desde la interfaz Web. Recordemos que accedemos a esta opción desde Visual Studio o Visual Web Developer en el menú Website, $631(7&RQ¿JXUDWLRQ. ASP.NET viene con dos proveedores que le pueden brindar el almacenamiento de los registros: ActiveDirectoryhipProvider, para conectarnos a un servidor de directorio activo de Microsoft, y SqlhipProvider, que nos permite almacenar toda la información en SQL Server o Access. Esta configuración se hace en el :HEFRQ¿J o utilizando el Web. Algunas propiedades interesantes son EnableReset, que permite definir si vamos a autorizar reiniciar la contraseña, EnableRetrieval, para definir si permitimos recuperar la contraseña, MaxInvalidAttemps, para la cantidad máxima de intentos de ingreso de claves erróneas antes de bloquear la cuenta, MinRequiredLength, para la cantidad de caracteres mínimos de la contraseña y IsOnlineTimeWindows, para la cantidad de minutos de inactividad en que se considera al desconectado. Los métodos que más vamos a utilizar, que son simples de reconocer por su nombre, son: Create, Delete, FindsByEmail, FindsByName, Generate (que genera contraseña al azar), GetAlls, GetNumerOfsOnline, Get y Validate. Clase hip Esta clase representa y almacena a un registrado en el proveedor de datos de hip. Los atributos más comunes de un son los siguientes: Atributos
Descripción
Comment
Comentario sobre el .
CreationDate
Fecha y Hora de creación.
Visual Studio - Firtman, Natale
Alfaomega
284
6- ASP.NET
Email
Dirección de correo electrónico.
IsApproved
Si esta opción está en falso, el no puede loguearse.
IsLockedOut
Si esta opción está en verdadero, el fue bloqueado.
IsOnline
Dice si el está conectado.
LastDate
Última fecha de .
Question
Pregunta que definió el para recuperar su contraseña.
Name
Devuelve el nombre de .
Los métodos que más utilizaremos son Change, que cambia la contraseña del , Reset, para reinicializar la contraseña y Unlock, para desbloquear un que ha sido bloqueado por intentos erróneos de contraseña.
Roles El sistema de membresía también incluye una istración de roles (la clase Roles). Un rol simplemente es un nombre conceptual al que luego podemos asignarle s y, ante alguna operación o a algún Webform, podemos definir a qué roles habilitamos.
Controles Más allá de que tengamos una clase hip que se ocupa de todo, ¿qué más ha cambiado desde ASP.NET 1.x? Para saber eso, veamos la lista de controles de , que ahora harán todo automáticamente comunicándose con la clase hip.
Este es el control principal que usaremos si utilizamos los servicios de hip. Es un control Web que representa la casilla de identificación de en el sitio Web. Posee dos campos de texto, para y contraseña, una casilla de verificación para definir si queremos ser recordados la próxima vez y un botón de . Cuando el ingresa sus datos automáticamente es el sistema de hip el que lo valida y el que valida las credenciales del .
Alfaomega
Visual Studio - Firtman, Natale
Identificación de s
285
Fig. 6-38. Aquí vemos el control sin ninguna propiedad especial configurada. Con este control ya tenemos un sistema de identificación de s funcionando.
Algunas propiedades útiles para tener en cuenta son: Propiedad FailureText
Descripción Texto que se ha de mostrar cuando los datos de identificación son inválidos.
InstructionText
Texto que se muestra por encima de los controles de ingreso, dando instrucciones al de cómo debe proceder.
ButtonImageUrl
Si queremos que el botón de sea una imagen, aquí va la URL.
ButtonText
Texto que se ha de mostrar en el botón.
ButtonType
Puede ser Button, Image o Link.
Label
Etiqueta que se ha de usar para mencionar la contraseña.
Etiqueta que se ha de usar para mencionar el nombre de . La URL a dónde dirigir al cuando se loguea correctamente. Permite definir si mostramos o no al la opción de que el sistema lo recuerde. Permite definir una orientación horizontal o vertical de los datos. Define si las etiquetas de los controles de ingreso se muestran a la izquierda (TextOnLeft) o arriba (TextOnTop).
RequiredErrorMessage Mensaje de error cuando no ingresa la contraseña. NameRequiredErrorMessage Mensaje de error cuando no ingresa el .
Visual Studio - Firtman, Natale
Alfaomega
286
6- ASP.NET
También, tenemos disponibles propiedades para generar links adicionales en el control. Estas propiedades son <nombre>Text con el nombre que se ha de mostrar en el link, <nombre>Url con la dirección a dónde enviar al y <nombre>Icon con la Url de un ícono opcional para utilizar en el link. Donde dice <nombre> podemos reemplazarlo por Create para el link destinado a que el se cree una nueva cuenta; HelpPage para el link de ayuda; y Recovery para el link que lleve al a recuperar su contraseña. Name Este control es realmente muy simple pero útil. Muestra el nombre del logueado, si es que está identificado, y no genera ningún código HTML si el todavía no está identificado. Su única propiedad fuera de las propiedades de formato, es FormatString, que permite definir un texto adicional, por ejemplo : {0}, donde se reemplazará el nombre de donde está el {0}. Status Este control permite generar vínculos para loguear o desloguear al . Es útil para ubicar en todas las páginas Web y, según el estado de identificación del , muestra uno y otro link. El texto de los links lo podemos cambiar en Text y Text o, si queremos utilizar imágenes, ImageUrl y ImageUrl. También debemos definir cuál es la página encargada de hacer el (eliminar la identificación) en PageUrl y qué queremos hacer una vez que el se desloguea (Action), si refrescar la misma página (Refresh), redirigir al a la página que definimos anteriormente (Redirect) o reenviar al a la página de (RedirectToPage). View La vista de , o View, es un control que hereda comportamiento del control View y permite definir distinto contenido (vistas) según si el está identificado o no. También, en el caso de que lo esté, es posible definir vistas según el rol al que pertenezca el . De esta manera, cuando arrastramos un control de esta clase a un Webform, veremos que Visual Studio nos da automáticamente dos vistas disponibles para editar: AnonymousTemplate para cuando el no está identificado y LoggedInTemplate para cuando sí lo está.
Alfaomega
Visual Studio - Firtman, Natale
Identificación de s
287
Fig. 6-39. En Visual Studio o VWD podemos elegir la vista que se ha de utilizar, ya sea para s anónimos o para s registrados.
El entorno de trabajo nos da la posibilidad, también, de agregar una vista adicional por cada grupo de roles que querramos definir. Un ejemplo sería:
Es del rol
(VWi/RJXHDGRFRQHOXVXDULR
1RHVWiORJXHDGR
Recovery El control Recovery (Recuperación de Contraseña) ofrece toda la funcionalidad para que el pueda recuperar la contraseña si la ha olvidado, enviándosela por e-mail en caso de que cumpla con los requisitos. El control se basa en tres plantillas: Name, que le pide el nombre de para enviar la contraseña, Question, que le pide la pregunta y respuesta privada para garantizar que es quien dice ser y Sucess, que le avisa al que la contraseña le fue enviada por e-mail. Así como el control , podemos cambiar todos los textos y nombres de los botones. Recordemos que la funcionalidad se realiza automáticamente y no Visual Studio - Firtman, Natale
Alfaomega
288
6- ASP.NET
necesitaremos ningún código de servidor para que funcionen; automáticamente usan el servicio de hip. Change Otro control que realiza toda su funcionalidad automática es el cambio de contraseña o Change. Este control automáticamente le pide al su contraseña actual y que ingrese una nueva dos veces, realiza la verificación de estos datos y, si fue existoso, pasa a una vista de Sucess, donde le avisa al que el cambio fue realizado. Tendremos varias propiedades para definir todos los textos en nuestro idioma y las propiedades CancelDestinationPageUrl y ContinueDestinationPageUrl para definir los nombres de las páginas si el cancela o hace el cambio de contraseña, respectivamente.
Fig. 6-40: Así se ve el control para cambiar la contraseña, editando las propiedades que definen el nombre de cada componente
CreateWizard Este control es un asistente para registrar un nuevo en el sistema, con dos pasos ya diseñados por el control: Sign Up (con los datos para registrarse) y Complete para informar al que ha sido dado de alta. Este control es tan complejo en la cantidad de propiedades y estilos que podemos definir hasta lograr la apariencia que deseamos, que excede el espacio que podemos dedicarle en este libro. Tengamos en cuenta que si queremos podemos también definir nuevos pasos para información adicional. Por defecto, los campos que solicita al son: Nombre de , Contraseña, E-mail, Pregunta y Respuesta (para recuperar la contraseña). Si definimos en True a Created, automáticamente el será identificado después de crearse y si activamos DisableCreated, el no podrá identificarse en el sistema hasta tanto no sea habilitado por el . Con FinishDestinationPageUrl definiremos la dirección a dónde enviar al luego de crear su nueva cuenta.
Alfaomega
Visual Studio - Firtman, Natale
Identificación de s
289
Es importante que mencionemos un objeto 0DLO'H¿QLWLRQ dentro del CreateWizard, que permite definir distintas opciones relativas al e-mail de confirmación de alta que recibirá el cuando se cree su cuenta. Entre ellas tenemos From (dirección remitente), IsBodyHTML (define si el cuerpo es en formato HTML), Subject (asunto) y BodyFileName, que especifica el nombre de un archivo (de texto o HTML) que contiene el cuerpo del mensaje que se le enviará al . También podemos enviarle archivos adjuntos a través de EmbeddedCollection. El archivo que tiene el cuerpo del mensaje podrá contener expresiones del tipo <%Name%> y <%%> para ser reemplazados por el y contraseña recién creados.
Fig. 6-41. El clásico formulario de registro de s ahora lo tenemos disponible con sólo arrastrarlo y, sin escribir nada de código, funcionará solo gracias al servicio de hip.
Visual Studio - Firtman, Natale
Alfaomega
290
6- ASP.NET
Fig. 6-42. Visual Web Developer nos ofrece una lista extensa de Smart Tasks para el control CreateWizard.
Perfiles de ¿Qué es un Perfil? El perfil de un es un conjunto de atributos que queremos almacenar por cada registrado en nuestro sitio. Estas propiedades se definen en un archivo :HEFRQ¿J y, luego, en ASP.NET podemos accederlos a través del objeto instanciado 3UR¿OH dentro de cualquier página. ASP.NET automáticamente toma el esquema que se definió en :HEFRQ¿J y genera la clase con sus propiedades públicas, como cualquier clase. Si bien la idea de 3UR¿OH es almacenar información de s registrados, también podemos hacerlo con s anónimos a través de la opción $QRQ\PRXV,GHQWL¿FDWLRQ. Luego, si se identifican o registran podemos migrarlos a perfiles autenticados. A través del 3UR¿OH0DQDJHU podemos istrar los perfiles de s, consultar estadísticas, saber la cantidad que no ha actualizado su perfil en un período de tiempo o eliminar perfiles por unidad o por antigüedad.
Alfaomega
Visual Studio - Firtman, Natale
WebParts
291
Definición de los Perfiles Veamos rápidamente de qué se trata mirando un ejemplo de :HEFRQ¿J: <system.Web> SUR¿OHHQDEOHG WUXHGHIDXOW3URYLGHU 4XLFN6WDUW3UR¿OH6TO3URY LGHULQKHULWV 8VHU'H¿QHG3UR¿OH&ODVV! <properties>
DGGQDPH &RORU'H7LSRJUD¿D3UHIHULGRW\SH 6\VWHP Drawing.Color" allowAnonymous="true" serializeAs="Binary"/>
SUR¿OH!
Accediendo al perfil Si queremos acceder a una propiedad, tenemos a 3UR¿OH3URSLHGDG para leer o escribir una propiedad desde cualquier página. A través de IsAnonymous podremos saber si el es anónimo o autenticado y LastActivityDate nos da la posibilidad de saber la última fecha de actividad en el perfil (lectura o escritura).
WebParts Qué son El último paso que ha incorporado ASP.NET 2.0 en cuanto a personalización son los WebParts. Es una serie de controles Web que permiten tener contenido dinámico y personalizado en una página, en donde el puede decidir en qué lugar colocar cada contenido, qué contenido quiere ver y la posibilidad de personalizar cada uno de los módulos de contenido. Por ejemplo, podemos tener una página Web con información del clima, información financiera, noticias, etc. o una página principal de una intranet don-
Visual Studio - Firtman, Natale
Alfaomega
292
6- ASP.NET
de ofrecemos noticias de la empresa, al calendario de eventos, a un módulo para pedir comida al restaurant de la empresa y hasta a un módulo para analizar las ventas del empleado. Todos estos módulos podrían ser editados por cada , entendiendo por editar la posibilidad de cambiar preferencias de cada uno, cambiarlos de lugar, de tamaño o eliminar módulos y agregar otros. Todo esto queda registrado debidamente en el sistema para que cuando el vuelva en otro momento, sus preferencias se mantengan vigentes. Esto es WebParts en ASP.NET. Y agregamos la idea de que el manejo de los controles tiene una interfaz muy agradable y similar a la que utilizan sitios de este tipo, como Windows Live, Google Personalizad o My Yahoo! La información de personalización se guarda automáticamente por y es él quien puede mover el contenido en la página, arrastrando y soltando, generando un efecto visual en la interfaz.
Conceptos Una página Web con WebParts contiene lo siguiente: 1. Distintos WebParts (módulos de contenido) que no son más que controles Web o controles personalizados, que trabajarán en conjunto con las zonas y el de WebParts. 2. Zonas de WebParts, que son el método para organizar los módulos de contenido en regiones dentro de la página. 3. El de WebParts (WebPartManager), que istra el estado de personalización de las partes, coordina el movimiento y comunicación entre las partes y las zonas. Es obligatorio tener un control de éstos en una página. Además, existirá un catálogo de WebParts, que indicará un listado de módulos de contenido que estarán disponibles para que cada agregue o quite de su página personalizada.
Controles El uso de WebParts podría llevarnos todo otro libro completo, por lo que vamos a nombrar los controles Web que tenemos disponibles para trabajar con esta funcionalidad y dejamos por cuenta del lector aumentar la profundidad de estos temas.
Alfaomega
Visual Studio - Firtman, Natale
293
Nuevos frameworks
Control Web
Descripción
WebPartManager
Es el control del sistema de WebParts y es obligatoria su inclusión.
ProxyWebPartManager
Permite definir una conexión estática cuando estamos incluyendo un WebPartManager en una página Maestra.
WebPartZone
Representa una zona en la página donde se podrán incluir WebParts.
CatalogZone
Representa una zona donde se le ofrece al la lista de módulos de información disponibles para agregar.
DeclarativeCatalogPart
Permite agregar controles de servidor al catálogo.
PageCatalogPart
Contiene referencia a las WebParts que el ha cerrado previamente.
ImportCatalogPart
Permite importar la referencia a una WebPart desde un archivo.
EditorZone
Es una zona que se utiliza cuando cualquier intenta editar cualquier WebPart. Aquí el define todas las propiedades.
AppearanceEditorZone
Deriva de EditorZone y permite definir propiedades respecto de la apariencia de un módulo.
BehaviourEditorZone
Deriva de EditorZone y permite definir propiedades respecto del comportamiento de un módulo.
LayoutEditorZone
Deriva de EditorZone y permite definir propiedades respecto de la estructura de visualización.
PropertyGridEditorZone
Permite definir propiedades personalizadas por el .
ConnectionZone
Permite que las WebParts se puedan conectar entre ellas y enviarse información.
Nuevos frameworks Dynamic Data ASP.NET Dynamic Data (Datos Dinámicos o Campos Dinámicos) es un nuevo framework incorporado en 3.5 SP1, que nos permite generar un sitio basado en un origen de datos (generalmente una base de datos) que cubra la mayor parte de la funcionalidad básica de estos sitios, a saber: s s s s
Consulta de Registros. Búsqueda de Registros. Filtrado de Registros. Manejo de Relaciones Maestro > Detalle.
Visual Studio - Firtman, Natale
Alfaomega
294
s s s s s s
6- ASP.NET
Manejo de Relaciones Padre > Hijo, por ejemplo, Categorias y sus Productos. Manejo de Relaciones Hijo > Padre, por ejemplo, Producto y la categoría a la que pertenece. Edición de Registros. Eliminación de Registros. Inserción de Registros. Validación de la edición e ingreso de datos.
Fig. 6-43. Aquí vemos un ejemplo de sitio Web de campos dinámicos, sin escribir una sola línea de código.
Y la mejor parte de todo esto, es que el framework se encarga de todo. Lo único que tenemos que poseer es un modelo de datos consistente y bien armado, por ejemplo, LINQ To SQL.
Alfaomega
Visual Studio - Firtman, Natale
Nuevos frameworks
295
Creando un proyecto Los pasos para poder trabajar en forma simple y rápida con este framework son: s
Crear un nuevo proyecto o sitio Web de tipo FDPSRVGLQiPLFRV (para LINQ to SQL) o de tipo HQWLGDGHV GH FDPSRV GLQiPLFRV (para Entity Framework).
s
Crear la base de datos en SQL Server, con todas las claves primarias, todas las relaciones armadas con restricción de clave foránea, correctamente definidos los campos que aceptan nulos.
s
Crear el modelo LINQ to SQL o Entity Framework (según el caso). Para el primer caso, en Visual Studio, no es más que crear un nuevo elemento de tipo Clases de LINQ to SQL y arrastrar todas las tablas del SQL Server al diseñador.
s
Editar el archivo global.asax, descomentar la línea indicada, indicar el tipo de datos DataContext, que en LINQ to SQL sabemos que será (nombre)DataContext.
s
En global.asax definir ScaffoldAllTables = true.
s
Ejecutar la aplicación Web.
Al crear el proyecto, el sistema ya nos creará una serie de archivos, entre ellos el global.asax y una carpeta DynamicData con varias plantillas y archivos que podremos editar. En el global.asax debemos encontrar una línea que aparece comentada así en Visual Basic: ' model.Context(GetType(YourDataContextType), New ConWH[W&RQ¿JXUDWLRQ :LWK^6FDIIROG$OO7DEOHV )DOVH`
En C#: //model.Context(typeof(YourDataContextType), new ConWH[W&RQ¿JXUDWLRQ ^6FDIIROG$OO7DEOHV IDOVH`
Debemos descomentar la línea en cuestión, cambiar YourDataContextType por el nombre de la clase DataContext que LINQ to SQL nos generó, que debería ser nombreDataContext. Por ejemplo, si al modelo creado le llamamos Ventas, el tipo sería VentasDataContext. Por defecto, el sistema no habilita el trabajo sobre todas las tablas, a no ser que cambiemos el atributo ScaffoldAllTables por verdadero.
Visual Studio - Firtman, Natale
Alfaomega
296
6- ASP.NET
Fig. 6-44. Así se ve la primera pantalla de un sitio Web de campos dinámicos.
Si navegamos por el sitio, veremos que tendremos disponible un vínculo por cada tabla y en cada tabla, la posibilidad de editar, eliminar, insertar y filtrar. Todo sin escribir una sola línea de código.
Fig. 6-45. Podemos verificar que, en el caso de claves foráneas, automáticamente al editar se utiliza un DropDownList para la selección del dato. Alfaomega
Visual Studio - Firtman, Natale
297
Nuevos frameworks
Maestro-Detalle Si en lugar de tener todas las operaciones (listar, detalle, edición e inserción) sobre la misma página, quisiéramos cambiar a un modelo Maestro Detalle, para eso comentaríamos o eliminaríamos las siguientes líneas: routes.Add(New DynamicDataRoute("{table}/{action}.aspx")_ With { _ .Constraints = New RouteValueDictionary(New With_ {.Action = "List|Details|Edit|Insert"}), _ .Model = model})
Y descomentaríamos las siguientes: routes.Add(new DynamicDataRoute("{table}/ListDetails.aspx") { Action = PageAction.List, ViewName = "ListDetails", Model = model }); routes.Add(new DynamicDataRoute("{table}/ListDetails.aspx") { Action = PageAction.Details, ViewName = "ListDetails", Model = model });
Funcionamiento Si intentamos analizar un poco del código del método Routes en el global.asax, el cual es invocado por el evento Application_Start, vemos que lo que realiza es una cierta registración de algo llamado Ruta Dinámica. Una ruta dinámica es una URL inexistente físicamente en el directorio de la aplicación Web, pero que es capturada por un nuevo framework de rutas y redirigida a la función correspondiente. Por eso, cuando probamos nuestro proyecto de campos dinámicos veremos que al entrar a una tabla, por ejemplo, Clientes, vemos una URL del estilo: http://dominio/Clientes/List.aspx
Visual Studio - Firtman, Natale
Alfaomega
298
6- ASP.NET
Fig. 6-46. También podemos separar la lista del detalle.
Y si revisamos en la carpeta de archivos, no existe ninguna subcarpeta con el nombre Clientes. Este es el concepto de ruteo dinámico. El sistema automáticamente relaciona algo/List.aspx a una operación de listado, siendo algo, el nombre de la Tabla. Personalización básica La primera personalización que podemos hacer a un sitio Dynamic Data es modificar sus estilos, y para ello podemos encontrar en la carpeta raíz los archivos Site.master y Site.css, siendo la página principal (Master Page) y la hoja de estilos CSS, respectivamente. Luego encontraremos en la carpeta DynamicData/PageTemplates varias páginas ASPX, que son las plantillas que se utilizan para cada operación del sitio Web, a saber:
Alfaomega
Visual Studio - Firtman, Natale
299
Nuevos frameworks
Archivo
Propósito
Details.aspx
Muestra el detalle de un registro.
Edit.aspx
Muestra la edición en detalle de un registro.
Insert.aspx
Muestra la inserción en detalle de un registro.
List.aspx
Muestra las operaciones de listado.
ListDetail.aspx
Muestra las operaciones de listado cuando estamos en el formato maestro-detalle.
En la carpeta DynamicData/FieldTemplates encontraremos distintos controles de ASCX, que se utilizan automáticamente para cada tipo de datos, en versión visualización (como Boolean.ascx) y en versión edición (como Boolean_Edit.ascx). Allí se pueden editar, y también agregar, cómo queremos que se dibuje cada campo, según el tipo de datos definido en el origen de datos.
Fig. 6-47. Aquí vemos la carpeta FieldTemplates con cada control representando un tipo de datos.
Por último, en DynamicData/Content encontraremos controles que se utilizan en la paginación de registros y en el filtrado del contenido de las tablas. Personalización avanzada El framework de campos dinámicos es mucho más complejo e interesante. Recomendamos ampliamente visitar el sitio www.asp.net/dynamicdata y revisar toVisual Studio - Firtman, Natale
Alfaomega
300
6- ASP.NET
dos los videos y ejemplos de cada funcionalidad que podemos aplicar al sitio Web, sin ingresar código intrusivo, es decir, sin terminar haciendo un sitio Web complejo. Por ejemplo, es posible definir qué tablas van a ser editables, definir una página existente distinta por cada una, en lugar de utilizar ruteos dinámicos. También, a través de metadatos sobre las clases del modelo, es posible modificar: s s s s
s s
Nombres visibles de las tablas. Nombres visibles de cada atributo. Qué control Web utilizar para mostrar y/o editar cada campo, por ejemplo, un control de texto rico para un campo comentarios. Agregar reglas de validación adicionales, como por ejemplo, una expresión regular para una dirección de correo, un rango válido de un campo numérico o una regla de negocio propia de un campo. Definir campos de sólo lectura. Utilizar AJAX y controles ricos para los campos.
Estos temas escapan al alcance de este libro, pero vale mencionar que estas opciones no se modifican en un ASPX o en una plantilla, sino directamente sobre el origen del dato y sobre las clases que representan a cada tabla, lo que lo hace totalmente orientado a objetos y muy simple y sencillo de modificar. Esto es gracias a que, por ejemplo en LINQ to SQL, cada clase que representa a una tabla (Cliente por ejemplo), está definida como una clase parcial. Esto implica que podemos agregar un nuevo archivo Cliente.cs o Cliente.vb, también parcial, e incorporarle a la clase metadatos (atributos), nuevos atributos, métodos de lógica y todo lo que queramos. El framework Dynamic Data lee, mediante reflexión, estos metadatos y con ello modifica el comportamiento del sitio Web. Por ejemplo, con definir en la clase parcial el nombre de una clase extra de metadatos alcanza para que Dynamic Data cambie su comportamiento: >0HWDGDWD7\SHW\SHRI&OLHQWH0HWDGDWD @ public partial class Cliente { } // Sirve para proveerle de metadatos a Dynamic Data sobre la // clase Cliente Public class ClienteMetadata { // Con colocar object como tipo de datos, alcanza. // Solo importa que el nombre del atributo concuerde con // el atributo en Cliente >'LVSOD\1DPH1RPEUHGHO&OLHQWH @ >5HTXLUHG(UURU0HVVDJH (OQRPEUHHVREOLJDWRULR @ public object Nombre; }
Alfaomega
Visual Studio - Firtman, Natale
Nuevos frameworks
301
El código anterior habilitaría a Dynamic Data a colocar “Nombre del Cliente” como encabezado y una validación de campo obligatorio sobre el atributo Nombre de la clase Cliente, y como mensaje de error colocaría “El nombre es obligatorio”. Otros atributos de uso común sobre los atributos son RegularExpression, Range, UIHint, DataType, DisplayFormat y StringLength. La lista de posibles atributos que se pueden definir sobre las tablas y/o sobre los atributos las encontramos en System.ComponentModel y System. ComponentModel.DataAnnotations. También DynamicData define algunos métodos parciales, que podemos implementar en nuestra porción de la clase para capturar cambios en las tablas y cambios en cada uno de los atributos, lo que nos permitirá definir una lógica más compleja de validación, auditoría y aceptación de cambios. El código anterior es totalmente no intrusivo respecto de la visualización y operación de las páginas Web y queda claro que modifica sólo la capa de lógica de negocios. Manualmente dinámicos Si bien no es habitual, también es posible utilizar los controles del framework de datos dinámicos en un sitio ASP.NET común y manejar toda la lógica manualmente. Para ello, el framework incorporó los controles DynamicDataManger, que es la clase organizadora de esta metodología y obligatoria para el uso del resto, DynamicControl, que representa un control Web dinámico según el tipo de datos del campo que se ha de mostrar y DynamicValidator, que agrega validación automática según las reglas de negocio y metadatos asociados al atributo en cuestión.
URL Routing Este framework disponible en el namespace System.Web.Routing permite utilizar las técnicas de ruteo dinámico vistas en Dynamic Data y que quedan por ver en ASP.NET MVC, para nuestras propias aplicaciones con nuestra lógica. Esto permite trabajar con URLs dinámicas y, entre las ventajas, además de encapsular la dirección física de los formularios Web y sus parámetros, permite realizar lo que se conoce como URLs amigables o friendly URLs, que ayudan a que los motores de búsqueda indexen mejor nuestra Web, cambiando URLs del tipo dominio.com/producto.aspx?id=2344 por URLs del tipo dominio.com/monitores/pantalla-lcd/samsung-syncmaster-740. Antes de este framework era posible realizar esta tarea a través de HTTP Handlers con ASP.NET.
Visual Studio - Firtman, Natale
Alfaomega
302
6- ASP.NET
Ruteo por IIS Para una técnica de ruteo dinámico más estática, que se pueda expresar por reglas definidas por expresiones regulares, el equipo del servidor Internet Information Server (IIS) lanzó un agregado gratuito para IIS 7 llamado URL Rewrite, que emula de cierta forma la ya conocida metodología del servidor Apache para realizar la misma tarea. Este módulo agregado puede ser descargado desde el sitio iis.net.
Controles Silverlight Ya sabemos que Silverlight es un nuevo modelo de desarrollo de aplicaciones Web ricas en el cliente, equiparable en alguna medida con Adobe Flash, utilizando .NET Framework y varios lenguajes, entre ellos C#, Visual Basic y JavaScript, compatible con varios navegadores y sistemas operativos. Descargando Silverlight SDK desde silverlight.net, podremos no sólo crear aplicaciones Silverlight desde Visual Studio y desde Visual Web Developer Express, sino que también se nos agregarán controles ASP.NET para trabajar con contenido de este tipo. Estos controles se agregan en el namespace System.Web.UI.Silverlight Controls y requieren que se disponga de un ScriptManager, control de ASP. NET AJAX, que veremos en el próximo capítulo. MediaPlayer Es un nuevo control ASP.NET, que nos permitirá generar un reproductor multimedia en nuestro sitio Web. Podremos reproducir cualquier archivo compatible con el runtime de Silverlight, entre ellos WMV (Windows Media Video), WMA (Windows Media Audio), MP3 o un contenido distribuido por un Windows Media Server. Sólo debemos elegir el origen de datos y elegir de una lista de skins o temas predefinidos para el reproductor creados en formato XAML. Aquí vemos un ejemplo de código.
Alfaomega
Visual Studio - Firtman, Natale
Nuevos frameworks
303
Fig. 6-48. Crear un reproductor de video es cuestión de segundos con Silverlight y ASP.NET.
Contenido Silverlight El control Silverlight permite incluir un contenido RIA (aplicación rica de Internet) desarrollado con tecnología Silverlight fácilmente. Utilizar el control simplemente se realiza definiendo el archivo origen, tamaño del control y versión del player de Silverlight requerido.
También es posible integrarse con JavaScript, capturar eventos y crear controles Silverlight que interactúen con ASP.NET.
Web Platform Installer En el afán de simplificar la tarea de crear aplicaciones ASP.NET, Microsoft creó los Starter Kits o kits de iniciación, que no son más que aplicaciones ASP.NET completas y funcionales, con todo lo necesario para ponerla en funcionamiento. Existen sitios personales, foros, sitios de e-commerce y pueden ser descargadas gratuitamente desde www.asp.net/s. Para darle un apoyo todavía más fuerte a la instalación de paquetes prearmados de sitios Web en ASP.NET, Microsoft creó el Web PI (Platform Installer o instalador de plataforma). Visual Studio - Firtman, Natale
Alfaomega
304
6- ASP.NET
El Web Platform Installer es un paquete gratuito que incluye las últimas versiones de: s
Internet Information Server.
s
SQL Server Express.
s
.NET Framework.
s
Visual Web Developer.
s
Aplicaciones Web populares ASP y PHP, como Wordpress o DotNetNuke.
Lenguajes Dinámicos Una incorporación todavía no finalizada al momento de editar este libro es la de lenguajes dinámicos, no estrictos ni compilados, en el framework de ASP.NET. Por ello existe el sitio www.asp.net/dynamiclanguages, donde es posible descargarse la última versión y adosarla a ASP.NET. El lenguaje disponible es IronPython, una implementación del clásico lenguaje Python para ASP.NET, que permitiría escribir sitios Web más rápidos (con las ventajas y desventajas de cualquier lenguaje dinámico). En el futuro se piensa incluir IronRuby, Managed Visual Basic (similar al viejo VBScript) y JScript (similar a JavaScript).
Alfaomega
Visual Studio - Firtman, Natale
ASP.NET AJAX
AJAX y MVC
305
7
ASP.NET AJAX AJAX y RIA El framework Historia Microsoft no quiso quedarse atrás en el mundo de las aplicaciones ricas de Internet y de AJAX en concreto y, luego del lanzamiento de ASP.NET 2.0, comenzó a trabajar en el proyecto con nombre en código “Atlas”, un framework unido a los formularios Web de ASP, que permite trabajar con la modalidad de refresco parcial de un formulario, entre otras características. Así, a principios de 2007 surgió el hoy llamado ASP.NET AJAX, compuesto por una librería de cliente –Microsoft AJAX Library– compatible con cualquier lenguaje de servidor, un conjunto de controles de servidor, AJAX ASP.NET Extensions y una serie de controles y adaptadores de servidor de código abierto y ASP.NET AJAX Control Toolkit. ASP.NET AJAX está disponible como una descarga independiente para Visual Studio 2005 y .NET 2.0 y está incluido desde Visual Studio 2008 y .NET 3.5, por lo que ahora es parte integral de la plataforma .NET y evoluciona en conjunto con el resto de los componentes. AJAX Éste no pretende ser un libro de AJAX; para profundizar en esta técnica, recomendamos el libro “AJAX. Web 2.0 para Profesionales”, de esta misma editorial, y cuyo autor, Visual Studio - Firtman, Natale
Alfaomega
306
7- AJAX y MVC
el Lic. Maximiliano Firtman, es uno de los autores de este libro de Visual Studio. Para resumir, diremos que AJAX (Asynchronous JavaScript and XML, es decir, JavaScript Asincrónico con XML), es una técnica que involucra JavaScript, XML (aunque estrictamente XML se empezó a dejar de usar a cambio de JSON o de HTML) y un objeto hoy llamado XMLHttpRequest, invento original de Internet Explorer (y hoy compatible con el resto de los navegadores), que permite realizar una petición desde JavaScript hacia el servidor y recibir la respuesta sin necesidad de cambiar la página que el está viendo, ni realizar un POST o modificar el historial del navegador. La mayoría de los sitios Web 2.0, ágiles y rápidos (como Facebook, GMail, Google Maps, Windows Live Mail), están desarrollados utilizando esta técnica.
Fig. 7-1. Libro “AJAX. Web 2.0 para Profesionales”, de Maximiliano Firtman.
Librería de Cliente La librería conocida como Microsoft AJAX Library es un conjunto de funcionalidades que se agregan al lenguaje JavaScript para darle soporte de características propias de .NET. Esta librería es compatible con todos los navegadores y cualquier plataforma en el servidor, como ser PHP. Entre los agregados a JavaScript se encuentran: s
Extensiones al tipo String: Funciones endsWith, format, startsWith, trim, trimEnd, trimStart, localeFormat. s Extensiones al tipo Object: Funciones getType, getTypeName. s Extensiones al tipo Number: Funciones format, localeFormat, parseLocale y parseInvariant. s Extensiones al DOM: $get y funciones addCssClass, containsCssClass , removeCssClass , getLocation , setLocation, getBounds. s Extensiones de tipo Eventos: Funciones addHandler, removeHandler, clearHandlers, preventDefault, stopPropagation. s Extensiones al tipo Date: Funciones format, localeFormat, parseLocale y parseInvariant. s Extensiones al tipo Array: Funciones add, addRange, clone, contains, dequeue, forEach, indexOf, insert, parse, enqueue, remove, removeAt. s Incorporación de conceptos de espacio de nombres, para agrupación de objetos y clases. Alfaomega
Visual Studio - Firtman, Natale
ASP.NET AJAX
s s
307
Incorporación de Sys.Application, emitiendo los eventos init, load, unload y disposing. Incorporación de Sys.WebForms.PageRequestManager, emitiendo los eventos initializeRequest, beginRequest, pageLoading, pageLoaded, endRequest.
window
Application
PageRequestManager
onload
pageload init
load
initializeRequest
beginRequest
pageLoading
pageLoaded onunload endRequest pageUnload unload
disposing
Fig. 7-2. El nuevo modelo de eventos disponible en una página desde el punto de vista de JavaScript, utilizando Microsoft AJAX Library. Visual Studio - Firtman, Natale
Alfaomega
308
7- AJAX y MVC
Librería de Servidor Si bien es posible utilizar la librería de cliente sin necesidad de ASP.NET, lo cierto es que si trabajamos con esta plataforma de servidor, es muy probable que no necesitemos escribir JavaScript para utilizarla. Eso es debido a que el framework incluye unos controles de servidor que facilitan la tarea de implementar soluciones AJAX en nuestros desarrollos Web, conocidos como las Extensiones AJAX para ASP.NET. El framework no sólo es un framework más de AJAX disponible en el mercado; es el único que puede convertir cualquier sitio Web en AJAX en razón de 30 segundos, mientras que esté desarrollado en ASP.NET 2.0 o superior. Y esto se debe a que la naturaleza de los formularios y controles Web de ASP.NET, el PostBack y otras características propias de ASP hicieron que sea no intrusiva la incorporación de técnicas AJAX.
Configuración Si estamos utilizando Visual Studio 2008 (o Visual Web Developer 2008 Express), así como .NET Framework 3.5 o superior, no necesitaremos nada especial para hacer uso de este framework. Simplemente, incluiremos en nuestro formulario Web los controles de AJAX que veremos a continuación y funcionará sin problemas. Si, en cambio, estamos todavía utilizando Visual Studio 2005, necesitaremos descargar ASP.NET AJAX Extensions desde el sitio Web www.asp.net/ajax y configurar en el :HEFRQ¿J las extensiones. Al instalar lo que es el agregado, Visual Studio tendrá una plantilla de creación de sitio Web que configurará automáticamente las extensiones. Desde 2008, esto ya no es necesario, aunque es posible descargar las plantillas por compatibilidad con proyectos viejos. Para poder hacer uso de estas extensiones, en cada formulario Web donde queremos hacer uso de AJAX, debemos disponer de un control ScriptManager. Veamos entonces cómo trabajar con estos nuevos controles. ScriptManager El ScriptManager ( de Scripts) es un control Web no visual indispensable para utilizar el resto de los controles. Su tarea principal es proveer de la inclusión en el HTML de los archivos JavaScript externos necesarios para el funcionamiento del framework de AJAX. Sólo puede haber un control ScriptManager por formulario Web (y por HTML resultante) y debe estar obligatoriamente definido antes que cualquier otro control que lo requiera. Por eso, generalmente acostumbramos a colocarlo al inicio del formulario Web, donde queremos hacer uso de
Alfaomega
Visual Studio - Firtman, Natale
ASP.NET AJAX
309
técnicas AJAX. Podremos arrastrar el control desde el Cuadro de Herramientas en la categoría Extensiones AJAX. Desde Visual Studio 2008, dado que forma parte de la plataforma, se utiliza la etiqueta asp:ScriptManager. En versiones anteriores, es común encontrar el uso de otro prefijo, como ajax:ScriptManager. No obstante, se trata del mismo control con algunas mejoras y soluciones de problemas. En la mayoría de los casos utilizaremos el control en su condición mínima:
Sobre este control podremos definir la propiedad AsynostBackTimeout, un valor entero en milisegundos, que será el tiempo máximo en el que el framework de cliente esperará a una respuesta del servidor ante una petición AJAX. Pasado este tiempo, se generará un error y se cancelará la petición. Por defecto, el ScriptManager se quedará esperando hasta que el servidor devuelva la respuesta o defina un error. La propiedad ScriptPath nos permite definir una ruta virtual para la que se generarán los scripts hacia el navegador para aprovechar los mecanismos de caché. Así, aún cuando utilicemos muchas carpetas distintas en nuestra aplicación, el navegador utilizará la misma versión del framework JavaScript, sin repetir la descarga. El control también nos permite definir cuáles archivos JavaScript queremos incluir y el framework se encarga de hacer la inclusión, asegurándose de que nuestros archivos JavaScript se cargarán luego de que la librería Microsoft AJAX Library esté cargada. Para ello, la colección Scripts nos permite definir nuestra colección:
<Scripts>
Por defecto, todos los archivos JavaScript (los propios del framework y los nuestros) se emiten en modo automático. Esto quiere decir que pueden emitirse en versiones de depuración (debug) como final (release), según la propiedad debug en el :HEFRQ¿J. Si queremos forzar a un cambio, podemos utilizar la propiedad ScriptMode disponible tanto en el control ScriptManager, como en ScriptReference. Esta propiedad acepta los valores Auto, Release o Debug. En el caso de los ScriptReference, al definir una versión de debug, el sistema buscará un archivo que posea .debug.js como sufijo del nombre –en nuestro ejemplo, controlador.debug.js–. La diferencia radica en los comentarios, mensajes de error y propiedades de depuración que podemos utilizar.
Visual Studio - Firtman, Natale
Alfaomega
310
7- AJAX y MVC
También es posible incluir, a través de la colección de Scripts, archivos JavaScript embebidos en un compilado DLL, utilizando las propiedades Name y Assembly en lugar de Path, en el control ScriptReference. Estos recursos pueden estar localizados y así manipular textos JavaScript en distintos idiomas de una forma sencilla. Y para simplificar el desarrollo para múltiples países, ScriptManager acepta la propiedad lógica EnableScriptGlobalization que, al definirlo en true, nos permitirá localizar fechas y números en distintos idiomas desde JavaScript. Control de errores Otro tema importante que se incorpora cuando utilizamos técnicas AJAX es el control de errores. Pueden ocurrir diversos errores durante una petición asincrónica que se haga por AJAX: Puede ser que el servidor no responda, que se le haya cortado la conexión a Internet al , que el script en el servidor haya generado una excepción, etc. En una situación sin AJAX, el error se vería directamente en el navegador, pero en una situación asíncrona, es desde JavaScript donde necesitamos capturar este evento. Para ello, podremos capturar el evento JavaScript endRequest del objeto P a g e R e q u e s t M a n a g e r o s i m p l e m e n t e d e f i n i r l a p r o p i e d a d AsynostBackErrorMessage del control de servidor ScriptManager. Si usamos esta última opción, el recibirá ese mensaje en una alerta de JavaScript en el navegador. También podremos capturar el evento de servidor OnAsynostBackError del ScriptManager y decidir qué hacer o qué mensaje entregarle al cliente. Como última opción al control de errores asíncronos, podremos definir la propiedad AllowCustomErrorsRedirect en true, en el control ScriptManager y, de esta manera, cuando ocurra un error de servidor el perderá el dibujado parcial AJAX y se redirigirá a la clásica pantalla de excepciones que vemos sin AJAX. Decíamos que por defecto el mensaje de error (genérico o personalizado) se muestra en una clásica ventana de alerta de JavaScript. Para modificar ese comportamiento, podemos capturar el error en JavaScript y mostrar el mensaje de otra manera (en un DIV, por ejemplo). Para capturar el error el código sería el siguiente: <script type="text/javascript" language="javascript"> Sys.WebForms.PageRequestManager.getInstance().add_ endRequest(EndRequestHandler); function EndRequestHandler(sender, args) { LIDUJVJHWBHUURU XQGH¿QHG
Alfaomega
Visual Studio - Firtman, Natale
ASP.NET AJAX
311
{ // Hubo un error en la petición asincrónica var mensaje; if (args.get_response().get_statusCode() == '200') { mensaje = args.get_error().message; } else { // Ocurrió otro tipo de error PHQVDMH $QXQVSHFL¿HGHUURURFFXUUHG } // Le decimos al framework que nos encargamos // nosotros de manejar el error args.set_errorHandled(true); // TODO: Tomar acción con el error el mensaje } }
ScriptManagerProxy ¿Qué sucede cuando trabajamos con Master Pages (Páginas Principales) y queremos hacer uso de AJAX, tanto en dicha plantilla como en cada página de contenido? Si colocamos un ScriptManager en la Página Maestra o Principal y un ScriptManager en el Formulario Web ASPX, recibiremos un error porque no puede haber dos es coexistiendo. Para ello, existe el control ScriptManagerProxy. El objetivo es colocar un ScriptManager en la página maestra y un ScriptManagerProxy en cada página de contenido donde queramos utilizar AJAX o definir alguna propiedad adicional. Este control posee las mismas propiedades que el anteriormente visto, pero se aplicará solo al .aspx en cuestión y no a todos los que utilicen la página maestra. Esta misma condición se aplica cuando programamos controles personalizados que usen este framework. Utilizaremos un ScriptManagerProxy para realizar un puente hacia el único ScriptManager que existirá en el formulario Web o página maestra. Su uso es idéntico al anterior, por lo que alcanzará con definirlo de la siguiente manera:
Visual Studio - Firtman, Natale
Alfaomega
312
7- AJAX y MVC
Métodos de Página Uno de los más útiles agregados que tenemos disponible en ASP.NET AJAX es la posibilidad de poder ejecutar, desde JavaScript, cualquier método de nuestra página Web y recibir su resultado de forma asíncrona y sin depender de ningún control Web. Esta técnica se conoce como Page Methods o métodos de página. Para poder utilizarlo, sólo debemos crear un método estático (static en C# o Shared en VB) en nuestra clase parcial del formulario Web y agregarle el atributo WebMethod, como si se tratara de un servicio Web. Hecho esto, desde JavaScript estará disponible un objeto llamado PageMethods, que poseerá funciones JavaScript ya disponibles para cada uno de los métodos de página definidos. Esta función JavaScript recibirá la misma cantidad y tipo de parámetros del método .NET y tres parámetros opcionales más: Una función de callback, para cuando la operación se realizó correctamente, otra función para cuando generó error y un parámetro opcional, llamado contexto, que se enviará tal cual a la función de callback. .NET se encargará de convertir los parámetros y devolución desde y hacia JavaScript y .NET e, incluso, podremos generar excepciones en el servidor y recibirlas de esta manera en el cliente. Para poder hacer uso de los métodos de página, también es necesario explicitar el atributo EnablePageMethods en true del ScriptManager. Veamos un ejemplo para entender mejor el concepto: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="_Default" %>
Suma con PageMethods 5c3o2b <script type="text/javascript"> function sumar() { var a = $get("a").value; var b = $get("b").value; PageMethods.Sumar(a, b, sumaOK); } function sumaOK(valor) { alert("La suma es " + valor); }
Alfaomega
Visual Studio - Firtman, Natale
ASP.NET AJAX
313
Using System.Web.Services public partial class _Default : System.Web.UI.Page { [WebMethod] public static int Sumar(int a, int b) { return a + b; } }
Para el caso que fuera Visual Basic, el código Code Behind sería el siguiente:
Imports System.Web.Services Public Partial Class _Default Inherits System.Web.UI.Page <WebMethod()> _ Public Shared Function Sumar(ByVal a As Integer, ByVal b As_ Integer) As Integer Return a + b End Function End Class
El ejemplo toma dos valores y los suma, llamando vía AJAX a un método de página llamado Sumar. El framework se encarga de la petición asincrónica para lograrlo.
Visual Studio - Firtman, Natale
Alfaomega
314
7- AJAX y MVC
Fig. 7-3. Aquí vemos nuestra petición AJAX realizada, vista en Internet Explorer 8.
Si quisiéramos capturar los errores, cambiamos el código JavaScript por el siguiente: function sumar() { var a = $get("a").value; var b = $get("b").value; PageMethods.Sumar(a, b, sumaOK, sumaError); } function sumaOK(valor) { alert("La suma es " + valor); } function sumaError(result) { alert("No se pudo ejecutar la suma: " + result.get_message()); }
Alfaomega
Visual Studio - Firtman, Natale
ASP.NET AJAX
315
Fig. 7-4. Aquí vemos el mensaje de error de la excepción generada en el servidor, capturado por JavaScript.
Servicios Web Los métodos de página, en realidad, son una forma particular de la llamada asincrónica a Servicios Web (Web Services), que nos provee el framework de ASP.NET AJAX. Esta metodología nos permite invocar a cualquier servicio Web definido en nuestro servidor en formato asmx desde código JavaScript, de forma sencilla. Para poder hacer uso de esta función, necesitamos que nuestra clase servicio Web posea el atributo ScriptService. Esto permitirá que el framework genere las clases proxy en JavaScript, para acceder a todos los métodos públicos de dicho servicio Web. Además, debemos agregar nuestro servicio Web con extensión ASMX en la colección Services del control ScriptManager. Esto se puede hacer manualmente en el código ASP.NET o utilizando el asistente provisto por Visual Studio para tal fin. Veamos un ejemplo de código: Visual Studio - Firtman, Natale
Alfaomega
316
7- AJAX y MVC
<Services>
Fig. 7-5. Aquí vemos el asistente de Visual Studio para agregar servicios Web al ScriptManager.
Además, nuestro servicio Web deberá verse como el siguiente. Notemos el atributo ScriptService: [WebService(Namespace = "http://itmaster.es/")] >:HE6HUYLFH%LQGLQJ&RQIRUPV7R :VL3UR¿OHV%DVLF3UR¿OHB @ [System.Web.Script.ScriptService] public class WebService : System.Web.Services.WebService { … [WebMethod] public string HelloWorld() { return "Hola a todos"; } }
Alfaomega
Visual Studio - Firtman, Natale
ASP.NET AJAX
317
Para el caso de VB, el código quedaría: <System.Web.Script.Services.ScriptService()> _ <WebService(Namespace:="http://tempuri.org/")> _ :HE6HUYLFH%LQGLQJ&RQIRUPV7R :VL3UR¿OHV%DVLF3UR¿OHB !B
_ Public Class WebService Inherits System.Web.Services.WebService <WebMethod()> _ Public Function HelloWorld() As String Return "Hola a todos" End Function End Class
Al hacer esto, desde JavaScript tendremos una clase ya creada con el mismo nombre del servicio Web, que hará de puente para invocar cada método del servicio Web y convertir la respuesta hacia JavaScript nuevamente. Esta función de JavaScript poseerá, primero, la misma cantidad de parámetros que el método en el servicio Web y, luego, se le agregarán tres parámetros no obligatorios de tipo función o callback: La función que queremos que se ejecute cuando la respuesta del servicio Web llegue, una función para capturar errores y una función para capturar un timeout o fin de espera de respuesta del servidor. Por defecto, la clase que crea se incluye como un JavaScript aparte. Podemos evitar esto con la propiedad InlineScript en true, dentro de la definición de cada servicio Web. Veamos un ejemplo completo de un conversor de grados Celsius a Fahrenheit, utilizando servicios Web y ASP.NET AJAX: Conversor.aspx <%@ Page Language="C#" AutoEventWireup="true" CodeFile="Conversor.aspx.cs" Inherits="_Default" %>
Conversor de Temperaturas 733t3q <script type="text/javascript"> function convertir() {
Visual Studio - Firtman, Natale
Alfaomega
318
7- AJAX y MVC
var c = $get("c").value; var f = $get("f").value; if ((c=="") && (f=="")) { DOHUW'HEHHVSHFL¿FDUDOPHQRVXQDWHPSHUDWXUD } else if ((c!="") && (f!="")) { DOHUW'HEHHVSHFL¿FDUVyORXQDWHPSHUDWXUD } else if (c!="") { Conversor.CelsiusToFahrenheit(c, fahOK, error); } else { Conversor.FahrenheitToCelsius(f, cenOK, error); } } function fahOK(valor) { $get("f").value = valor; } function cenOK(valor) { $get("c").value = valor; } function error(result) { alert("No se pudo ejecutar la conversión: " + result.get_message()); }
/// <summary> /// Convierte de grados Fahrenheit a Celsius y Viceversa /// [WebService(Namespace = "http://conversor.itmaster.es/")] >:HE6HUYLFH%LQGLQJ&RQIRUPV7R :VL3UR¿OHV%DVLF3UR¿OHB @ [System.Web.Script.Services.ScriptService] public class Conversor : System.Web.Services.WebService {
JSON Además del uso de los servicios Web antes mencionados junto al framework de ASP.NET AJAX, con ASP.NET 3.5 también es posible generar servicios Web utilizando WCF (Windows Communication Foundation) que den su respuesta en formato JSON (JavaScript Object Notation). Este formato es muy utilizado en la actualidad en servicios y APIs de proyectos Web 2.0, en reemplazo de XML o SOAP. El uso de esta modalidad escapa al alcance de este libro, pero basta con mencionar que podemos crear un servicio con extensión svc y agregarlo a la colección Services como en el ejemplo anterior. Visual Studio - Firtman, Natale
Alfaomega
320
7- AJAX y MVC
Fig. 7-6. Nuestro conversor de temperaturas AJAX en funcionamiento.
Actualización Parcial Introducción Uno de los platos más fuertes de ASP.NET AJAX es, sin duda, la actualización parcial (partial updates). Esta técnica implica definir una zona de nuestro formulario Web donde todos los postback al servidor se hagan, automática y mágicamente, por peticiones asincrónicas AJAX y el resultado se actualice en la zona definida, sin que el perciba cambios en el historial del navegador o haya un refresco total de la página. Esta posibilidad está automáticamente habilitada en el ScriptManager y, si no vamos a hacer uso de la técnica, conviene desactivar la opción para reducir el código generado. Esta opción está disponible en EnablePartialRendering y, por defecto, está definida en true. El control Web que permite estas actualizaciones parciales es el de actualización (Update). Update El control Update equivale visualmente a un , agrupa una serie de controles y HTML en una zona definida mediante un nombre. La gran diferencia radica en que, por defecto, todos los controles que generaban un PostBack normalmente (esto es, refrescar toda la página vía POST y ejecutar el ciclo de vida de la página en el servidor), ahora lo realizarán mediante la técnica provista por AJAX. Así de simple: Sin escribir nada de código JavaScript, ni modificar nada de nuestros controles Web ni del código C# o VB que manipula los eventos de la página. Alfaomega
Visual Studio - Firtman, Natale
Actualización Parcial
321
Con poseer un ScriptManager y colocar un Update con un contenido dentro, tendremos una funcionalidad AJAX sobre el contenido. El framework se encargará de ejecutar todo el ciclo de vida, como si de un PostBack normal se tratara, notificar de los cambios en el HTML en la zona contenida en el Update y actualizarnos en el navegador. Ahora bien, el contenido del Update debe ser insertado en la plantilla ContentTemplate. Si utilizamos la técnica de arrastrar y soltar en la vista Diseño de Visual Studio, sólo copiaremos el contenido dentro del Update y el ContentTemplate se generará solo. Veamos un calendario que genera distintos eventos de tipo PostBack, inserto en un Update, funcionando con AJAX:
Si probamos el ejercicio anterior, veremos un calendario común y corriente, pero que al cambiar de mes o seleccionar una fecha no veremos el clásico refresco de la página, sino que se actualizará solo. De esta manera, si abrimos cualquier formulario Web ASP.NET y encerramos todos los controles en un Update, tendremos una funcionalidad AJAX de todo el sitio. Esto funciona pero también es peligroso. Hay que tener en cuenta que el framework de ASP.NET transmite todo el contenido del Update, desde el servidor hacia el navegador, y no sólo aquella porción que se haya modificado. Por eso, si encerramos todo en un de actualización, se transferirá igual toda la página, sólo que sin refrescar el navegador. De esta manera, es importante pensar exactamente cuál o cuáles deberían ser las zonas de actualización parcial, para reducir el envío de código de más y mejorar la velocidad de respuesta. Por defecto, todo aquel control que esté fuera de un Update seguirá funcionando con los clásicos PostBacks y refrescará la página entera, incluso las zonas de actualización. El Update configura su propiedad RenderMode en Block, generando de esta manera una etiqueta
en HTML. Podremos cambiar esta propiedad por el valor Inline y convertirlo así en una etiqueta <span>, pudiendo de esta manera actualizarse sólo una porción de un párrafo o zona de contenido. Como un podremos colocarlo visible o invisible a través de la propiedad Visible.
Visual Studio - Firtman, Natale
Alfaomega
322
7- AJAX y MVC
Fig. 7-7. Con Firefox y Firebug podemos ver las peticiones AJAX que se realizan entre el navegador y el servidor, lo mismo que con IE8 y las herramientas para desarrolladores.
Algo interesante que podemos definir son los desencadenadores o triggers. Por defecto, todos los controles hijos del Update generan postbacks asincrónicos, o sea, por AJAX. Esto se puede desactivar utilizando la propiedad ChildrenAsTriggers, definiéndola en false. Hecho esto, ahora podremos especificar manualmente qué controles, y qué eventos de cada uno, queremos que se realicen de manera asincrónica. Para ello, podremos definir la colección Triggers del Update, creando explícitamente objetos PostBackTrigger (para postbacks normales) o AsynostBackTrigger (para postbacks por AJAX). Ambos definen la propiedad ControlID como el identificador del control que queremos definir y para los asincrónicos podemos especificar exactamente qué evento queremos hacer por AJAX, a través de la propiedad EventName. Algo importante para tener en cuenta, y que simplifica el desarrollo AJAX, es la posibilidad de trabajar con desencadenadores externos. Los controles que definamos en los desencadenadores pueden estar tanto dentro como fuera del Update. De esta forma, podemos definir un Update como zona de actualización parcial, pero que el desencadenador de dicha actualización AJAX Alfaomega
Visual Studio - Firtman, Natale
Actualización Parcial
323
sea un control (como un botón) fuera de dicha zona. Así se puede combinar la creación de postbacks asincrónicos y normales para adaptarse a todas nuestras necesidades.
Fig. 7-8. Creando triggers desde el diseñador de Visual Studio.
es múltiples Una página Web puede contener la cantidad de es de actualización que queramos, cada uno encerrando una zona de actualización distinta dentro del mismo formulario Web. Así, las zonas de la página que nunca se van a modificar según un evento quedan fuera de los procesos de actualización y reducimos ampliamente el tráfico entre el servidor y el navegador. Si revisamos las peticiones AJAX emitidas cuando hay más de un de actualización, y generamos un postback en una de esas zonas, veremos que en la respuesta del servidor también se recibe el contenido actualizado (o igual al anterior) de las otras zonas de actualización. Veamos un ejemplo sencillo: <%@ Page Language="C#" AutoEventWireup="true"
Visual Studio - Firtman, Natale
Alfaomega
324
7- AJAX y MVC
CodeFile="MultipleUpdate.aspx.cs" Inherits="MultipleUpdate" %>
Página sin título 4c3270
Esto se debe a que, por defecto, el control Update viene con la propiedad UpdateMode en Always. Esto implica que siempre que se genera un postback asincrónico vía AJAX, el de actualización se refrescará, haya cambiado o no, haya sido el causante del postback o no. Por ello, salvo algún caso particular, siempre se debe cambiar la propiedad UpdateMode al valor Conditional. Al pasarlo a modo condicional, el Update sólo será refrescado si el postback fue generado por un trigger o desencadenador de su propiedad o si nosotros invocamos explícitamente una actualización a través del método Update() del Update en cualquier evento de servidor.
Alfaomega
Visual Studio - Firtman, Natale
Actualización Parcial
325
Fig. 7-9. Aquí vemos cómo la respuesta del servidor, luego de un postback asincrónico, incluyó el contenido de ambos Update.
Esto nos permite manipular exactamente qué contenido actualizar y cuándo. Con este nuevo modo en mente, podemos definir una zona de actualización que no tenga ningún trigger –por ejemplo, simplemente un Label– y, desde cualquier desencadenador de cualquier otra zona, definir en un momento dado qué queremos cambiar (el contenido o las propiedades de dicha zona). Veamos un ejemplo: <%@ Page Language="C#" AutoEventWireup="true" CodeFile="MultipleUpdate2.aspx.cs" Inherits="MultipleUpdate" %>
Página sin título 4c3270
public partial class MultipleUpdate : System.Web.UI.Page { protected void Button1_Click(object sender, EventArgs e) { texto.Text = "Actualizando otro Update"; Update2.Update(); } }
Aviso de Progreso Mientras una petición asincrónica se está realizando para un refresco parcial de la página, el no recibe ningún aviso de que debe esperar una respuesta del servidor. Para poder darle un aviso al , existe el control UpdateProgress (progreso de actualización). Este control visual es un invisible que, habiendo pasado medio segundo del inicio de la petición asincrónica y no habiendo recibido todavía respuesta del servidor, se convierte en visible. Dentro de este control podemos colocar un mensaje de Cargando... o una imagen animada indicando al que espere. Al colocar un UpdateProgress, se da el aviso para todas las peticiones asincrónicas que se realicen en la página. Podemos especificar que dicho aviso sea sólo para una zona de actualización a través de la propiedad AssociatedUpdateID. De esta manera, podemos tener un aviso por un o varios que no posean aviso y que hagan las peticiones sin que el las note. Los avisos de progreso pueden estar ubicados en cualquier lugar de la página Web y es allí donde se mostrarán, a no ser que utilicemos CSS para ubicarlo en otro lugar. Alfaomega
Visual Studio - Firtman, Natale
Actualización Parcial
327
A través de la propiedad DisplayAfter se podrá definir luego de cuántos milisegundos colocar el aviso. No se recomienda poner un valor más bajo de 500 milisegundos (medio segundo) porque, en peticiones que respondan muy rápido, el casi no verá el aviso y notará una imagen que aparece y se va, sin comprender el significado. En los siguientes dos ejemplos veremos cómo ubicar un aviso de progreso al estilo aplicaciones de Google, con CSS, y otro ejemplo con una imágen clásica de carga:
Cargando...
Fig. 7-10. Aquí vemos un aviso de progreso en funcionamiento.
Cronómetro El último control incluido en las extensiones AJAX para ASP.NET es el cronómetro o Timer. Este control en sí mismo se puede utilizar sin AJAX, pero su utilidad radica en usarlo dentro de un Update o definir su evento Tick como desencadenador de un Update. Este control lo que hace es ejecutar un postback cada n milisegundos. El evento que genera se llama Tick y si lo utilizamos como un postback asincrónico, podremos actualizar alguna zona de actuaVisual Studio - Firtman, Natale
Alfaomega
328
7- AJAX y MVC
lización con novedades periódicas, sin que el realice una acción activa de actualización. Veamos un ejemplo, para entender la idea en VB, que actualiza un reloj cada un minuto. Hay que tener cuidado con este tipo de actividades, que pueden llegar a saturar el servidor si nos abusamos como en ejemplos como éste, que podrían reemplazarse por un simple JavaScript: <%@ Page Language="VB" %> <script runat="server"> Protected Sub Timer1_Tick(ByVal sender As Object, ByVal e As System.EventArgs) lblHora.Text = DateTime.Now.ToString("t") End Sub
Página sin título 4c3270
Alfaomega
Visual Studio - Firtman, Natale
Actualización Parcial
329
AJAX Control Toolkit ¿Qué es? Junto con el framework de ASP.NET AJAX, Microsoft trabajó con la comunidad en un proyecto de código abierto conocido como Control Toolkit. Este set de controles es gratuito y se descarga desde www.asp.net/ajax/ajaxcontroltoolkit. El set está compuesto de algunos controles ricos visuales, que no tienen existencia en el framework de ASP.NET, y algunos extensores. Los extensores son controles que no actúan por sí mismos, sino que modifican el comportamiento y aspecto visual de otros controles clásicos del framework de ASP.NET, dando en este caso comportamientos visuales ricos (con JavaScript y CSS) y comportamientos asincrónicos a través del framework de AJAX. Los controles se van actualizando y se van agregando nuevos periódicamente, por lo que es recomendable ingresar al sitio Web y consultar nuevas versiones que resuelven errores y agregan funcionalidades. En el sitio Web encontraremos una demostración de cada uno de los controles con una explicación y ejemplos de uso. También existen instrucciones y ejemplos para crear nuestro propio control rico, utilizando el framework o nuestro propio extensor. Y por eso existen en Internet algunos otros controles de código abierto disponibles para utilizar, que no forman parte de este kit. Requisitos Para poder utilizarlo, podemos descargar una versión que sea compatible con el Framework 2.0 o con el Framework 3.5, según el que queramos utilizar. En las descargas encontraremos cuatro versiones, siendo XX el número de framework para el cual es compatible: AjaxControlToolkit-FrameworkXX.zip: Incluye ejemplos, documentación, instalador para Visual Studio y Visual Web Developer y código fuente de todos los controles. Dentro del archivo comprimido encontraremos un archivo con extension vsi (Visual Studio Installer) y ese es el que debemos utilizar para instalar los controles en Visual Studio. AjaxControlToolkit-FrameworkXX-NoSource.zip: Igual al anterior, evitando la descarga del código fuente de los controles. Probablemente, sea el paquete más adecuado para descargar. AjaxControlToolkit-FrameworkXX-DllOnly.zip: Sólo incluye el compilado DLL de todos los controles. Manualmente podremos colocar el DLL en la carpeta Bin de nuestro proyecto o sitio Web y agregar la referencia manualmente en cada página.
Visual Studio - Firtman, Natale
Alfaomega
330
7- AJAX y MVC
AjaxControlToolkit-ScriptFilesOnly.zip: Sólo incluye el código JavaScript para el funcionamiento de cada control.
Fig. 7-11. Así se ve la página de demostración de los controles ricos del Control Toolkit.
Fig. 7-12. Aquí vemos el cuadro de herramientas con los nuevos controles del Toolkit. Alfaomega
Visual Studio - Firtman, Natale
Actualización Parcial
331
Controles Entre los controles nuevos disponibles en el Control Toolkit, al momento de escribir este libro, se encuentran: s
Accordion: Permite crear es que se abren y retraen, simulando un efecto de acordeón. Debemos utilizar AccordionPane como cada uno de los es que se abren y cierran.
s
NoBot: Permite reducir el spam y el uso de robots para completar formularios.
s
Rating: Permite al darle un puntaje a algún elemento con un diseño clásico de sitios Web 2.0, capturando el evento vía AJAX en el servidor del voto del .
s
ReorderList: Permite al cambiar el orden de los elementos de una lista, utilizando arrastrar y soltar. Posee distintas plantillas para trabajar.
s
TabContainer: Permite crear un diseño en pestañas mostrando de a un (TabContainer) a la vez. Desde Visual Studio podremos trabajar sin problemas en vista diseño con este nuevo control.
Fig- 7.13. Aquí vemos una combinación de diseño con pestaña de efecto acordeón, utilizando Control Toolkit.
Extensores Un extensor (extender en inglés) es una clase de .NET que a simple vista se define como cualquier control, con la diferencia de que no actúa por sí mismo, sino que extiende la funcionalidad de otro control del framework de ASP.NET, como ser una caja de texto.
Visual Studio - Firtman, Natale
Alfaomega
332
7- AJAX y MVC
Fig. 7-14. Aquí vemos el asistente de extensores para un TextBox, que nos permite agregar fácilmente una funcionalidad extendida del Control Toolkit.
Fig. 7-15. Cuando un control tiene uno o más extensores creados, encontraremos sus atributos en la ventana de Propiedades, en la sección Extenders.
Alfaomega
Visual Studio - Firtman, Natale
Actualización Parcial
333
Todos los extensores tienen la propiedad TargetControlID, que especifica el identificador del control destino de la extensión que se ha de aplicar. Los extensores disponibles son: s
AlwaysVisibleControl: Permite dejar visible todo el tiempo a un control (un , por ejemplo), aunque el desplace el cursor vertical u horizontalmente. Ideal para menús de navegación.
s
AutoComplete: Permite generar un comportamiento de tipo autocompletar en una caja de texto. Consulta un servicio Web por las sugerencias para mostrarle al según lo que va escribiendo.
s
Calendar: Permite generar un calendario emergente sobre una caja de texto, ya sea al hacer clic en la caja de texto o en un ícono a su derecha.
s
CascadingDropDown: Permite extender a un DropDownList con una funcionalidad en cascada para elegir datos en jerarquía, por ejemplo, elección de País, luego Provincia, luego Ciudad.
s
Collapsible: Permite contraer y expandir el contenido de un dinámicamente.
s
&RQ¿UP%XWWRQ: Extiende a un botón para que el confirme la operación mediante una ventana modal antes de hacer el postback.
s
Drag: Extiende a un otorgándole la habilidad de ser arrastrado por toda la página o por zonas definidas.
s
DropDown: Permite generar una lista desplegable con el contenido de un control.
s
DropShadow: Ofrece un diseño de sombreado a cualquier control.
s
Dynamiopulate: Reemplaza el contenido de un control por el resultado de una llamada a un servicio Web o método de página.
s
FilteredTextBox: Filtra los caracteres que se pueden insertar en una caja de texto, impidiendo el ingreso de caracteres inválidos.
s
HoverMenu: Permite adosar a cualquier control un menú desplegable, que aparece cuando el posa el cursor sobre el control.
s
ListSearch: Permite extender un ListBox o DropDownList, pudiendo el ingresar una búsqueda con el teclado dentro de la lista de opciones.
s
MaskedEdit: Extiende una caja de texto, pudiendo decidir el formato o máscara de entrada de los datos, por ejemplo, DD/MM/AAAA o $ 9999,99, obligando al a seguir ese formato.
s
ModalPopUp: Muestra un contenido de forma modal, bloqueando el al resto de los controles hasta que se cierre o cancele la ventana modal.
Visual Studio - Firtman, Natale
Alfaomega
334
7- AJAX y MVC
s
MultiHandleSlider: Es un selector de valores numéricos múltiplos. Ideal para rangos numéricos, por ejemplo, selección de rango de precio, donde el arrastra los controles hasta definir el rango deseado.
s
MutuallyExclusiveCheckBox: Permite definir cajas de selección exclusivas. Así, si seleccionamos un CheckBox otro obligatoriamente se deselecciona.
s
NumericUpDown: Agrega a un campo de texto la posibilidad de que el seleccione un valor numérico con botones.
s
PagingBulletedList: Permite que una lista con viñetas se divida en páginas automáticamente, por ejemplo, por orden alfabético
s
Strenght: Permite adosarse a un campo de texto utilizado como ingreso de contraseñas y que el vea la seguridad de la contraseña que está ingresando.
s
PopUpControl: Permite colocar cualquier control como popup al hacer clic sobre otro.
s
ResizableControl: Permite al cambiar las dimensiones de un control dinámicamente.
s
RoundedCorners: Permite dar bordes redondeados a un control.
s
Sliders: Permite al seleccionar un valor numérico arrastrando una imagen sobre un eje fijo horizontal o vertical.
s
SlideShow: Permite crear una galería de imágenes a partir de un control Image y botones para moverse opcionalmente. La galería puede funcionar de forma automática.
s
TextBoxWatermark: Deja una marca de agua en un campo de texto sin completar.
s
ToggleButton: Extiende un CheckBox, pudiendo cambiar su visualización por la de íconos de encendido y apagado.
s
UpdateAnimation: Permite asignar animaciones visuales antes de hacer una petición y después de que la respuesta del servidor llegue en una petición asincrónica de un Update.
s
ValidatorCallout: Extiende a los validadores existentes, otorgándoles un diseño de globo de diálogo sobre el campo que posee el error.
Una vez instalado el paquete para Visual Studio, todos los controles Web que poseen extensores disponibles aparecen automáticamente en el ícono de tareas rápidas como un directo: Agregar Extensor y otros s para eliminar, Quitar Extensor y tareas rápidas para los extensores ya definidos. Otra forma de utilizar un extensor es arrastrarlo desde el Cuadro de Herramientas y soltarlo sobre el control al cual queremos aplicarlo. Alfaomega
Visual Studio - Firtman, Natale
Actualización Parcial
335
Fig. 7-16. Distintos extensores a campos de texto.
Fig. 7-17. El control ModalPopUp permite crear ventanas modales atractivas. Visual Studio - Firtman, Natale
Alfaomega
336
7- AJAX y MVC
Fig. 7-18. ValidatorCallout permite crear validadores mucho más vistosos y con opciones de sugerencias al .
Framework de Animación A través del extensor AnimationExtender es posible crear animaciones simples y combinatorias, que se pueden ejecutar automáticamente ante un evento (OnClick, OnLoad, OnMouseOver, OnMouseOut, OnHoverOver, OnHoverOut) en el navegador. El framework de animación requiere la generación de una secuencia de animación en XML ante un evento en particular. Partícipes de la animación pueden ser efectos de aparición, desaparición, manejo de transparencia, movimiento, cambios de tamaño, cambios de color, así como también ejecución de scripts, modificación de propiedades y otras operaciones, que se pueden combinar en secuencia (una detrás de la otra) o en paralelo (en simultáneo). Cada efecto tiene propiedades comunes, como ser Duration, expresada en segundos con decimal, Fps, indicando cuántos cuadros por segundo queremos que el efecto tenga, así como propiedades particulares de cada tipo de efecto. Consultando la guía online de Animator encontraremos todos los detalles. Alfaomega
Visual Studio - Firtman, Natale
Actualización Parcial
337
jQuery ¿Qué es? jQuery es una librería de AJAX de cliente, liviana, rápida y sencilla de codificar. Su sitio Web oficial es jquery.com y ofrece, en 19Kb (comprimido), toda su funcionalidad compatible con todos los navegadores conocidos del mercado. Entre las funciones que incluye podemos mencionar: s
Soporte del evento ready, que se ejecuta cuando el DOM se encuentra completamente cargando sin las imágenes.
s
Manejo de eventos sencillo a través de funciones.
s
Efectos visuales con poco código.
s
Soporte de cadena de ejecución con la posibilidad de ejecutar múltiples instrucciones en una sola línea.
s
Manipulación de documentos con CSS 1, 2 y 3.
s
Soporte de algunos estándares de Prototype como $ y $$, incorporando expresiones XPath.
s
Soporte de nuevos plug ins.
Algunos ejemplos con jQuery: // Trae todos los radiobuttons seleccion var radio = $("input[@type=radio][@checked]"); // Le agrega la clase CSS texto a todos los párrafos $("p").addClass("texto"); // Pone a todos los links para que se abran en ventana nueva $("a").attr({ target: "_blank", title: "Se abrirá en ventana nueva" }); $JUHJDXQDLPDJHQOXHJRGHFDGD¿QGHSiUUDIR S DIWHULPJVUF ¿QMSJ ! 'H¿QHODIXQFLyQDHMHFXWDUVHHQHOFOLFGHOERWyQ $("btnEnviar").click(function() { alert("Gracias por enviar los datos") }); // Anima un div $("divContenido").fadeIn("slow"); // Hace una petición AJAX
Visual Studio - Firtman, Natale
Alfaomega
338
7- AJAX y MVC
$.ajax({ type: "POST", url: "guardar.php", data: "nombre=Juan&apellido=Gomez", success: function(mensaje){ alert(mensaje); } }); // Hace algo si es Internet Explorer if ($.browser.msie) { } // Itera entre una colección $.each( [2, 4, 6], function(i, total) { alert(i) }); // Busca todos los párrafos que sean de clase "copete", // les agrega la clase "deportes" y los muestra con un efecto lento $("p.copete").addClass("deportes").show("slow");
Relación con ASP.NET Si bien jQuery es una librería de cliente y se puede utilizar con cualquier lenguaje de servidor, Microsoft anunció a fines de 2008 la integración de la librería con su framework de ASP.NET, por lo que en futuras versiones la librería ya estará incluida en el framework. Ella contendrá soporte de Intellisense en Visual Studio, documentación y ayuda, así como también creará controles ricos basados en jQuery para el framework de ASP.NET. Esto implica que jQuery se está convirtiendo en un estándar de mercado y que es importante conocer las funciones de la librería, ya sea para utilizarla manualmente o para futuras versiones de ASP.NET, donde la librería será parte integral de la plataforma.
Futuro de ASP.NET AJAX El futuro del framework ASP.NET AJAX es cada vez más promisorio y, entre las ventajas que ya se anunciaron o que están disponibles en betas y ejemplos, al editar este libro podemos mencionar que en futuras versiones tendremos un excelente soporte para manejar y capturar el historial del navegador (History), así como también crear extensores no intrusivos, que directamente se configuren en el XHTML y no requieran de un control Web para su utilidad. Esto será muy beneficioso para incorporar funcionalidades AJAX al framework ASP.NET MVC, Alfaomega
Visual Studio - Firtman, Natale
ASP.NET MVC
339
las cuales veremos en breve. De esta manera, podremos crear plantillas que actualicen información y se enlacen automáticamente a un origen de datos desde el XHTML sin escribir ni código de script, ni código de servidor. Por ejemplo, el siguiente código sería el necesario para mostrar una lista de links:
La lista podría enlazarse automáticamente con un servicio Web, utilizando el framework ASP.NET AJAX o con una acción de un controlador de ASP.NET MVC. Este otro ejemplo, sería el equivalente a un campo de texto con dos extensores no intrusivos, un autocompletador y una marca de agua: ... ...
El código anterior es el que quedaría en el navegador y es válido para XHTML, porque se definen los espacios de nombre del XML. El framework será el encargado de convertir esas referencias en el XHTML en comportamientos adecuados.
ASP.NET MVC El framework ASP.NET MVC (Modelo Vista Controlador o Model View Controller) es una alternativa, no un reemplazo, a Formularios Web ASP.NET. Permite crear aplicaciones, utilizando este patrón de diseño de separación de funcionalidades, y ofrece los beneficios de una separación clara (y obligatoria) de las responsabilidades en el código: Extrema facilidad en la posibilidad de realizar pruebas sobre Visual Studio - Firtman, Natale
Alfaomega
340
7- AJAX y MVC
el código, control fino sobre el HTML y JavaScript generado, un sistema de URLs más intuitivo y una metodología simple para crear APIs compatibles con Web 2.0.
El patrón MVC Es un patrón de diseño definido en 1979 (sí, hace más de treinta años), originalmente para aplicaciones SmallTalk. Hoy se usa para todo tipo de aplicaciones, especialmente para aplicaciones Web. Según Wikipedia, el patrón de diseño MVC se define como: “Un patrón de arquitectura de software que separa los datos de una aplicación, la interfaz de y la lógica de control en tres componentes distintos. El patrón MVC se ve frecuentemente en aplicaciones Web, donde la vista es la página HTML y el código que provee de datos dinámicos a la página. El modelo es el Sistema de Gestión de Base de Datos y la Lógica de negocio y el controlador es el responsable de recibir los eventos de entrada desde la vista”. Luego de esta definición, observamos que el modelo de formularios Web de ASP.NET tiene algo de esta idea. Tenemos el Code-Behind, el ASPX y podríamos llegar a tener un modelo de clases. Sin embargo, esto no es obligatorio y no cumple exactamente con la definición y las ventajas que puede tener un modelo MVC. Por ello, Microsoft nos ofrece este framework alternativo, disponible como descarga independiente en ASP.NET 3.5 y como parte de la plataforma desde 4.0. Modelo El modelo es el encargado de mantener el estado de la aplicación en todo momento. En general, cuando hablamos de ASP.NET el modelo estará almacenado en una base de datos y, más específicamente, podría estar almacenado en un modelo LINQ To SQL o Entity Framework, con toda la lógica de negocios que le queramos agregar a cada clase. Entonces, en el modelo estarán todos los objetos de negocio, su lógica de trabajo y su lógica de almacenamiento y recupero de fuentes de persistencia, como una base de datos. El modelo no tiene permitido modificar ni consultar nada de la vista. De esta manera, aquí nos olvidamos de mostrar textos, de HTML, de Response.Write y de capturar cualquier tipo de evento de . Vista La vista representa a todos los componentes que conforman la interfaz visual con la que el interactúa y donde el ve la información y actualización del estado del sistema. En nuestro caso, las vistas estarán conformadas por plantillas en HTML con JavaScript.
Alfaomega
Visual Studio - Firtman, Natale
ASP.NET MVC
341
Controlador El controlador es el encargado de recibir notificaciones de actualización y eventos desde la vista y, si fuera necesario, avisar y notificar al modelo de un cambio. El modelo podrá o no modificar su estado, según estas acciones avisadas por el controlador y será el controlador el que decidirá si corresponde o no notificar al con alguna vista. El controlador entonces es el que une a la vista con el modelo, decidiendo qué hay que hacer, cómo hay que hacerlo (es el modelo el que sabe) y cómo hay que mostrarlo (es la vista la responsable).
Framework ASP.NET MVC es un framework alternativo que puede ser descargado gratuitamente desde el sitio Web www.asp.net/mvc. Podríamos escribir todo un libro completo sólo dedicado a este nuevo framework. Sin embargo, en este libro, daremos las bases del nuevo marco de trabajo y dejamos libertad al lector para su profundización. Ruteo dinámico Cuando creamos un proyecto de tipo MVC en Visual Studio, tendremos un modelo de carpetas totalmente distinto al conocido y automáticamente estarán configuradas opciones de ruteo dinámico, donde ya no estaremos ingresando a archivos ASPX, sino que estaremos invocando acciones de un controlador determinado. Esta acción podría recibir parámetros y, según ellos, el controlador determinar qué acción devolver. Las nuevas direcciones, o URLs de nuestro proyecto, tendrán el siguiente formato, comenzando en la raíz de la aplicación: /Controlador/Controlador/Accion /Controlador/Accion?parametros/Controlador/Accion/ParametroID
Por ejemplo: /Clientes /Clientes/Insertar /Clientes/Listar?formato=json/Clientes/Editar/125
Estas URLs, que son inexistentes físicamente, invocarían a un controlador llamado Clientes, que veremos que se tratará de una clase llamada ClientesController. Este controlador tendría una acción por defecto (en el caso 1), una acción llamada Insertar y una acción llamada Editar, que recibe un parámetro numérico. Visual Studio - Firtman, Natale
Alfaomega
342
7- AJAX y MVC
¿Qué puede hacer un controlador? Un controlador, en cada una de sus acciones, puede decidir: s
Entregar una vista. Para el caso se tomará una Vista a elección y se le podrán completar parámetros en la plantilla HTML.
s
No generar resultado.
s
Redirigir al a una nueva URL.
s
Devolver un resultado JSON para ser utilizado por una aplicación AJAX u otra aplicación.
s
Devolver un script JavaScript.
s
Devolver un texto.
s
Devolver un archivo.
Incluso, la misma acción según un parámetro podría dar resultados distintos. Por ejemplo, los siguientes ejemplos podrían darnos un listado de clientes en una página Web, un listado de clientes en JSON para una aplicación AJAX que lo requiera o un listado de clientes convertido a Excel. /Clientes/Listar /Clientes/Listar/JSON /Clientes/Listar/Excel
El controlador no sabrá cómo generar la lista de clientes, sólo le dirá al modelo que lo genere y la lista de Clientes se la pasará como parámetro a la vista que sabrá cómo dibujarlo, o la convertirá a JSON o a un archivo de Excel.
Creando un sitio MVC Introducción Para crear un sitio utilizando ASP.NET MVC, debemos estar trabajando con ASP. NET 3.5 o superior y descargar el agregado ASPNETMVCx.msi desde asp.net/ mvc, donde X es la versión del framework. Este instalador de Visual Studio 2008, VWD 2008 Express o superior, instalará las plantillas de generación de proyectos bajo MVC. Una vez instalado, debemos crear un proyecto de tipo Aplicación Web ASP. NET MVC o ASP.NET MVC Web Application, ya sea en Visual Basic o C#.
Alfaomega
Visual Studio - Firtman, Natale
Creando un sitio MVC
343
Fig. 7-19. Al instalar las plantillas encontraremos el nuevo tipo de proyecto MVC.
Fig. 7-20. Estructura de archivos y carpetas creadas luego de generar el proyecto. Visual Studio - Firtman, Natale
Alfaomega
344
7- AJAX y MVC
En el archivo global.asax encontramos el sistema de ruteo dinámico utilizado por defecto y estamos libres para modificarlo: public static void Routes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "Default", // Route name "{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "" } // Parameter defaults ); } protected void Application_Start() { Routes(RouteTable.Routes); }
En la carpeta Content encontraremos extras utilizados por las plantillas, como imágenes u hojas de estilo CSS; en la carpeta Controllers, los controladores de nuestra aplicación; en la carpeta opcional Models, los modelos; en la carpeta Scripts, todos los JavaScript que vamos a utilizar (está incluido jQuery y Microsoft AJAX) y en la carpeta Views, las vistas como archivos ASPX separados en subcarpetas por el nombre del controlador. En la plantilla de Visual Studio ya tenemos dos controladores con sus respectivas vistas, un controlador llamado (Cuenta), que istra la autenticación de s y un controlador llamado Home (Inicio), sin contenido, que es el controlador que se toma por defecto. Estos controladores, aunque no los utilicemos, podemos tanto dejarlos como quitarlos. Defininiendo el modelo El modelo puede ser estrictamente un conjunto de clases de lógica de datos y negocios o podemos utilizar algunos de los frameworks provistos por ASP. NET, como LINQ To SQL o Entity Framework, tal como se vio en capítulos pasados. Todas las clases se pueden colocar en la carpeta Models y así estar en el namespace NombreProyecto.Models. Defininiendo los controladores Un controlador es una clase que hereda de System.Web.Mvc.Controller y que, por convención, debe tener como sufijo la palabra Controller. Así, si quisiéramos crear el controlador Clientes, la clase se llamaría ClientesController. Para Alfaomega
Visual Studio - Firtman, Natale
Creando un sitio MVC
345
crear un controlador basta con agregar un nuevo elemento en la carpeta Controllers de tipo MVC Controller Class o Clase Controlador MVC. La forma más simple es hacer clic derecho sobre la carpeta controllers y en menú Agregar encontraremos a Controller.
Fig. 7-21. Al trabajar con el menú contextual de las carpetas, tendremos s directos a la creación de archivos controladores o vistas.
En C# nuestra clase nos quedaría así: using using using using using using
namespace MvcApplication1.Controllers { public class ClientesController : Controller { // // GET: /Clientes/ public ActionResult Index() { return View(); } } }
Visual Studio - Firtman, Natale
Alfaomega
346
7- AJAX y MVC
Notemos que nos crea un método (o acción) Index que por defecto es el invocado cuando en la URL no se identifica a ninguna acción. Notemos que el método devuelve un objeto de clase ActionResult. Esta clase será la encargada de decidir qué acción tomará el controlador, según vimos anteriormente. En este ejemplo simple creado automáticamente devuelve View(). Lo que hace este método es buscar en la carpeta /Views/Clientes (notemos la convención de tomar el nombre de la clase sin Controller) un archivo llamado Index.aspx y devolver esa plantilla. Existe una sobrecarga que nos permite cambiar el nombre y entregar una vista con un nombre en particular utilizando View(nombre). Definiendo las vistas Para poder definir la vista de la acción Index del controlador Clientes, debemos entonces crear la carpeta Clientes en Views y dentro crear una nueva vista llamada Index. Para eso, podemos utilizar el menú contextual de nuestra nueva carpeta Clientes y seleccionar Agregar > View o utilizar Agregar nuevo elemento, y seleccionar del tipo MVC View Page o MVC View Content Page si utilizamos Master Page. Al agregar una vista podremos elegir una página principal (Master Page) y crear una vista fuertemente tipada (strongly-typed), lo que permite que la vista posea propiedades a las que poder definir desde el controlador de manera tipada. Por defecto, podremos igualmente definir valores en la plantilla a través de propiedades string.
Fig. 7-22. El asistente para agregar una nueva vista.
Alfaomega
Visual Studio - Firtman, Natale
Creando un sitio MVC
347
Ya existe una página principal creada automáticamente disponible para cambiar en /Views/Shared/Site.Master. Al ver la vista finalizada, vemos que tenemos un ASPX que hereda de System.Web.Mvc.ViewPage, que no tiene Code-Behing ni código de servidor y donde no vamos a utilizar controles de servidor. Si bien es posible, no está pensado para eso, sino para utilizar HTML y zonas de reemplazo de contenido. <%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/ Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
Index
Index j3043
Desde un método del controlador, si accedemos al menú contextual de la clase, encontraremos dos s directos: Add View..., que nos permite crear una nueva vista para este controlador, y Go To View (Ir a Vista), que nos permite abrir automáticamente la vista asociada a la acción donde estamos parados. Desde la vista también tendremos a un menú contextual Go To Controller (Ir a Controlador) para abrir el controlador asociado a la vista actual.
Fig. 7-23. Si abrimos el sistema, podemos ver cómo acceder a la acción Index de Clientes.
Visual Studio - Firtman, Natale
Alfaomega
348
7- AJAX y MVC
Creando otra acción Ahora vamos a crear una nueva acción (Nuevo) en Clientes, que nos pida el nombre del cliente y el correo electrónico para insertar en una supuesta base de datos. Para ello creamos un nuevo método en el controlador de la siguiente manera: public ActionResult Nuevo() { return View(); }
Este método sólo retornará el formulario para insertar un nuevo cliente, cuya vista la vamos a crear en Views/Clientes/Nuevo.aspx: <% @ Page Title="Insertar Nuevo" Language="C#" MasterPageFile="~/ Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %>
Nuevo Cliente
Nuevo Cliente i194d
Notemos el action del formulario HTML. En lugar de enviarlo a un formulario Web, lo enviamos a una nueva acción llamada Insertar del controlador Clientes, en la carpeta actual. Ahora, entonces, agreguemos la acción Insertar y notemos lo simple que es: public ActionResult Insertar(string nombre, string email) { // TODO: Llamo al modelo e inserto el cliente // Devuelvo una acción del tipo redirección a otra acción return RedirectToAction("Index"); }
Alfaomega
Visual Studio - Firtman, Natale
Creando un sitio MVC
349
Con simplemente recibir parámetros, el sistema se encarga de tomar esos parámetros por el nombre del QueryString recibido desde el formulario con GET, guardamos el nuevo estado en el modelo y luego podemos devolver un View, mostrando un mensaje o una acción de tipo redirect enviando al a la lista. A través de atributos, se puede especificar que sólo se acepta acepta Post, por ejemplo. HTML Helpers Para simplificar la tarea, existen los ayudantes de HTML o HTML Helpers. Son funciones que devuelven strings y nos ayudan en la construcción de los códigos HTMLs más comunes. Por ejemplo, en lugar de escribir la etiqueta input podríamos reemplazarlo por: <%= Html.TextBox("email") %>
Entre los ayudantes disponibles, tenemos: ActionLink, BeginForm, CheckBox, DropDownList, EndForm, Hidden, ListBox, , RadioButton, TextArea y TextBox. Datos de la Vista Otra funcionalidad extremadamente útil son los datos de la vista o ViewData. Es una colección de tipo diccionario disponible en todas las vistas, que nos permite pasarle datos desde el controlador a la vista para su visualización. Por ejemplo, desde el controlador podríamos haber colocado: public ActionResult Nuevo() { ViewData["titulo"] = "Creando un nuevo cliente"; // En VB seria ViewData("titulo") return View(); }
Y en la vista, donde queremos colocar el título:
<%= ViewData("titulo") %> 2921x
También es posible pasar colecciones y, desde el ASPX de la vista, generar una instrucción de tipo For Each y dibujar así manualmente una tabla HTML. Un ejemplo en Visual Basic:
<% For Each Registro In ViewData("coleccion") %>
Visual Studio - Firtman, Natale Alfaomega 350 7- AJAX y MVC
<%= Registro.Nombre %>
<%= Registro.Email %>
<% Next %>
Algunos pueden pensar que pareciera que estamos volviendo a un modelo como ASP 3 (Clásico), pero hay que tener en cuenta la clara separación en capas que debemos lograr y la vista ASPX no debería escaparse de estas pequeñas cuestiones. Mostrar información y, como mucho, recorrer una colección no debería tomar demasiadas decisiones más que estas sencillas cosas. Como dijimos antes, también es posible crear vistas fuertemente tipadas y devolver la colección para mostrar directamente como parámetro de la vista. Devolviendo JSON Si estamos haciendo una aplicación AJAX podemos pedirle a una acción que nos devuelva un JSON con una colección de datos, por ejemplo: public ActionResult ListarJSON() { return Json(from c in bd.Clientes orderby c.Nombre select c); }
Eso nos devolvería, en lugar de una vista HTML, simplemente un JSON representando el objeto pasado por parámetro. Seguir con MVC Esto fue sólo una introducción al framework ASP.NET MVC. Invitamos al lector a visitar el sitio Web asp.net/mvc y consultar todo el gran potencial que, además de lo visto, posee este nuevo framework. Entre los temas que escaparon a este libro, se encuentran: Vistas fuertemente tipadas, tipos extra de resultados, creando rutas personalizadas, creando HTML Builders, validando los datos de entrada, manejo de errores, manejo de filtros de acción, caché de resultados, seguridad y pruebas (testing). ASP.NET MVC es un nuevo mundo por descubrir y desde aquí abrimos la puerta de entrada hacia la investigación y para saber qué proyectos son los ideales para ASP.NET MVC y cuáles lo son para ASP.NET Webform.
Alfaomega
Visual Studio - Firtman, Natale
Related Documents 171j1w
Visual Studio .net Framework 3.5 Para Profesionales - Maximiliano Firtman-librosvirtual.pdf 312v32