Page 1

Visual Basic.NET • C# • Delphi • ASP.NET • ADO.NET • .NET Framework • Windows Server System

dotNetManía www.dotnetmania.com

Dedicada a los profesionales de la plataforma .NET

nº7 septiembre 2004 • Precio: 6,00 €6 (España)

Enumeradores e iteradores

en C# y C#2.0

Entrevista a Dino Esposito ASP a ASP.NET sin esfuerzo • Carga dinámica de clases • Yo clasifico, tú clasificas… ¡que clasifique él! • Aplicando código y herencia en la configuración visual del control DataGrid • Extendiendo Sharepoint Portal Server • Seguridad de Internet Information Server (II). Autorización Comunidad Clikear Arquitectura Los avatares de las arquitecturas Open Source HealthMonitor opinión

Aseguramiento de la calidad. Normas versus personas


dnm.editorial

Windows XP SP2 para todos <<

La primera gran noticia después del parón veraniego es sin duda la publicación del esperado Service Pack 2 de Windows XP del que se dice que se instalará en unos 200 millones de PCs en el mundo. Básicamente esta actualización focaliza sus mejoras en la protección de las redes y de la memoria del PC, además de algunas mejoras en la seguridad en el envío de emails y del explorador. Merecerá la pena, sin duda merecerá la pena. Porque lo más importante está en la clara vocación de este service pack de aportar seguridad ante la proliferación de ataques de los pesados virus y worms tipo Blaster, además de aportar una mayor fiabilidad, siguiendo fielmente el espíritu de la política Trustworthy Computing que la compañía puso en marcha en enero de 2002. La forma de principal de distribución será a través de las actualizaciones automáticas y está enfocada a los PCs individuales que, por otra parte, son los más "desvalidos" y, posiblemente, el objetivo principal de esta actualización para evitar "las epidemias" que sufrimos (puede desactivarse temporalmente usando la utilidad XPSP2BlockerTools.EXE y posponer la instalación para mejor ocasión, o descargar la ver-

Editor Paco Marín (paco.marin@dotnetmania.com) Administración Pilar Pérez (pilar.perez@dotnetmania.com) Asesor Técnico/Coordinación Marino Posadas (marino.posadas@dotnetmania.com) Publicidad Mediadev Sophie Mancini (sophie@mediadev.es) Redactores y Colaboradores Alejandro Mezcua, Angel Esteban, Antonio Quirós, Antonio Rojo, Daniel Mazzini, David Carmona, Eladio Rincón, Francisco Charte, Fernando Guerrero,

sión para la instalación en red de unos de 270 Mb). De esta forma se instalará en muchos PCs que de no hacerse así quedarían sin actualización. Muchas personas nos llevamos un PC a nuestra casa, lo pinchamos en nuestra ADSL y ya tenemos nuestro ordenador listo… para ser infectado y atacado y, la mayor de las veces, también para ser usado para infectar y atacar a otros. Y no es culpa nuestra, sino del sistema que debería ser seguro sin necesidad de que quien lo use sea un experto en seguridad. Así que la buena noticia es que Microsoft distribuya su sistema operativo de escritorio con la seguridad bien estrecha, como por otra parte ya se hace en Windows 2003 Server. Igualmente, los desarrolladores tenemos que concienciarnos sobre la importancia que tiene que programemos de forma segura. Desde dotNetManía queremos poner nuestro granito de arena para crear entornos más seguros con artículos sobre seguridad de forma continuada. Actualmente, como muestra de ello, venimos publicamos una serie de artículos de José Manuel Alarcón sobre seguridad de Internet Information Server, la que lógicamente recomiendo.

Fernando Nogueras, Guillermo ‘guille’ Som, Jesús López, Jordi Rambla, Jorge Serrano, José Manuel Alarcón, José Miguel Torres, Juan Torres, Liborio López, Luis Miguel Blanco, Marino Posadas, Mario del Valle, Miguel Egea, Miguel Katrib, Pablo Abbate, Pedro Gómez, Pedro Pozo, Pepe Hevia, Salvador Ramos Diseño y Maquetación Éride Diseño Gráfico Tel.: (34) 91 477 48 85 eride@eride.net • www.eride.net Edita Netalia c/ Robledal, 135 28529 Rivas-Vaciamadrid (Madrid) Tf. (34) 91 6667477 Fax (34) 91 4991364

Imprime Gráficas Vallehermoso www.graficasvallehermoso.com

Depósito Legal M-9.999-2004

Suscripciones suscriptores@dotnetmania.com

Redaccion redaccion@dotnetmania.com

Nuevos colaboradores colaboradores@dotnetmania.com

<<dotNetManía

<<

dnm.editorial

3


7

dnm.sumario Aseguramiento de la calidad. Normas versus personas

8-10

El error es consustancial a la condición humana. Pero perseguirlo para minimizarlo también lo es. Como una de las manifestaciones más de lo humano, la construcción de software se encuentra sujeta a esta dialéctica entre el error y su persecución.

Entrevista a Dino Esposito

11-12

Dino Esposito es uno de los autores más conocidos del panorama actual del evangelismo en .NET. Es colaborador habitual de MSDN Magazine (con su sección “Cutting Edge”), y autor de varias obras sobre ADO.NET, ASP.NET y XML.

ASP a ASP.NET sin esfuerzo

13-16

Este artículo no pretende en ningún caso enseñar a desarrollar páginas ASP.NET, sino ayudarles a actualizar sus códigos ASP (ya escritos en VBScript) para que se ejecuten bajo el .NET Framework; obteniendo por tanto sus ventajas en cuanto a rendimiento.

Carga dinámica de clases

17-21

Ya hemos podido leer interesantes enfoques de ejecución dinámica de código. Pero a veces, esa aproximación puede suponer auténticos quebraderos de cabeza. La solución muchas veces pasa por mecanismos más sencillos, pero que parten del mismo principio: la abstracción.

Yo clasifico, tú clasificas... ¡que clasifique él!

22-24

dnm.sumario

Cómo implementar clases que sean clasificables por el .NET Framework.

Enumeradores e Iteradores en C# y C# 2.0

25-33

En este trabajo se ilustra la utilidad de los enumeradores y del ciclo foreach en C#. Se analizan cuáles son las limitaciones de estos enumeradores que justifican la importancia de la nueva inclusión de iteradores en el venidero C# 2.0.

Aplicando código y herencia en la configuración visual del control DataGrid

34-41

En este artículo ilustramos cómo la combinación de código y herencia en la plataforma .NET Framework, aplicadas sobre el control DataGrid, nos permitirá extender sus capacidades más allá de las barreras que impone el propio diseñador.

Extendiendo Sharepoint Portal Server

42-44

Cómo modificar o extender el comportamiento de Sharepoint Portal Server.

Seguridad de Internet Information Server (II) - Autorización

45-49

La autenticación de usuarios es tan sólo una pequeña parte de los problemas que debemos resolver en la seguridad de nuestro servidor IIS. Otro de ellos es la autorización.

Los avatares de las arquitecturas

50-52

Lo que queremos plantear en este artículo es que no existe una forma específica de la arquitectura, que lo que llamamos puntos de vista son a su vez metáforas, porque corresponden a perspectivas conceptuales y, en consecuencia, cada perspectiva conceptual está concibiendo la cosa con una forma, aspecto y funcionalidad diferentes.

dnm.trucos

53

dnm.comunidad.net

54

Clikear.com

dnm.opensource.net

56

HealthMonitor

dnm.biblioteca.net

57

The C# Programming Language Enterprise Development with Visual Studio .NET, UML and MSF.

dnm.desvan

58


dnm.noticias

6

noticias.noticias.noticias.noticias.noticias.noticias

<<dotNetManía

<<

dnm.noticias

I Reunión PASS Spanish Group Encuentro con los expertos en SQL Server El pasado 9 de julio tuvo lugar en Torrevieja (Alicante) la primera reunión del PASS Spanish Group (Asociación de Profesionales de SQL Server). En esta primera reunión la asociación tuvo el placer de contar con profesionales mundialmente conocidos por la comunidad de SQL Server como Ron Talmage, Michael Hotek y Fernando Guerrero. En un principio, parecía que la barrera del idioma iba a suponer algún problema, pero según se iban desarrollando las sesiones, la complicidad entre los ponentes (dos de ellos estadounidenses) y los asistentes fue en aumento. La agenda del evento fue la siguiente: Mejorando el Rendimiento de las Consultas en SQL Server 2000 (Ron Talmage). Para hacer nuestro sistema efectivo debemos conocer como funciona; Ron nos presentó cómo están organizados los datos internamente en SQL Server 2000 y nos enseñó a saber “leer” cómo se realizan las consultas en SQL Server; de esta forma aprendimos a mejorar nuestras consultas y cómo trabaja SQL Server. Introducción a Alta Disponibilidad (Michael Hotek). Cada día trabajamos con sistemas de bases de datos más grandes; dependiendo de las necesidades de nuestra empresa debemos garantizar la disponibilidad de nuestro sistema. Mike nos habló de los problemas que debemos afrontar y nos dio una introducción a las soluciones que ofrece SQL Server. Visión general de SQL Server 2005 (Fernando Guerrero). La próxima versión de SQL Server vendrá cargada de muchas novedades; auque el producto no saldrá al mercado hasta el año que viene, Fernando nos presentó las novedades más importantes desde el punto de vista del administrador y del desarrollador de bases de datos. Los miembros de PASS Spanish Group quieren agradecer públicamente al Ayuntamiento de Torrevieja por la cesión de un escenario tan incomparable como

el Palacio de la Música de Torrevieja, a Microsoft por la excelente organización del registro y publicidad dado al evento y a Solid Quality Learning y sus tres ponentes por hacer un pequeño hueco en sus vacaciones por las costas de Levante. Esta asociación tiene como objetivo poder realizar reuniones cada 6-8 semanas para hablar de temas relacionados con SQL Server y serán de especial atenFernando Guerrero ción mesas redondas sobre temas Solid Quality Learning que se deben afrontar en el del día a día tanto desarrolladores como administradores de SQL Server. Cuenta con toda la ayuda de Microsoft y la comunidad universitaria y la Asociación de Profesionales de SQL Server (PASS, http://www.sqlpass.org) para organizar los eventos por lo que no será de extrañar que veamos a expertos de soporte, arquitectura e IT de Microsoft en cualquiera de las reuniones. A su vez, MVPs de SQL Server de la talla de Miguel Egea, Salvador Ramos, Eladio Rincón (lo que bautizamos cariñosamente en dotNetManía como la escuela murciana) y Fernando Guerrero serán ponentes habituales en estas reuniones. Si deseas formar parte de la asociación, te puedes poner en contacto con cualquiera de los siguientes miembros: Eladio Rincón (eladio@SolidQualityLearning.com); MVP de SQL Server y Webmaster de siquelnet.com. Fernando Guerrero (fernando@SolidQualityLearning.com); MVP de SQL Server y presidente de Solid Quality Learning. Miguel Egea (miguel@SolidQualityLearning.com); MVP de SQL Server y Webmaster de portalsql.com. Salvador Ramos (webmaster@helpdna.net); MVP de SQL Server y Webmaster de helpdna.net.

Otros eventos PASS en el mundo 2004 PASS Community Summit Microsoft SQL Server Users Conference & Expo 28 de septiembre-1 de octubre. Orlando (Florida-USA) Donde también hablarán ponentes de Solid Quality Learning como el mismo Fernando Guerrero (omnipresente, por cierto), Kalen Delaney, Carl Rabeler, Andrew Kelly, Ron Talmage, y Dejan Sarka, además de otras compañías de reconocido prestigio (algunas de las cuales tienen interesantes sitios web que recomen-

damos) como Unisys, Ultimate Software, SQLDev.Net, IT Mentors, Spyglass, SQLServer-Performance.Com, Intellinet Corporation, SQLServerCentral.com, Quest Software, Avanade, WebbTech Solutions, SQLskills.com, etc., además, cómo no de Microsoft. Más información: www.sqlpass.org/events/summit04


<< dnm.noticias

IT Forum 2004 Microsoft IT Forum 2004 16-19 Noviembre, Copenhague (Dinamarca) Conferencia europea para profesionales TI, arquitectos de sistemas y aplicaciones, especialistas y administradores. Indudablemente este evento es el mayor evento europeo que queda de este año. Podrá escuchar a Bill Gates que abrirá con la keynote de apertura de esta conferencia de cuatro días intensos, en los que además podrá escuchar a algunos de los mejores ponentes del mundo, tanto personal de Microsoft como de otras empresas e independientes. Entre ellos se encuentran Scott Charney, Eric Rudder, Jesper Johansson, Steve Riley, Rafal Luckawiecki, Mark Russinovich, David Solomon y Kimberly Tripa entre otros. Alrededor de 200 conferencias técnicas sobre administración y gestión, en las que se usarán herramientas como Microsoft Operations Manager, System Management Server, Application Center y Microsoft Operantions Framework. Además de estas conferencias se han seleccionado una serie de preconferencias para el día 15 que servirán para que nos resulte más fácil sacar provecho de las conferencias posteriores. Entre ellas habrá preconferencias de .NET para profesionales TI, administración, configuración y despliegue; Microsoft Operations Manager 2005; buenas prácticas de SQL Server 2000; y una más que interesante sobre componentes internos de seguridad del núcleo de Windows por Rafal Luckawiecki y David Solomon, entre otras. Para más información: http://www.microsoft.com/europe/msitforum.

Windows XP Service Pack 2 disponible

La versión en castellano se espera para la primera quincena de septiembre Como venimos informando, Windows XP SP2 es una actualización de Windows XP que incluye mejoras de seguridad como el nuevo Windows Security Center que centralizará la configuración de seguridad y que incluye incluso protección antivirus; un nuevo firewall que reemplaza al actual ICF (Internet Connection Firewall) con políticas de Windows Security Center grupo integradas en el Active Directory; una nueva versión de RPC para protección contra ataques en la red; un bloqueador de popups y un gestor de descargas en el también nuevo Internet Explorer, junto con mejoras de seguridad en el Outlook Express y en el Windows Messenger; un nuevo Windows Update; una remodelada protección de memoria para evitar los comunes "overruns"; mejoras de seguridad en el Windows Media Player; y otros cambios. "Service Pack 2 es un paso significativo en la consecución de nuestros objetivos para ayudar a los clientes a hacer que sus PCs estén mejor aislados y más resistentes contra los ataques cada vez más sofisticados", dijo Bill Gates, Chairman and Chief Software Architect en Microsoft. "Este es el resultado de la continua inversión en innovación y una extensa colaboración con la industria." Durante el lanzamiento a nivel mundial del Service Pack 2, Microsoft localizará el software en 25 idiomas en los dos próximos meses y se distribuirá a fabricantes de ordenadores, clientes empresariales y consumidores a través de descargas, CDs gratuitos, y en nuevos PCs. El camino más fácil para que los actuales usuarios de Windows XP reciban la actualización cuando se libera la de su idioma es activando la característica de actualizaciones automáticas de Windows XP. Microsoft espera distribuir el Service Pack 2 a aproximadamente 100 millones de PCs a través de las actualizaciones automáticas en los próximos dos meses. Los clientes que no quieran obtener la actualización por este medio, pueden pedir un CD desde el sitio web de Microsoft. Si bien la versión inglesa está disponible desde finales de agosto, la versión en castellano no estará hasta la primera semana de septiembre. Para más información: http://www.microsoft.com/windowsxp

La NASA ha incorporado iPAQs como parte del equipamiento en la Estación Espacial Internacional Soyuz 8 Los iPAQs van a ser empleados como herramientas de trabajo móviles con las que la tripulación grabará los procedimientos que sigan y un diario personal, al igual que podrán consultar sus correos electrónicos y agendas. Además, con los iPAQs los astronautas podrán entretenerse escuchando música, viendo fotos enviadas por sus familias o leyendo e-books, lo que les permitirá

durante el viaje tener algunas de las comodidades de las que disfrutarían en sus casas. En tierra, los dos centros de soporte de la NASA, Jonson Space Center y Rusia´s City Space Center, utilizarán también los ordenadores de bolsillo de HP durante los entrenamientos a los que se somete la tripulación así como para evaluar nuevas aplicaciones que pue-

dan ser desarrolladas para futuros vuelos interespaciales. Sólo ha sido necesario realizar pequeñas modificaciones para adaptarlo al entorno espacial. Junto a los iPAQ H5550, también se han cedido baterías, tarjetas PC Card y SD, y lector de códigos de barras para el ordenador de bolsillo, todo lo cual será utilizado en la estación espacial.

dnm.noticias


Por Antonio Quirós General Area Manager Alhambra-Eidos

Aseguramiento de la calidad. Normas versus personas El error es consustancial a la condición humana. Pero perseguirlo para minimizarlo también lo es. Como una de las manifestaciones más de lo humano, la construcción de software se encuentra sujeta a esta dialéctica entre el error y su persecución. La pregunta que nos haremos en este artículo versará sobre si la mayor eficacia en la persecución del error la lograremos activando políticas de recursos humanos que nos ayuden a contratar a los mejores profesionales o si, por el contrario, deberemos desarrollar métodos eficaces y sistemas de control de calidad que nos ayuden a lograr el objetivo mencionado de forma independiente al perfil de los recursos humanos implicados en los procesos.

<<dotNetManía

<< El aseguramiento de la calidad

8

Aseguramiento de la calidad, he ahí la palabra clave. Visto así, alguien podría pensar que voy a soltar una retahíla de normas ISO o similares acerca de cómo garantizar que el software haga lo que el usuario haya pedido que deba hacer y que, además, lo haga bien. Pero no, no hablaré de aspectos demasiado técnicos sino que, aprovechando esta tribuna de opinión, reflexionaré tal como he hecho en otras ocasiones sobre el alcance teórico de los términos y los fundamentos que subyacen a las realidades descritas por los mismos. Todo ello con la finalidad de ayudar a pensar (¡qué presunción por mi parte!) a quienes deben moverse muchas veces entre estos términos sin haberse parado a pensar lo suficiente sobre los mismos y sus implicaciones y efectos. Los que realmente deseen concretar técnicamente en este asunto pueden acudir a los correspondien-

tes tópicos sobre aseguramiento de calidad que encontrarán en cualquier manual sobre Ingeniería del Software o a los interfaces con el aseguramiento de la calidad que las metodologías de desarrollo 1 realizan . En cualquier caso y, por trabajar con una definición operativa razonable definiremos el aseguramiento de la calidad en el proceso de desarrollo de software como la concordancia del software producido con los requisitos funcionales y de rendimiento explícitamente establecidos, con los estándares de desarrollo explícitamente documentados y con las características implícitas que se esperan de todo software desarrollado profesionalmente. En líneas generales podemos considerar que todo sistema construido con un mínimo de profesionalidad debe seguir este criterio y ocuparse de garantizar que el software desarrollado tenga unos niveles de calidad razonables. Sin embargo esta realidad se ve enturbiada por un amplio conjunto de interfe-

1 Así, por ejemplo, puede ver el interfaz con el aseguramiento de la calidad de Métrica 3 en el web del Consejo Superior de Informática: http://www.csi.map.es/csi/metrica3/calidad.pdf. O puede seguirse lo prescrito por el modelo CMM : http://www.sei.cmu.edu/cmm/cmm.html


<< dnm.opinion

¿Wellington o El Empecinado? En general, podemos considerar que la calidad en un proceso se obtiene por la definición de normas que se ocupen de asegurar el nivel adecuado de la misma y por una determinada actitud personal, una tendencia psicológica en el individuo, agente del proceso, que le lleva a cuidar los detalles sobremanera y a responsabilizarse claramente de la consecución de los objetivos que se deban lograr. Ante este binomio, la reflexión surge de inmediato. ¿Cuál de las dos facetas es más importante? La actitud del individuo o la determinación de normas. Sigamos reflexionando, antes de contestar a la pregunta. ¿Quién ganó la Guerra de la Independencia en España? ¿Wellington o El Empecinado? Nuestros manuales de historia, haciendo acopio del narcisismo nacionalista habitual han tendido a remarcar que la fogosidad de la raza española y sus héroes individuales fueron capaces de vencer al invasor francés. ¡Qué drama cuando hoy nos documentamos y descubrimos que, al menos, la victoria se debió en una 2

El error es consustancial a la condición humana, pero perseguirlo para minimizarlo también lo es.

buena parte (quién sabe cual) a la organización y táctica del ejército inglés dirigido por el Duque de Wellington! En cualquier caso, una actitud empresarial pragmática debería llevarnos a no descuidar ambos términos del binomio, ocupándonos tanto de establecer los métodos adecuados para controlar la calidad, como de realizar los procesos de selección de personal más razonables para garantizar que los perfiles de los profesionales que contratemos tengan la adecuada tendencia a la minimización del error.

CMM o XP En el fondo es la misma polémica que se plantea entre los seguidores de las metodologías procedimentales rigurosas (CMM, Capability Maturity Model, como la más conocida y desarrollada) frente a los acólitos de la agilidad (XP, eXtremme Programming, como teoría más conocida). Ejército regular o guerra de guerrillas. El primero se organiza con una escala de oficiales clara, unos procedimientos rigurosos, unas comunicaciones bien determinadas, etc. Su objetivo son los grandes resultados, las grandes batallas (en las que a veces,

<<

William Poundstone, How Would You Move Mount Fuji?, Little, Brown and Company, 2003

<<dotNetManía

rencias que hacen que no siempre se consiga el objetivo previsto. Igualmente debo precisar que considero que el gran avance tecnológico de la sociedad occidental se debe a la mejora permanente de los procesos que, gracias en buena parte a los sistemas de gestión de calidad, se vienen desarrollando desde mediados del siglo pasado. Así, pues, los que tenemos una edad avanzada (perdón por el eufemismo) recordaremos la inexactitud de muchos aspectos industriales o de servicios y cómo el mundo ha ido avanzando hacia procesos mucho más rigurosos. Vivimos en una sociedad altamente desarrollada que no tolera el fallo en los procesos y que, por tanto, exige a sus gobiernos, empresas, etc. que se ocupen de que el error esté lo más eliminado posible dentro de nuestro horizonte vital. Imagine el sufrido lector lo frecuente que era un apagón eléctrico de varias horas en nuestro país hace 30 años y lo infrecuente que es hoy. Multamos a las eléctricas si esto sucede. Recuerde, igualmente, la de veces que debía llevar su coche al taller a resolver pequeños fallos durante el periodo de garantía y como dichos fallos hoy se han visto minimizados. Exigimos cada vez periodos de garantía más largos a los fabricantes y, por tanto, éstos se ocupan de mejorar sus procesos para que nada falle. Caminando ya hacia ejemplos más concretos en el mundo del software, piénsese lo que sucedería si nuestro banco hiciera mal los cálculos en nuestra cuenta corriente, si expendiera mal los billetes en un cajero o si Hacienda perdiera la información que le sirve de base para hacernos pagar.

9


<< dnm.opinion

… no creo en los extremos y, por tanto, pienso que una empresa debe cuidar sobremanera ambas realidades: las normas y las personas.

por cierto, no se tiene claro quien gana y quien pierde, solo se contabiliza el número de muertos). El segundo se caracteriza por la acción rápida y eficaz, el efecto brillante, aunque a veces por pequeño sea inútil. Ambas filosofías tienen cabida en el mundo del desarrollo de software actual. Por ejemplo, CMM se impone cada vez más a nivel de las grandes empresas de desarrollo, a nivel de las factorías asiáticas de software, etc. Realmente, donde tiene más importancia, la relación contractual con el cliente que la bondad tecnológica del producto a desarrollar, CMM, o sea el ejército regular, es la clave. Las metodologías ágiles parecen tener más presencia en las empresas desarrolladoras de producto propio, aquellas donde la calidad de lo hecho debe prevalecer a cualquier otra consideración. XP, o sea la guerra de guerrillas representa el paradigma dominante. En el fondo lo que se produce aquí es una contraposición entre la calidad del producto final (XP) y la calidad del modelo de colaboración con el que el producto se desarrolla.

<<dotNetManía

Microsoft y los afinadores de pianos

10

En el mundo empresarial del software, tal como hoy se desarrolla encontramos experiencias de todo tipo. Hay empresas que han hecho de la protocolización de sus acciones una máxima que les ha llevado a reglamentar cualquier actividad de las mismas. Esto está muy bien y seguro que incrementa la capacidad de pronóstico sobre cómo funcionará determinado proyecto, sin embargo también burocratizará sus acciones lo que, probablemente, le llevará a no afrontar competitivamente determinadas áreas de negocio. Frente a ellos nos encontramos a quienes ponen el foco principalmente en la captación y cuidado de los mejores recursos humanos. Microsoft quizá sea uno de los prototipos de empresa a este respecto. William Poundstone ha escrito una obra donde, a colación de las técnicas de selección de personal que

hoy siguen las empresas, hace un importante estudio de cómo Microsoft aborda este problema. A este respecto indica que una de las normas principales de la compañía es no contratar a la persona equivocada, aunque ello signifique, en ocasiones, perder candidatos excelentes. Para lograr esto se usan determinadas herramientas, pero una de las más repetidas es la de los juegos lógicos que se proponen a los candidatos para ver cómo estructuran su respuesta. Una curiosa pregunta es ¿Cuantos afinadores de pianos hay en el mundo?. Evidentemente el no lo sé o el soltar una cifra sin ton ni son no sirve. Lo que se busca es el proceso de análisis mental por el que el candidato intentaría hacer un cálculo razonable para la respuesta. En fin, lo que quiero concluir con esto es que algunas empresas de gran éxito en el ámbito del desarrollo de software basan mucho más su estrategia empresarial en la selección de los mejores recursos humanos que en la determinación de los mejores protocolos de actuación para los mismos. Se parte de que personas de gran capacidad, muy competentes técnicamente, con autonomía y perspicacia, serán capaces de dar la respuesta más efectiva a las complicadas disyuntivas que nuestro complejo mundo actual les presenta.

Y qué opino yo Bueno, al fin y a la postre, esto es un artículo de opinión y, por tanto, debo indicar mi punto de vista ante el tema. Para ello aplicaré la vieja máxima aristotélica acerca de que la virtud siempre está en el término medio. Personalmente no creo en los extremos y, por tanto, pienso que una empresa debe cuidar sobremanera ambas realidades: las normas y las personas. Sólo personas con el adecuado perfil tendente al cuidado de la calidad de los procesos pueden hacer que éstos se lleven a cabo de forma razonable, pero sólo las normas y los protocolos de actuación garantizarán la repetibilidad de los éxitos. A lo largo de mi vida profesional me he enfrentado a proyectos, empresas, personas, etc. que han dado más importancia a alguno de los dos términos. Mi conclusión en este artículo es que si esto se hace así no estaremos poniendo los medios necesarios para lograr los mejores sistemas posibles. O lo que es lo mismo: a) Los mejores técnicos no conseguirán hacer las mejores aplicaciones sin el método, las normas y la planificación adecuada. b) Que la empresa mejor organizada del mundo difícilmente construirá un buen proceso si no se encarga de seleccionar a los empleados con más tendencia a la persecución de sistemas libres de fallos.


Por Marino Posadas MVP Visual Developer C# Alhambra-Eidos www.elavefenix.net

Entrevista a Dino Esposito En el pasado Tech-Ed Europe 2004 en Ámsterdam, tuvimos oportunidad de reunirnos con Dino Esposito, uno de los autores más conocidos del panorama actual del evangelismo en .NET. Colaborador habitual de MSDN Magazine (con su sección "Cutting Edge"), y autor de varias obras sobre ADO.NET,ASP.NET y XML, Dino es, junto a Francesco Balena y Marco Bellinaso, el máximo representante de la escuela italiana y responsable del mantenimiento de http://www.dotnet2themax.com, y de una atractiva bitácora http://weblogs.asp.net/despos, donde comenta habitualmente sus actividades y opiniones sobre el "roadmap" the .NET Framework.

do Tech-Ed, Dino respondió a nuestro requerimiento con gran amabilidad. Comenzamos preguntándole por su actividad como autor de libros y artículos. Sigo con asiduidad tu trabajo y la característica que más te identifica -en mi opinión- es una extrema facilidad para tratar por igual temas de alto nivel y soluciones concretas en las que aplicas el bajo nivel. ¿Cómo se consigue eso? Quizá debido a mi formación en la universidad, en la que me centré en el estudio de C++ como lenguaje base, y donde se nos decía -como a todos- que ese era el lenguaje del futuro. Más tarde escribí varias aplicaciones utilizando C++ y el SDK de Windows. Eso era a comienzos de los 90. Eran tiempos en los que no nos quedaba más remedio que ocuparnos del alto nivel desde el punto de vista del análisis de aplicaciones, y luego, escribir mucho código, así que crecí haciendo cosas de bajo nivel para construir aplicaciones de alto nivel. Por eso, cuando escribo libros y artículos, en los que muchas veces trato de demostrar los trucos que conoz-

co del API de Windows intento siempre poner las cosas bajo la perspectiva de las aplicaciones del mundo real. Al objeto de presentarte a nuestros lectores, ¿cuál es exactamente tu trabajo en estos momentos? Básicamente, utilizo 3 palabras para describir eso: escribir, enseñar y realizar consultorías. Ahora, mi actividad principal es la escritura, pero previamente lo era la consultoría, en varias empresas (Accenture, etc.). Tras eso, decidí dedicarme exclusivamente a investigar, escribir y dar conferencias. Había trabajado mucho en el entorno consultor y entré relativamente tarde en el mundo Web, aunque eso ocupa

ahora una buena parte de mi tiempo. Es como si hubiera ido moviéndome del entorno del cliente al actual que, fundamentalmente, es el servidor (ASP.NET). Así que la escritura abarca casi el 75% de mi actividad, y luego, algo de enseñanza y consultoría. A diferencia de lo que sucede en algunos otros países europeos y en EE.UU., en Italia la consultoría de alto nivel no es muy valorada y si quieres un reconocimiento de tu trabajo tienes que pertenecer a alguna gran compañía, que siempre limita tu actividad. Creo que tienes en cartera un último libro sobre ADO.NET 2.0…

<<dotNetManía

pesar de no figurar entre la lista de << A oficialmente entrevistables para el pasa-

11


<<dotNetManía

<< dnm.directo.entrevistas

12

Sí y está terminado desde hace ya algún tiempo. De hecho pensábamos sacarlo con Microsoft Press para el PDC2 porque se pensaba ofrecer la primera beta entonces. No obstante, Microsoft decidió posponer la salida y estamos esperando a esa beta para la publicación. El código (que se podrá descargar libremente), se ha revisado un poco, eso es todo. Entremos en materia entonces, y hablemos de esta nueva versión. ¿Puedes resumirnos las características más importantes de la versión 2.0? Respecto a ASP.NET, la primera cosa que me llama la atención es la productividad que se puede obtener. Existen herramientas añadidas que cubren necesidades reales, de cosas que la gente tiene que hacer el 70% u 80% de las veces. También nuevos controles, como los nuevos Datagrid o los controles que permiten añadir seguridad a los sitios Web, que son tareas pesadas y delicadas y ahora pueden hacerse en un momento. Otra de las cosas que me encantan de esta versión es el Modelo de Proveedores (Provider Model). Es una característica que se basa en patrones de diseño, permitiéndote, por ejemplo realizar importaciones entre la versión 1.x y la 2.0 con extrema sencillez. Con este modelo además, resulta muy fácil enlazar elementos de la interfaz de usuario con los respectivos proveedores a nivel de Back-End, cosa que no estaba presente en absoluto en la versión anterior. ¿Podría considerarse similar a lo que se consigue mediante otra nueva característica, los object spaces? En cierto modo sí. La diferencia es que en las versiones actuales, tienes que escribirte todo el código (CodeBehind) para gestionar los proveedores de información del Back-End. Bien, pues este código ha sido convertido en un componente, y tú sólo tienes que indicar la forma en que deseas que se comporte. Es una labor de "orquestación", más que otra cosa. Por otro lado, respecto al soporte de la "gestión del usuario" y la seguridad, prácticamente no existía en la versión 1.x. Eso significa mantenimiento de la base de datos, de usuarios, etc.

Con el Provider Model puedes seguir utilizando tu código antiguo de la versión 1.x, y aprovecharte -desde el propio diseño- de las nuevas características. Excelente. Pero todo eso es de cara al servidor. ¿Hay características nuevas de cara al cliente? Existe un nuevo modelo de objetos de cliente, más rico que el anterior en posibilidades. La característica más sobresaliente son los llamados script callbacks. Consiste en la posibilidad de llamar a objetos COM asociados con el Explorador IE 5 o superior, de forma que cuando cierta información se modifique en el cliente, éste puede hacer un callback al servidor de manera que se refresque la información dependiendo de ciertas características. Puede usarse a nivel de página, pero también -y especialmente- con algunos de los nuevos controles, como el TreeView o el Datagrid mejorado. Especialmente este último puede usarlo para ordenaciones en el cliente, sin necesidad de un PostBack explícito. El tercer punto sobre el que te quería preguntar es la implantación (deployment). ¿Habrá novedades a este respecto? Creo que sí. Y me baso en el nuevo modelo de compilación, mucho más potente que los anteriores. La idea central es que ciertos recursos (en función de su extensión), ahora pueden compilarse de forma dinámica. Por decirlo así los cambios son detectados y por tanto la necesidad de recompilación. Y esto, para distintos tipos de ficheros. En la versión 1.x el directorio /BIN tenía un significado especial para el runtime. En esta versión también lo tienen otros directorios, como /CODE, /TEAM, /RESOURCES o /DATA y la información incluida en ellos es especialmente sensible para el motor de ejecución. De forma que si tienes código C#, por ejemplo en el directorio /CODE, en cuanto se produce una modificación, ésta es detectada y el código recompilado y asignado a la aplicación sin compilación explícita. Últimamente hemos podido apreciar un cambio en tus intereses desde ADO.NET hacia ASP.NET y su paradigma de código de soporte de páginas (CodeBehind). Teniendo en cuenta que

XAML en Longhorn va a realizar la misma función (o muy similar), ¿podríamos decir que prevés que el modelo de desarrollo futuro va ser ya de esta forma a partir del nuevo sistema operativo? Sí. Absolutamente. Creo que vamos a tener una especie de lenguaje para declarar la IU, y para aquellas cosas que no pueden expresarse de una forma declarativa estará el código fuente en un lenguaje .NET. He trabajado algo con XAML y he visto muchas características similares. Esto demuestra que en Microsoft existe un intercambio continuo de ideas entre los equipos de trabajo al objeto de aprovechar el conocimiento de otras áreas de desarrollo. ¿Así que profundizar en la estructura del nuevo ASP.NET ayudaría a comprender el modelo propuesto para Longhorn? Por supuesto. Creo que un buen conocimiento del modelo de ASP.NET puede ubicar adecuadamente al desarrollador futuro de Longhorn, cualquiera que sea la implementación final de éste. Y no creo que los cambios que se están gestando ahora en Longhorn vayan a ser modificados significativamente, en los próximos dos años. Longhorn propone un modelo único de programación bajo el lema "desarrolla una vez y distribuye dónde y cómo quieras". ¿Será fácil convencer a los usuarios sobre las bondades de dicho modelo? Tal y como yo lo veo, es más una imagen de implementación que de desarrollo. Longhorn propone un modelo de distribución de aplicaciones Windows vía Web, pero no creo que eso tenga un implicación directa en las aplicaciones Web en la forma que las hacemos hoy. Podremos distribuir aplicaciones Windows de varias formas, pero creo que las aplicaciones Web seguirán siendo desarrolladas de forma similar a la actual en los próximos años todavía. Bien, pues te agradecemos esta deferencia y esperamos que tu escaso tiempo libre te permita colaborar con nosotros alguna vez. Tus aportaciones serán más que bienvenidas. Muchas gracias a vosotros y seguimos en contacto.


Juan Torres Antigoto www.indesia.com/AntiGoto

ASP en ASP.NET sin esfuerzo

<< Lo primero que debéis saber es ¿qué es la tecnología

.NET de Microsoft? (cosa que deberíais hacer leyendo artículos o manuales al respecto), yo sólo os diré que funciona con la misma filosofía que Java. Se ha diseñado un entorno o máquina virtual (.NET Framework) que con una tecnología denominada Common Language Runtime (CLR) es capaz de ejecutar un código intermedio (MSIL). A su vez se han rediseñado una serie de lenguajes como Visual Basic .NET y JScript .NET (también se ha diseñado un nuevo lenguaje denominado C#), que son compilados a este lenguaje intermedio (o sea el resultado de la compilación de estos lenguajes no es código máquina ejecutable sino MSIL, para todos el mismo). Con todo ello se pretende que trozos de código escritos en diferentes lenguajes puedan interaccionar y también que el software se pueda portar a diferentes entornos (simplemente diseñando versiones del .NET Framework para otras plataformas y sin tener que cambiar ni una línea de código). A todo ello hay que añadir mejoras en la orientación a objeto de los lenguajes, la interrelación de los mismos a través del Visual Studio .NET, las nuevas librerías comunes (como ADO.NET) y otras nuevas características como el soporte SOAP/XML de intercambio de información. Bueno eso sólo era una pequeña introducción (tal vez hasta improcedente en una revista como ésta en la que podemos leer tanto sobre .Net, pero

me gusta introducir los temas que voy a tratar), no os asustéis que a partir de ahora el tema va a ser más práctico. Dejémonos de discursos y pongamos manos a la obra. Centrémonos en nuestro objetivo: Queremos coger nuestra página ASP que funciona perfectamente y hacerla funcionar bajo ASP.NET (no me refiero a traducir el código ASP a código .Net sino a introducir algunas variaciones que lo hagan compatible con el .Net Framework). Para conseguirlo tomaremos una página de ejemplo escrita en ASP clásico e intentaremos ejecutarla directamente como si se tratara de una ASP.NET. Después iremos modificando el código para evitar los errores que nos irán apareciendo. En principio los errores se deberán, en su mayoría, a cambios sintácticos entre el VBScript (usado en las páginas ASP clásicas) y el Visual Basic.NET usado en las modernas ASP.NET. Hay que entender que ASP.NET no es una nueva versión de ASP, sino un cambio de tecnología por lo que no se mantiene toda la compatibilidad. (En este punto debo deciros que en un mismo sistema pueden coexistir páginas ASP clásicas y ASP.NET. Esto se debe a que usan librerias de ejecución distintas y el Internet Information Server usa la necesaria en cada caso) Bueno empecemos echándole un vistazo a la figura 1 que es la página que usaremos. Vamos a usar una página típica en la que mostraremos en

<< dotNetManía

Este artículo no pretende en ningún caso enseñar a desarrollar páginas ASP.NET. Ese tema ha sido y será tratado por otros compañeros en posteriores artículos. Lo que se pretende es que podáis actualizar vuestros códigos ASP (ya escritos en VBScript) para que se ejecuten bajo el .NET Framework; obteniendo por tanto sus ventajas en cuanto a rendimiento.

13


<< dnm.asp.net una lista datos obtenidos de una tabla de base de datos (concretamente una lista de tecnologías de desarrollo). Lo primero que nos encontramos en las líneas 1 y 2 es: <%@ Language=”vbscript” %> <% Option Explicit %>

<html> <head> <title>Actualizacion de ASP a ASP PuntoNET</title> </head> <body> <table align=”center”>

A continuación creamos tantas celdas en la tabla (etiquetas <tr><td>

celda lo devuelto por el campo nombre del recordset. <% while not rs.EOF %> <tr> <td> <%response.write rs(“nombre”)%> </td> </tr> <% rs.movenext wend %>

Después cerramos la tabla, el cuerpo y el documento HTML: </table> </body> </html>

Ya sólo nos queda cerrar la conexión:

Figura 1

En estas líneas definimos que vamos a usar el lenguaje VBScript y que queremos que se nos avise en el caso de que usemos variables sin declararlas. Seguidamente abrimos la conexión a la base de datos utilizando ADO (ActiveX Data Objects) y realizamos una consulta simple rellenando el recordset:

</td></tr>) como registros haya en el recordset. Y escribimos dentro de cada

<% rs.close set rs = nothing oConn.Close %>

Ni que decir tiene que este código tal y como está no se ejecuta correctamente sobre el .NET

<% Dim oConn, rs, SQL set oConn = Server.CreateObject( “ADODB.Connection”) set rs = Server.CreateObject( “ADODB.Recordset”) oConn.Open “DRIVER={Microsoft Access Driver (*.mdb)}; DBQ= “ & Server.MapPath(“bd.mdb”) SQL = “select * from desTemas” set rs = oConn.Execute(SQL)

<<dotNetManía

%>

14

Ahora la cabecera del documento HTML, abrimos el cuerpo del documento y la tabla:

Figura 2


<< dnm.asp.net Framework. Así que ahora que conocemos el código del que partiremos empecemos con la transformación. Lo primero que debemos hacer es cambiar la extensión de nuestro archivo de .ASP a .ASPX (debemos recordar que el Internet Information Server usará el ASP.DLL para manejar las

<%@page explicit=”true”%>

Explicit anteriormente y ahora usaremos el atributo language para indicar

Tras volver a mostrar la página en el navegador nos aparece un nuevo mensaje de error que vemos en la figura 3. En dicho mensaje se nos advierte de que sólo puede haber una directiva page. Para poder indicar en una sola directi-

que usaremos Visual Basic .NET (aquí cabe recordar que en ASP.NET ya no se usa VBScript sino alguno de los lenguajes compatibles con .NET framework como Visual Basic .NET, JScript .NET, C#, ...). Pondremos: <%@page language=”vb” explicit=”true”%>

De nuevo volvemos a intentar ejecutar la página (ver figura 4). El nuevo mensaje nos indica que en Visual Basic.NET han desaparecido las sentencias Set y Let por lo que debemos quitarlas. Las quitaremos en (dejamos las asignaciones igual pero sin SET): set oConn = Server.CreateObject( “ADODB.Connection”) set rs = Server.CreateObject( “ADODB.Recordset”)

y en: set rs = oConn.Execute(SQL)

y Figura 3

páginas con extensión ASP y el ASPNET_WP.EXE para las de extensión ASPX); así el IIS entenderá que nuestra página debe ser ejecutada a través del .NET Framework (que lógicamente debe estar instalado en el servidor). Tras hacerlo mostramos nuestra página en un navegador. Nos aparece la primera página de error (ver figura 2). Lo primero que advertimos es la mejora en los mensajes de error en relación a los que se mostraban en el sistema ASP clásico. Apreciamos como se nos indica un error en la línea 3 con la descripción:

va page varias cosas se han creado los atributos. Hemos usado el atributo

set rs = nothing

De nuevo recargamos la página en el navegador y de nuevo aparecen errores (ver la figura 5).

Este error hace referencia a que en ASP.NET no está permitido el uso de Option Explicit, para ello se ha creado un atributo de la directiva page de la página. O sea, debemos quitar el Option Explicit y colocar:

Figura 4

<< dotNetManía

“BC30024: La instrucción no es válida dentro de un método.”

15


<< dnm.asp.net el objeto recordset tiene la propiedad por defecto fields (y ahora hay que escribirla explícitamente) y a su vez fields tiene la propiedad value (que también debemos indicar). En definitiva la línea nos queda: <%response.write( rs.fields(“nombre”).value)%>

Figura 5

Concretamente nos aparece un error en la linea 11, indicando: “BC30800: Los argumentos de método se deben incluir entre paréntesis” O sea, en Visual Basic.NET ha habido un cambio (con respecto a versiones anteriores) y ahora la sintaxis para parámetros de funciones y procedimientos es la misma; es obligatorio que vayan entre paréntesis. Por ello debemos cambiar las siguientes líneas:

lo que aparece? Pues aparece una sucesión de System.__ComObject y pensaréis ¿esto que es? No os preocupéis, en este caso no hay errores sintácticos. Se trata de un error de conversión de tipos (por eso no hay mensaje). El problema radica en la linea 30: <%response.write(rs(“nombre”))%> ¿qué ocurre, no ves ningún error? En realidad en VBScript no lo hay. El problema está en que Visual Basic.NET no permite usar propiedades de objeto por defecto. O sea,

Tras todo esto y por fin, vemos el resultado deseado (idéntico al original en ASP clásico pero ahora ejecutado sobre un sistema mejorado y más eficiente. Ver figura 6). Bueno espero que este ejemplo de cómo actualizar una página ASP (vbscript) para que se ejecute correctamente bajo ASP.NET os haya servido y os permita optimizar vuestras páginas mientras aprendéis a crear verdaderas páginas ASP.NET. En la web de la revista y en http://www.indesia.com/Antigoto podéis encontrar los archivo con los códigos completos usados en el artículo para que lo podáis descargar. El código de la página original lo tenéis en el fichero ARTEJEMPLO01.ASP y las sucesivas actualizaciones en los ficheros ARTEJEMPLO01.ASPX a ARTEJEMPLO08.ASPX (en cada uno se ha ido subsanando un error) y también os paso una pequeña base de datos Access (la usada en el ejemplo) bd.mdb.

oConn.Open “DRIVER={Microsoft Access Driver (*.mdb)}; DBQ= “& Server.MapPath(“..\db\bd.mdb”)

por oConn.Open(“DRIVER={Microsoft Access Driver (*.mdb)}; DBQ= “& Server.MapPath(“..\db\bd.mdb”))

y <%response.write rs(“nombre”)%>

por

<<dotNetManía

<%response.write(rs(“nombre”))%>

16

Y por fin ya no aparecen más mensajes de error. Pero ... ¿Que es lo que debería mostrarse en el navegador? ¿Y que es

Figura 6


Por Pepe Hevia Software Architect Alhambra-Eidos

Carga dinámica de clases En otros artículos hemos podido leer interesantes enfoques de ejecución dinámica de código. Pero a veces, esa aproximación puede suponer auténticos quebraderos de cabeza. La solución muchas veces pasa por mecanismos más sencillos, pero que parten del mismo principio: la abstracción.

de la solución a un problema, en vez de abordar técnicas de fuerza bruta (implementación) para resolverlo. En nuestros días se hace muy necesario el plantearse una buena arquitectura de software que: a) Garantice un software muy modular. b)Adaptable a los cambios del negocio en tiempos récord y con el mínimo impacto. c) Que pueda ser extensible a nuevas especificaciones sin tirar todo el trabajo hecho por la borda. Y esto muchas veces no ocurre en los desarrollos actuales. Por ejemplo, quién -en ASP.NET o Visual Basic .NET-, no coloca en el mismo manejador del evento, el código que ejecuta una acción de negocio, en vez de sacar ese código a una clase, y llamar a la clase. O quién no coloca en una opción de menú (definida en diseño, claro) el código que dispara la llamada a un diálogo que realiza un proceso. Todo es muy típico. Y muchos pensaréis que es lo suyo. Pero creo que si os dieran tiempo para pensarlo veríais que repetís ese mismo principio para cada proyecto. Esto nos tiene que dar lugar a pensar que muchos procesos del software en el que reside la lógica de negocio, NO dependen de dicha lógica de negocio. Si no que proporcionan un valor añadido. Por ello, muchas veces es increíble oír que tenemos que recompilar todo un proyecto sólo por el hecho de que un informe cambie de filosofía, o mejor aún tengamos que incorporar nuevos informes. En este caso, el problema del software no está en la implementación de CADA informe, sino de intentar entender que para el software el problema está en que debe presentar UN informe en un momento dado -el que determi-

ne el usuario en su lógica de negocio-, y que este concepto de informe será elegido por el usuario en función de sus necesidades; y que estas necesidades cambian. Pero para la arquitectura del software, será mostrar un informe. El cómo se implemente dicho informe será una decisión que se tome en función de qué tipos de informes estén establecidos, cuál es el que le haga falta al usuario, etc. Es decir, que nuestra misión es aprender a separar la abstracción de la implementación. Que cualquier tecnología orientada a objetos -.NET en este caso- nos permite implementar empleando los conceptos de Interfaz <-> Clase empleando como pegamento un cargador dinámico de clases.

El análisis Qué necesitamos. Una aplicación que permita mostrar informes de una base de datos. Por ahora nuestro usuario quiere un informe de empleados en una aplicación para Windows. Pero más adelante quiere uno de productos, y más adelante, pues como que no se acuerda…, pero sabe que querrá más. El segundo problema, que cada informe necesita de un aspecto diferente. El de empleados lo quiere con una rejilla de datos al estilo Access y el de productos, con un control de lista de esos que tienen iconos. Por último, no quiere tener problemas con los informes, esto es, que no quiere que se le meta mano a su máquina con instalaciones ni historias. Que le gustaría algo del estilo a Windows Update. Bien, pues con estos requisitos, empezaremos a darle vueltas al coco. De acuerdo que el que conozca ADO.NET puede ver cómo queremos las cosas (todos de acuerdo en que una buena lógica de negocio depende de XML como estructura de almacena-

<< dotNetManía

<< Ese concepto que nos ayuda a buscar la definición

17


<< dnm.plataforma.net je de datos, dejando la implementación en una capa de encapsulación dentro de un objeto). Pero tenemos un problema: cada informe tiene un aspecto potencialmente diferente. Nuestra aplicación debe mostrar un menú con los informes disponibles… pero claro: a) ¿Qué informes han sido ya implementados? b)¿Va a haber más informes? c) ¡Necesitaremos muchas recompilaciones! d)Los informes seguramente cambien mucho de aspecto…¡tendremos que refactorizar de nuevo, y recompilar, y redistribuir!...una locura. Solución. Un poco de UML aclarará la historia. El problema radica en que Figura 1. UML de nuestra aplicación

<<dotNetManía

Usa las cosas cuando las necesites, y estrictamente cuando las necesites, y libéralas cuando ya no las necesites...

18

nuestra aplicación sabe que tiene que mostrar informes pero ni cuáles ni cómo. Nuestros informes saben lo que tienen que hacer pero no cuando. Por tanto: a) Nuestra aplicación requiere tener claro qué entiende por informe. b)Cada informe tiene que ser una implementación específica de lo que la aplicación entiende por informe. Habrá tantos componentes como distintos tipos de informes tengamos ahora, o en un futuro. c) Que la aplicación debe cargar los componentes en tiempo de ejecución (momento en el cual el usuario sabe lo que quiere), y para ello debe asegurarse de que las DLL que debe cargar, cumplen con la especificación de que son informes. Y puesto que la carga se hace en tiempo de ejecución es un momento ideal para aplicar en el acto nuevos cambios de versión, nuevos informes, etc.

Si -como decía- aplicamos un poco de UML, esto puede quedar tal y como vemos en la figura 1. Este análisis quiere decir: a) InterfazInforme representa el concepto del informe que tiene nuestra aplicación. Es un tipo de objeto que debe tener la operación de Mostrar(). b)Que los objetos InformeEmpleados e InformeProductos son dos clases que implementan la especificación del interfaz. Es decir, deben codificar el método Mostrar(). c) Cargador es una clase responsable de la carga dinámica de los informes de tipo InterfazInforme. d) GestorInformes es el contenedor responsable de mostrar los informes. Nuestra aplicación principal. Por lo tanto, estará compuesto de una agregación de n-diálogos. Un refinamiento. Puesto que es una aplicación Windows, requeriremos que sea una aplicación de tipo MDI. Por ello, especificaremos como parámetro de todo informe, el que podamos elegir quién es el padre del informe. Por esto, el interfaz obligará a pasar como parámetro a mostrar, una referencia a una ventana que actuará como padre del documento MDI Hijo. El explorador de soluciones de nuestro proyecto. Tal y cómo aparece en al figura 2 es cómo queda nuestro

proyecto. Hubiera estado bien acabar el UML, pero no hay espacio suficiente en disco… je, je, je.

Figura 2. Nuestra solución en acción

La implementación Los componentes de negocio Ahora viene la parte divertida: codificar el sistema. Empezaremos por el interfaz y las clases de informe. Estarán


<< dnm.plataforma.net Imports System.Windows.Forms Public Interface InterfazInforme Sub Mostrar(ByVal padre As Form) End Interface

Fuente 1. Proyecto CoInterfazInforme (referencia a System.Windows.Forms.Dll)

almacenadas cada una de ellas, en un proyecto de tipo "librería de clases", quedando como en el fuente 1. Ahora vienen los componentes con los diálogos de los informes. Y al loro que aquí hay truco. Crearemos un pro-

cambia el aspecto del diálogo y su código SQL. Ver fuente 3. El aspecto de nuestra DLL CoInformeProductos queda tal y como lo podemos ver en la figura 4.

El programa principal Para terminar con nuestra aplicación, sólo nos queda implementar el programa principal. Éste tiene como misión preparar de forma dinámica un menú de opciones que debe mostrarnos

Figura 4. El aspecto gráfico de InformeProductos

Imports System.Data.SqlClient Public Class InformeEmpleados Inherits System.Windows.Forms.Form Implements CoInterfazInforme.InterfazInforme ‘… se obvia la parte de código de diseño del formulario … Public Sub Mostrar(ByVal padre As System.Windows.Forms.Form) _ Implements CoInterfazInforme.InterfazInforme.Mostrar Me.MdiParent = padre Me.Show() End Sub

los diferentes tipos de informes de los que dispondrá el usuario. Para ello, necesitaremos especificar de alguna manera la relación de informes disponibles, y qué mejor forma de hacerlo que empleando un poco de XML. Dispondremos pues de un fichero de inicio XML que documentará qué

Private Sub InformeEmpleados_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim conexion As New SqlConnection Dim adaptador As _ New SqlDataAdapter(“select firstname,lastname,title from employees”, _ conexion) Dim datos As New DataSet(“DATOS”) conexion.Open() adaptador.Fill(datos, “Empleados”) conexion.Close()

Fuente 2. Proyecto CoInformeEmpleados (referencia System.Windows.Forms.Dll y CoInterfazInforme)

yecto del tipo "Biblioteca de clases", pero quitaremos la clase que genera la plantilla y añadiremos al proyecto un objeto de tipo Windows Form. Esto genera el código de un formulario (¡dentro de la DLL!). Y como todos pensamos ya, será este formulario el que extenderá nuestro interfaz de tipo Informe. Quedando el código como el del fuente 2. Siendo el aspecto gráfico de esta DLL algo del estilo a lo que vemos en la figura 3. De manera similar, creamos el componente del segundo informe. Sólo

Figura 3. El aspecto gráfico de la clase InformeEmpleados

Figura 5. Nuestro programa principal: un formulario IsMDIContainer y un Menu...

opciones tenemos y qué componentes deben ejecutarse como fruto de la selección de dicha opción. Para ello dejaremos el formulario principal tal y cómo se muestra en la figura 5. Antes de comenzar es importante que marquéis una referencia al proyecto del interfaz: CoInterfazInforme. Y sólo a él; los demás componentes no son necesarios. El sistema los cargará en tiempo de ejecución. Todo el follón comienza en el evento Load del formulario. Aquí cargaremos la lista de opciones y prepararemos las opciones dinámicas. Para ello emplearemos el pedazo objeto DataSet y convertiremos el XML en un contenedor

<<dotNetManía

DataGrid1.DataSource = datos.Tables(“Empleados”) End Sub End Class

19


<< dnm.plataforma.net Imports System.Data.SqlClient Public Class InformeProductos Inherits System.Windows.Forms.Form Implements CoInterfazInforme.InterfazInforme ‘… se obvia la parte de código de diseño del formulario … Public Sub Mostrar(ByVal padre As System.Windows.Forms.Form) _ Implements CoInterfazInforme.InterfazInforme.Mostrar Me.MdiParent = padre Me.Show() End Sub Private Sub InformeProductos_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim conexion As New SqlConnection(“server=(local);database=Northwind;”& _ “integrated security=false;” & _ “user id=sa;password=password”) Dim comando As _ New SqlCommand(“select productname,unitprice from Products”, _ conexion) conexion.Open() Dim datos As SqlDataReader = comando.ExecuteReader() ListView1.Items.Clear() While datos.Read() ListView1.Items.Add(String.Format(“{0} [{1} Euros]”, _ datos(“ProductName”), _ datos(“UnitPrice”)), 0) End While datos.Close() conexion.Close() End Sub

a) .NET carga nuevos tipos de datos a partir de librerías de componentes. b)Una vez cargado el tipo, éste se mantiene en el espacio de nombres de la aplicación todo su ciclo de vida. c) Por lo tanto, la carga de la librería sólo hay que hacerla una vez. El resto del tiempo, nos limitaremos a pedirle tipos. Activator es esa clase de .NET que nos permite poder jugar con el milagro de la carga "en caliente" de nuevas librerías de objetos. Obviamente, cuando cargamos el tipo por primera vez, debemos aislarlo de la identificación de la librería como tal, de ahí que pasemos antes por ObjectHandle. Al final, lo único que buscamos es el unboxing del tipo

End Class

Fuente 3. Proyecto CoInformeEmpleados (referencia System.Windows.Forms.Dll y CoInterfazInforme)

de opciones: Titulo, Componente y Tipo. En la implementación ¿qué técnicas se van a usar?: Crear dinámicamente un menú y asociarle un delegado en tiempo de ejecución que responda al even-

y que es responsable de la carga dinámica de clases lo puedes ver en el fuente 5. La explicación a este código es: Creamos un objeto DataView para encontrar la fila que coincide con la

que ya conocemos pero que no sabemos cómo funciona: el tipo InterfazInforme, que nos permitirá activar el diálogo oportuno de acuerdo a nuestras reglas: un interfaz MDI que tiene un padre, que es la ventana principal.

Private Sub Form1_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load dsConfig = New DataSet(“OPCIONES”) dsConfig.ReadXml(“opciones.xml”) ‘Renderizar el menu For Each fila As DataRow In dsConfig.Tables(“opcion”).Rows Dim opcion As New MenuItem(fila(“Titulo”), _ New EventHandler(AddressOf OnMenuManejador)) MainMenu1.MenuItems(0).MenuItems.Add(opcion) Next End Sub

El despliegue

<<dotNetManía

Fuente 4.

20

to OnClick . Quedando el código de OnLoad cómo puedes ver en el fuente 4. Donde el manejador genérico que hemos creado para cada opción de menú,

Figura 6. La vista del explorador de soluciones con todo listo...

opción de menú seleccionada. A continuación viene el proceso de la carga dinámica de clases, para la cual hay que tener en cuenta:

El despliegue es sencillo. Debemos colocar todo "parcialmente" junto. ¿Por qué digo esto? pues porque las DLLs pueden estar donde nos dé la gana; desde el mismo directorio del ejecutable como en un directorio virtual bajo el IIS. .NET Framework está más que capacitado para eso.


<< dnm.plataforma.net Private Sub OnMenuManejador(ByVal sender As System.Object, _ ByVal e As System.EventArgs) ‘Buscar la opcion seleccionada en la base de datos Dim vistaOpcion As New DataView(dsConfig.Tables(“opcion”)) vistaOpcion.RowFilter = “Titulo=” + CType(sender, MenuItem).Text ‘Cargar dinamicamente la clase Dim InformeImpl As CoInterfazInforme.InterfazInforme Try InformeImpl = CType(Activator.CreateInstance(vistaOpcion.Item(0)(“tipo”)), _ CoInterfazInforme.InterfazInforme) Catch ex As Exception Dim obj As System.Runtime.Remoting.ObjectHandle = _ Activator.CreateInstanceFrom( _ vistaOpcion.Item(0)(“componente”), _ vistaOpcion.Item(0)(“tipo”)) InformeImpl = CType(obj.unwrap, CoInterfazInforme.InterfazInforme) End Try InformeImpl.Mostrar(Me) End Sub

Fuente 5.

Figura 7. La ejecución de nuestro sistema de informes

La Ejecución Pues nada, sólo me queda mostraros la captura del sistema funcionando y enseñándoos lo que se puede hacer con un poco de paciencia y un buen análisis. El código fuente os lo dejo en la web de dotNetManía y lo tenéis todo organizado como sólo el Visual Studio lo puede hacer ...

La conclusión Ya hemos visto cómo funciona la carga dinámica de clases. Pero me quedan dos coletillas. En primer indicar lo que

[

Nota a tener en cuenta

]

El lector debe recordar que cualquier cambio que realice en los componentes de informes, deben obligar a redistribuir las DLLs afectadas. Esto es así porque nuestro programa principal en realidad no sabe que dispone de esos componentes. Esto es bueno por un lado, pero complica el versionado de código por otro. ¿Y para qué está WindowsUpdate? Para ofrecer un entorno de gestión de cambios en los sistemas de Microsoft.Ahora, que un InformesUpdate lo dejamos para otro artículo… A fin de cuentas, no es más que un "instalar con Copy" ¿no?.

alguno estará pensando… ha puesto la conexión dentro de los eventos de los diálogos en lugar de abrirla y compartirla. Pero en mi defensa diré que .NET proporciona pool de conexiones, por lo tanto no tengo que preocuparme de ello,

nombres y no se puede desligar la DLL. Alternativas a esta arquitectura: los dominios de aplicaciones, Remoting, Serviced Components y/o Servicios Web. ¡Quién da más!... Pero lo dejaremos para otros artículos.

<?xml version=”1.0” encoding=”utf-8” ?> <OPCIONES> <OPCION> <TITULO>Informe de empleados</TITULO> <COMPONENTE>informes/CoInformeEmpleados.dll</COMPONENTE> <TIPO>CoInformeEmpleados.InformeEmpleados</TIPO> </OPCION> <OPCION> <TITULO>Informe de productos</TITULO> <COMPONENTE>informes/CoInformeProductos.dll</COMPONENTE> <TIPO>CoInformeProductos.InformeProductos</TIPO> </OPCION> </OPCIONES>

Fuente 6.

<<dotNetManía

En nuestro ejemplo, y por simplificar las rutas, lo pondremos todo junto. Pero, mejor, en un subdirectorio del EXE porque en un futuro pueden llegar más DLLs y no queremos mezclar las cosas. ¡Abajo el infierno de las DLLs! Eso sí, en el mismo directorio del EXE necesitaremos un sencillo XML que documente las opciones del menú y apunte a los componentes de los informes. Ver fuente 6.

y es más, cumplo con todas las reglas de la orientación a objetos: usa las cosas cuando las necesites, y estrictamente cuando las necesites y libéralas cuando no las necesites. En segundo lugar, que nuestro programa principal es tan flexible que ya no debemos tocarlo. Sólo extenderlo ampliando el modelo de componentes y configurando apropiadamente el XML. De acuerdo que hay pequeños flequillos que se podrían optimizar..., pero para eso ya estáis vosotros. Por mi parte no quería complicarlo más con adornos. Espero que estéis de acuerdo. Para terminar no puedo evitar pensar que alguien puede comentar: ¡jope!, ¿por qué no se libera la DLL en lugar de estar todo el tiempo cargada? ¡Qué desperdicio! Ante esto, sólo comentaros que imagino que es una decisión de los ingenieros de Microsoft. Una vez cargado el tipo, éste queda cargado en el espacio de

21


Por Guillermo ‘Guille’ Som Visual Basic MVP desde 1997 elguille.info

Yo clasifico, tú clasificas... ¡que clasifique él! Cómo implementar clases que sean clasificables por .NET Framework

ticularmente algunos tipos de colecciones, que nos permiten clasificar los elementos contenidos en ellas mediante el método Sort, pero debido a que el contenido de dichas colecciones pueden (y suelen) ser de tipos de datos dispares, entre ellos los creados por nosotros, tenemos que darle algún tipo de información para que pueda realizar una clasificación que esté acorde con el tipo de datos contenido en la colección; en este artículo veremos cómo podemos hacerlo.

<<dotNetManía

Clasificar al estilo .NET

22

Los diseñadores de las clases de .NET Framework, entre otras cosas, sabían que los programadores de este entorno iban a necesitar funciones de clasificación de elementos; también sabían que posiblemente cada programador necesitara crear sus propias clases, las cuales no tenían porqué estar derivadas de otras ya prefabricadas que podrían estar preparadas “de fábrica” para que fuesen clasificables. Sabiendo todo esto lo que acordaron fue proporcionar de cierta característica a las clases para que se pudieran clasificar. De esta forma le daban al programador la libertad de poder definir sus clases como mejor les pareciera, pero para que todo funcionara bien, estos atrevidos programadores debían seguir unas pequeñas normas, así podrían ser libres de crear las clases a su antojo; eso sí, las clases que se fuesen a usar para ser clasificadas (y que, por tanto, formasen los elementos de una colección, o lista de objetos, que a priori serían de un tipo "indefinido", ya que sólo

cada programador sabrá en cada ocasión y momento cúal es la clase que usará para que sea clasificada) deberían seguir unas sencillas normas de conducta. Esas normas las impone una interfaz; como sabemos, al usar una interfaz lo que hacemos es firmar una especie de contrato en el que

el objeto con el que se compara, que no es ni más ni menos que el propio objeto que implementa dicho método. ¿Qué se consigue con todo esto? Que podamos tener la libertad de poder crear nuestros propios objetos "clasificables", de forma que, sabiendo qué es lo que con-

Las clases que se vayan a usar para ser clasificadas deberán seguir unas sencillas normas de conducta. Esas normas las impone una interfaz. nos comprometemos a usar de forma adecuada los miembros definidos en dicha interfaz. Una interfaz es una especie de clase abstracta que simplemente nos indica los métodos (o miembros) que nuestras clases deben implementar para que el contrato se formalice. Las clases/colecciones que implementan el método Sort (para clasificar), lo que hacen es usar el método CompareTo que debe estar implementado en las clases que se utilicen como elementos de una colección "clasificable". Para que dicho método CompareTo funcione como debe, ha de cumplir ciertas normas impuestas por la interfaz IComparable, cuyo único miembro es precisamente el método CompareTo, el cual recibe un parámetro de tipo Object (el tipo más genérico posible) y debe devolver un valor dependiendo de si ese objeto es menor, mayor o igual que

tiene nuestro objeto, seamos libres de poder decidir cómo clasificarlos. Ya que no es lo mismo clasificar objetos de tipo String, Integer, etc., que clasificar, por ejemplo, objetos del tipo Cliente o CuentaBancaria. En los primeros no hay duda que se usará para clasificar el propio contenido de dichos objetos, pero en los tipos de datos que nosotros definamos, puede que no esté tan claro en qué debemos basarnos para realizar dicha clasificación. Por tanto, en esos casos seremos nosotros los que definamos qué es lo que tendremos en cuenta a la hora de clasificar o para ser más precisos: qué es lo que debemos tener en cuenta para, teniendo sólo dos elementos, decidir cual debe ir en primer lugar y cual debe ir en segundo lugar. Ya que esto es lo que realmente hace .NET, comparar dos objetos y así decidir cual de ellos debe ir antes.

<< dotNetManía

<< Hay clases de .NET Framework, par-

22


<< dnm.plataforma.net

Fuente 1. La clase Colega.

¿Cómo definir una clase que sea "clasificable"? Para entender todo este galimatías, lo mejor es ver un caso práctico en el que tengamos una clase y queramos que se pueda usar, por ejemplo, como elemento de una colección ArrayList o cualquier otra que permita clasificar sus elementos. Primero definiremos la clase; en este caso se llamará Colega y que, para simplificar, sólo tendrá dos propiedades: Nombre y Apellidos. También redefiniremos el método ToString, que es el método que algunas clases de .NET Framework utilizan cuando se quiere mostrar el contenido de un objeto, de forma que nuestra implementación del método ToString devuelva los apellidos y el nombre que contiene el objeto de la clase Colega.

Para facilitar la tarea de crear nuevos objetos, hemos definido un constructor al que se le puede pasar como parámetros el nombre y los apellidos del nuevo objeto que queremos crear (ver fuente 1). Si utilizamos objetos de esta clase como elementos de una colección, por ejemplo, del tipo ArrayList y pretendemos clasificar su contenido, recibiremos una excepción indicándonos que no es posible clasificarlos, que .NET no sabe cómo clasificar objetos del tipo Colega. Por ejemplo, podríamos tener código del fuente 2 para usar los objetos de la clase Colega y añadirlos a una colección del tipo ArrayList. El error que se producirá no es demasiado aclaratorio aunque nos puede dar una pista: "Información adicional: Specified IComparer threw an exception." Es decir, la interfaz IComparer ha lanzado una excepción. Esto nos puede hacer pensar que realmente la interfaz que nuestra clase debe implementar es IComparer, pero no, la interfaz que nuestra clase Colega debe implementar para que sea clasificable es IComparable, que es la que al fin y al cabo utiliza el método Sort para clasificar los elementos de la colección. Por tanto, para que nuestra clase Colega pueda ser clasificada debemos implementar la interfaz IComparable. Si usamos una interfaz en nuestras clases, debemos implementar cada uno de los miembros de dicha interfaz, en el caso de VB, la implementación de los miembros de una interfaz llega aún más lejos, ya que de forma explícita hay que indicar que lo que queremos es precisamente definir un método que está ligado con una interfaz. Veamos el código que habría que añadir a la definición que hemos hecho de la clase Colega mostrada en el fuente 1. Justo después de la definición de la clase añadiremos Implements IComparable. Con esto le estamos indicando al compilador que nuestra clase quiere firmar un contrato para poder usar los miembros definidos en la interfaz IComparable: Public Class Colega Implements IComparable

A continuación debemos definir el método CompareTo, el cual recibe como parámetro un objeto de tipo Object que será el que debemos usar para comparar

‘una colección del tipo ArrayList Dim al As New ArrayList ‘Agregamos algunos datos al.Add(New _ Colega(“Pepe”,“Ruiz”)) al.Add(New _ Colega(“Pepe”,“Lopez”)) al.Add(New _ Colega(“Pepe”,“Sancho”)) al.Add(New _ Colega(“Maria”,“Ruiz”)) al.Add(New _ Colega(“Maria”,“Castillo”)) al.Add(New _ Colega(“Maria”,“Rodriguez”)) ‘los clasificamos al.Sort() ‘los mostramos For Each c As Colega In al Console.WriteLine(c) Next Fuente 2. El código de ejemplo para usar y clasificar los objetos del tipo Colega

con nuestro objeto, es decir debemos comparar dicho parámetro con la instancia que se ha creado de nuestra clase Colega. Empecemos viendo la definición de dicho método: Public Function CompareTo( _ ByVal obj As Object)_ As Integer Implements_ System.IComparable.CompareTo

Como podemos comprobar, es una función que devuelve un valor de tipo Integer, que recibe como parámetro un objeto del tipo Object y que, de forma explícita, indica que está implementando el método CompareTo de la interfaz IComparable. El problema con el que nos encontramos es que el parámetro es de tipo Object, no del tipo Colega, (de este detalle nos ocuparemos en el siguiente párrafo), para devolver el valor de la comparación nos apoyaremos en otra función, la cual devolverá cero si los dos objetos son iguales, menor que cero si la instancia actual es menor que la que se indica en el parámetro y mayor que cero si la instancia actual es mayor que la indicada en el parámetro. Para poder comparar la instancia actual del tipo Colega con la indicada en

<<dotNetManía

Public Class Colega Private _nombre As String Private _apellidos As String ‘ Constructores Public Sub New() End Sub Public Sub New(_ ByVal elNombre As String, _ ByVal losApellidos As String) _nombre = elNombre _apellidos = losApellidos End Sub ‘ Public Property Nombre() As String Get Return _nombre End Get Set(ByVal value As String) _nombre = value End Set End Property ‘ Public Property Apellidos() _ As String Get Return _apellidos End Get Set(ByVal value As String) _apellidos = value End Set End Property ‘ Public Overrides Function _ ToString() As String Return _apellidos & “, “ & _nombre End Function End Class

23


<< dnm.plataforma.net el parámetro del método CompareTo, debemos suponer (o dar por sentado) que dicho parámetro realmente es otro objeto del tipo Colega, pero como .NET usa el tipo Object de forma genérica para indicar cualquier tipo de objeto, (con idea de que el mismo método sirva para cualquier tipo de clase, cosa que cambia en la versión 2.0 de .NET Framework y el uso de generics y que podrás ver en el próximo número de dotNetManía), por tanto lo que debemos hacer es una conversión del tipo Object a nuestro propio tipo, en este caso Colega. Para ello usaremos la instrucción CType o DirectCast de forma que podamos tener una clase adecuada al tipo que queremos comparar: Dim c1 As Colega = CType(obj, Colega)

Ahora la variable c1 será la "versión" de tipo Colega del parámetro que ha recibido el método CompareTo. Una vez que ya tenemos 2 objetos del mismo tipo (uno el que representa a la instancia que implementa el método CompareTo y otro el que se ha pasado como parámetro a esta función) podemos comprobar cual es mayor. En lugar de hacer tres comparaciones para saber si son iguales, mayor o menor, vamos a usar el método estático (o compartido) Compare de la clase String que se encargará de devolver el valor adecuado: Return String.Compare( _ Me.ToString, c1.ToString)

En este caso hemos usado el valor devuelto por el método ToString, ya que dicho método lo que hace es devolver el apellido seguido del nombre, por tanto será totalmente válido para realizar la comparación que indicará si un objeto es mayor o no que otro. Por supuesto en el valor devuelto puedes usar la comparación que creas oportuno, si sólo quieres clasificar teniendo en cuenta los apellidos podrías hacer esto:

<<dotNetManía

Return String.Compare(_apellidos, _ c1.Apellidos)

24

En el caso de que quieras que se clasifiquen de forma descendente, puedes invertir el orden de los valores que le pasamos a String.Compare:

Return String.Compare(c1.Apellidos, _ _apellidos)

¿Qué ocurre si quiero clasificar elementos que no han sido preparados para ser clasificados? Pero se nos puede dar el caso en el que queremos usar una clase que no haya sido preparada como clasificable pero que nos interese que sí lo sea y, para complicar la situación, de la que no podamos derivar nuevas clases ni de modificar el comportamiento interno de dicha clase. En estos casos, podemos echar mano de una de las sobrecargas del método Sort, en la que se puede indicar una clase que implemente la interfaz IComparer y que reciba como parámetros dos objetos del mismo tipo del que queremos clasificar. En este caso podríamos crear una nueva clase que implemente dicha interfaz y el único método que tendrá la clase será el método Compare, el cual recibirá dos objetos por parámetro del tipo contenido en la colección y que es el que queremos clasificar. Public Class ColegaComparer Implements IComparer ‘ Public Function Compare(_ ByVal x As Object, _ ByVal y As Object)_ As Integer Implements _ System.Collections.IComparer.Compare Dim c1 As Colega = _ CType(x, Colega) Dim c2 As Colega = _ CType(y, Colega) ‘ Return String.Compare( _ c1.ToString, c2.ToString) End Function End Class Fuente 3. La clase ColegaComparer para usarla para clasificar elementos tipo Colega

Suponiendo que tenemos la definición de la clase Colega del fuente 1, (a la que aún no habíamos añadido la implementación de la interfaz IComparable), y son objetos de esa clase los que queremos usar para añadir a la colección del tipo ArrayList, pero queremos que puedan ser clasificados, tendremos que crear una clase que implemente la interfaz IComparer

y cuyo método Compare haga una comparación entre dos elementos del tipo Colega. (Ver el fuente 3 para mayor claridad). Como vemos, esta clase sólo tiene un método que es el que se define en la interfaz IComparer. Para usar esta clase junto con el método Sort de la colección ArrayList tendremos que hacer: Dim miComparer As New ColegaComparer al.Sort(miComparer)

Es decir, pasamos como parámetro un objeto creado de la clase que sabe cómo clasificar dos objetos del tipo que estamos usando como elemento de la colección. Fíjate que realmente el objeto pasado como parámetro al método Sort, no tiene ninguna relación directa con ninguno de los objetos que contiene la colección; simplemente .NET Framework lo usará pasándole como parámetros al método Compare dos objetos del tipo Colega y dicho método se encargará de devolver el valor adecuado.

Comprobar que el tipo a clasificar es del tipo adecuado En estos ejemplos no se ha usado ningún tipo de validación de datos correctos, es decir, hemos dado por supuesto que todo el contenido de la colección son objetos del tipo Colega, pero si por alguna casualidad, de esas que nunca se suelen producir, la colección contuviera elementos de distintos tipos, el código del fuente 2 así como el modificado para usar la interfaz IComparer, fallaría al intentar convertir un objeto que no es del tipo Colega al tipo Colega. En estos casos, lo mejor es utilizar un Try/Catch para curarnos en salud. Que se produce un error al convertir el parámetro, pues en lugar de dejar que se quede "parada" la aplicación, podemos devolver un valor cero, indicando que "no sabemos" cúal es menor, por tanto asumimos que son iguales. Por supuesto que aquí también podríamos tener en cuenta otros tipos de objetos, por ejemplo, que alguno de ellos sean del tipo String o de algún otro tipo “conocido” por nosotros; en esos casos podríamos hacer una comprobación del tipo de datos que recibimos como parámetro antes de realizar la conversión (o cast).


Por Mario del Valle y Miguel Katrib Grupo Weboo Universidad de La Habana

Enumeradores e Iteradores en C# y C# 2.0 El patrón Enumerador en C#

<<

En este trabajo se ilustra la utilidad de los enumeradores y del ciclo foreach en C#. Se analizan cuáles son las limitaciones de estos enumeradores que justifican la importancia de la nueva inclusión de iteradores en el venidero C# 2.0. Se ejemplifican las ventajas de estos iteradores en C# 2.0 para escribir código más elegante, legible y menos propenso a errores. Finalmente se proporciona una implementación de iteradores por medio de hebras que, además de ilustrar la utilización de las hebras, ofrece una forma concreta de usar iteradores en el actual C#.

interface IEnumerator{ bool MoveNext(); object Current{get;} void Reset(); }

De este modo si una clase C implementa IEnumerator class C: IEnumerator{ … }

Entonces sobre un objeto c de tipo C se puede hacer la siguiente iteración C c = new C(); … while (c.MoveNext()) { … Process c.Current }

El método Reset permite que haciendo c.Reset() se deje disponible a c para una nueva iteración. En este sentido una instancia de C hace las veces de “colección” porque se pueden recorrer “los elementos de C” siguiendo el patrón anterior.

Colecciones “Virtuales” La colección de elementos a recorrer a través del enumerador puede ser “virtual”, es decir

<<dotNetManía

<<

En C# si una clase quiere permitir una iteración sobre elementos contenidos en una instancia de la misma, o "producidos" de algún modo a partir de dicha instancia, la clase debe implementar el patrón enumerador. Este patrón está tipificado en .NET por la interface IEnumerator.

25


<< dnm.lenguajes.net los elementos que se procesan como resultado de la iteración no tienen que estar físicamente contenidos en una estructura contenedor (sea un array, una lista, etc). Por ejemplo con la siguiente clase se permitiría recorrer los enteros pares de un intervalo. class EvenEnumerator: IEnumerator { bool moveOK; int currentValue, lower, upper; public EvenEnumerator(int lowerBound, int upperBound) { if (lowerBound > upperBound) throw new ArgumentException( “Wrong interval”); if (lowerBound % 2 != 0) lower = lowerBound-1; else lower = lowerBound – 2; upper = upperBound; Reset(); } public bool MoveNext() { if (currentValue > upper) return false; currentValue = currentValue+2; moveOK = currentValue<=upper; return moveOK; } public object Current{ get { if (moveOK) return currentValue; else throw new InvalidOperationException( “There is no current element”); } } public void Reset() { moveOK = false; currentValue = lower; }

<<dotNetManía

}

26

Para apreciar mejor la utilidad del recurso de iteración que se propondrá más adelante es importante comprender este concepto de que los elementos que se recorren en una iteración no tienen que haber sido previamente almacenados físicamente en alguna parte. 1

El ciclo foreach Asociado al patrón enumerador C# ofrece un recurso importante: el ciclo foreach. Si una clase Items implementa la interface IEnumerable interface IEnumerable{ IEnumerator GetEnumerator(); } class Items: IEnumerable{ … }

O simplemente implementa un método de signatura IEnumerator

foreach(object x in list) { ... Process x }

pues nos protegemos de errores en la inicialización de la variable de control del ciclo for (derecha), la pregunta de control y el incremento de la variable. El “azúcar sintáctico” que nos ofrece este ciclo foreach aumenta la productividad, previene cometer errores por un uso inadecuado de los patrones tradicionales y favorece la escritura de un código más elegante. Sin embargo, desafortunadamente C# no introduce una notación similar para ser utilizada en expresiones lógicas (de tipo bool). Podría ser muy útil disponer de dos operadores lógicos como:

IEnumerator e = list.GetEnumerator(); while(e.MoveNext()) { object x = e.Current; ... Process x; } Tabla1. Código generado para el ciclo foreach

GetEnumerator() entonces sobre un objeto list de tipo Items se puede hacer

un recorrido como se denota en el código a la izquierda (tabla 1) para el cual el

T[] list = new T[10]; foreach (x in list) { ... Process x ... }

forall (object x in list): <expresión bool que usa a x> exist (object x in list): <expresión bool que usa a x>

T[] list = new T[10]; for (int i=0; i<list.Length; i++) { ... Process list[i] } Tabla2. Ciclos foreach y for sobre un array

compilador lo expandirá en un código equivalente al código a la derecha. Los arrays (la familiar construcción primitiva para almacenar colecciones de elementos) pueden ser tratados también con un ciclo foreach (Tabla 2 código a la izquierda) en lugar del tradicional ciclo for (Tabla 2 código a la derecha) De este modo si no nos interesase la posición de los elementos dentro del array es más cómodo y seguro recorrerlos con el ciclo foreach (izquierda)

Tales recursos, unidos a las capacidades de atributos, reflection y CodeDom podría aumentar las capacidades de C# como lenguaje no sólo de implementación sino también de especificación1.

Limitaciones del patrón enumerador Implementar un recorrido con el patrón enumerador es relativamente simple cuando se hace un recorrido secuencial sobre un contenedor como

La inclusión de un recurso de este estilo será tratado por los autores en un próximo trabajo para incluir aserciones lógicas en C#.


<< dnm.lenguajes.net

class Tree { object nodeValue; public ArrayList Nodes { get{…} } }

Si se quisieran listar en preorden los elementos de un árbol se podría incluir dentro de la clase un método ListInPreOrder. class Tree { object nodeValue; public ArrayList Nodes { get{…} } public void ListInPreOrder() { ListInPreOrder(this); } private void ListInPreOrder(Tree t) { Console.WriteLine( t.nodeValue.ToString()); foreach (Tree t1 in t.Nodes) ListInPreOrder(t1); } }

Como se puede observar este recorrido usando la recursión es muy simple pero fuerza a que el procesamiento a realizar con los elementos (listarlos en este caso) esté encapsulado dentro de la clase Tree. Para dar más facilidad a que sea el código cliente el que decida qué quiere hacer con los elementos que recorre en un determinado orden, sería deseable tener dentro de la clase Tree un enumerador que nos devuelva los nodos del árbol en preorden. Una solución simple podría ser:

class Tree { object nodeValue; public ArrayList Nodes { get{…} } public IEnumerable NodesInPreOrder { get { ArrayList nodes = new ArrayList(); ListInPreOrder(this, nodes); return nodes; } } private void ListInPreOrder(Tree t, ArrayList nodes) { nodes.Add(t.nodeValue); foreach (Tree t1 in t.Nodes) ListInPreOrder(t1, nodes); } }

Sin embargo, esta solución tiene el inconveniente que obliga a colocar todos los nodos en un contenedor físico (ArrayList en este caso) para luego recorrer dicho contenedor. Esto no es una solución muy eficiente, más aún si es posible que el código cliente (como se muestra a continuación) puede decidir abortar el recorrido, porque en este caso de todos modos el verdadero recorrido de los nodos en el árbol ya se habría realizado. Tree t = new Tree(); ... foreach (object x in t.NodesInPreOrder) { if (some condition on x) break; else ...Process x ... }

Otra solución sería programar directamente el enumerador, pero en este caso habría que implementar el mantener el estado interno del recorrido recursivo. Una implementación para este caso se muestra a continuación. Note la complicación del código porque para conservar el estado de la iteración en preorden, entre una llamada a MoveNext y otra, ha sido necesario utilizar una pila como estructura auxiliar. class Tree { object nodeValue; public ArrayList Nodes { get{…} } public IEnumerable NodesInPreOrder

<<dotNetManía

un array o una lista, o cuando se recorre una colección virtual simple como la del caso de intervalo de números pares ilustrado anteriormente. En cualquier caso la implementación del par MoveNext-Current dentro del enumerador debe mantener el “estado interno” del recorrido entre una llamada a MoveNext y otra, de manera de poder retomar este estado en cada nueva llamada. Mantener tal estado no es nada simple cuando se quiere hacer un recorrido recursivo complicado sobre una estructura como un árbol o para una colección virtual que produce sus elementos por algún algoritmo recursivo (por ejemplo los movimientos de los discos del juego de las Torres de Hanoi, lo que se verá en el ejemplo de la sección final). Considere a continuación una versión simplificada de una clase Tree

27


<< dnm.lenguajes.net { get { return new PreOrderEnumerable(this);} } //Inner class class PreOrderEnumerable:IEnumerable { Tree t; public PreOrderEnumerable(Tree t){this.t=t;} public IEnumerator GetEnumerator() { return new PreOrderEnumerator(t); } class PreOrderEnumerator: IEnumerator { Tree t; Stack s; object current; bool moveOK; public PreOrderEnumerator(Tree t) { if (t==null) throw new InvalidArgumentException( “Null parameter”); this.t=t; s = new Stack(); Reset(); } public bool MoveNext() { if (s.Empty) return false; else { Tree t = s.Pop(); current = t.nodeValue; moveOK = true; if (t.Nodes!=null) for (int k = t.Nodes.Count-1; k>=0, k—) s.Push(t.Nodes[k]); return moveOK; } } public object Current { if (moveOK) return current; else throw new InvalidOperationException( “Enumeration is out of limits”); }

<<dotNetManía

public void Reset() { s.Clear(); s.Push(t); } }

28

} }

2

¡Nueva inclusión de Iteradores en C# 2.0! Inspirados en lenguajes como CLU, Sather y otros, el venidero C# 2.0 [1] incluirá un recurso muy útil de programación: iteradores (iterators)2. Con ello se facilitará la forma en que se itera sobre una colección de elementos a través de una instrucción foreach. Note, con el ejemplo de recorrido de los nodos de un árbol (sección anterior), que dependiendo de la complejidad del algoritmo de recorrido (que puede incluir recursividad), implementar el mecanismo para conservar el estado de la iteración con los métodos MoveNext y Current puede resultar bastante complicado (y por consiguiente menos elegante, menos legible y más propenso a errores). El nuevo recurso de iterador en C# 2.0 permite solucionar inconvenientes como éste. Un iterador servirá de contraparte de un ciclo foreach. Se considerará un iterador a un método que devuelva un IEnumerable o un IEnumerator y que

Inspirados en lenguajes como CLU, Sather y otros, el venidero C# 2.0 [1] incluirá un recurso muy útil de programación: iteradores

incluya en su definición la utilización de una operación especial yield return expresión. Este operador yield return actuará como un return pero con la diferencia de que al volver a invocarse al método, como consecuencia de un nuevo paso en la iteración del ciclo foreach, la ejecución del método empezará a continuación del último yield return. En este sentido el método que incluye el yield return actuará como “corutina” de quien lo llamó. De este modo la definición de un iterador para recorrer el árbol en preorden puede ser class Tree: IEnumerable { object nodeValue; public ArrayList Nodes { get{...} } ... public IEnumerator GetEnumerator() {

También habíamos propuesto una idea similar para incluir iteradores en el lenguaje Eiffel (ver [2], [3])


<< dnm.lenguajes.net

Note el foreach (object x in t) yield return x ; anidado dentro del foreach más externo, aquí estamos usando recursivamente el propio concepto de iterador ahora aplicado a cada uno de los hijos del árbol original.

Más de un iterador para una misma clase Se podrá usar como iterador todo método que devuelva un IEnumerator o un IEnumerable y que incluya un yield. Con esto se supera una limitación anterior en C# que forzaba a estar definiendo clases IEnumerable sólo con la intención de devolver un IEnumerator a través del método GetEnumerator. Suponga, para un ejemplo como el de la clase árbol, que se quieren tener dos formas de iteración: una preorden y otra en postorden. La solución en C# 2.0 es tan simple como: class Tree { object nodeValue; public ArrayList Nodes { get{…} } public IEnumerable PreOrder { get { yield return nodeValue; foreach (Tree t in Nodes) foreach (object x in t) yield return x; } } public IEnumerable PostOrder { get { foreach (Tree t in Nodes) foreach (object x in t) yield x; yield nodeValue; } }

}

De este modo un árbol t se podría recorrer fácilmente de los dos modos: foreach (object x in t.PreOrder)

public IEnumerable PostOrder { get { return new PostOrderEnumerable(this); } } class PostOrderEnumerable:IEnumerable { Tree t;

y foreach (object x in t.PostOrder)

Si queremos que se pueda hacer un recorrido foreach (object x in t) directamente sobre la propia variable árbol, supongamos se asume en este caso el recorrido en preorden como recorrido predeterminado (by default), se puede definir la clase Tree del modo siguiente (note que incluso mantenemos el recorrido con nombre a través de la propiedad PreOrder) class Tree: IEnumerable { object nodeValue; public ArrayList Nodes { get{…} } public IEnumerator GetEnumerator() { yield return nodeValue; foreach (Tree t in Nodes) foreach (object x in t) yield return x; } public IEnumerable PreOrder { get { return this; } } public IEnumerable PostOrder { get { foreach (Tree t in Nodes) foreach (object x in t) yield x; yield nodeValue; } } }

Note la implementación de la propiedad PostOrder en el ejemplo anterior de Tree, en el get hemos incluido directamente el código del iterador. Con esto nos evitamos tener que escribir el par de clases internas PostOrderEnumerable y PostOrderEnumerator como se muestra en el extracto de código a continuación: class Tree: IEnumerable { ...

public new PostOrderEnumerable(Tree t) { this.t=t; } public IEnumerator GetEnumerator() { return new PostOrderEnumerator(t); } } class PostOrderEnumerator:IEnumerator { /* ...latosa implementación de los métodos MoveNext y Current */ } }

Una implementación de iteradores usando hebras Veremos en esta sección como podemos implementar este concepto de iterador utilizando hebras. La solución se basa en utilizar un tipo especial Iterator que implementa a IEnumerable. Para crear un objeto de tipo Iterator hay que ofrecer un objeto delegate del tipo: public delegate void IteratorMethod(IYield yieldObject);

El método que se asocie a este objeto delegate será el que encierre el verdadero proceso de iteración (que puede incluir la complejidad de una recursión). Cuando producto de aplicar el patrón que se muestra en la Tabla 1, se hace el primer MoveNext , éste echará a andar una hebra asociada al delegate anterior. Esta hebra sincronizará con la hebra que la creó (aquella del código cliente que ha desencadenado el ciclo foreach) mediante un objeto especial de tipo IYield que se usará dentro del cuerpo del método del delegate.

<<dotNetManía

yield nodeValue; foreach (Tree t in Nodes) foreach (object x in t) yield return x; } }

29


<< dnm.lenguajes.net public interface IYield { void Yield(object result); } Hacer y.Yield(x) significa poner a x como valor del Current del enumera-

dor y detener la hebra en ese punto hasta que por demanda del código cliente a través del foreach se vuelva a invocar a un próximo MoveNext. El código fuente 1 ilustra cómo incluir en una clase Tree un iterador en preorden. La instrucción en la línea 12

de la hebra hasta que se vuelva a invocar a un próximo MoveNext (ver más adelante el código fuente 2 con la implementación de la clase Iterator) producto de una nueva iteración del código cliente (instrucción línea 30). En el código fuente 2 se muestra la implementación de la clase Iterator, note que el método MoveNext (línea 34) es quien desencadena la hebra cuando es llamado por primera vez al aplicar el código cliente el patrón de la tabla 2.

1. class Tree 2. { 3. object nodeValue; 4. ... 5. public ArrayList Nodes 6. { 7. get{...} 8. } 9. public IEnumerable PreOrder 10. { 11. get 12. {return new Iterator(new IteratorMethod(PreOrderMethod)); 13. } 14. } 15. void PreOrderMethod(IYield y) 16. { 17. PreOrderMethod(y, this); 18. } 19. void PreOrderMethod(IYield y, Tree t) 20. { 21. y.Yield(t.nodeValue); 22. foreach (Tree t1 in t.Nodes) PreOrderMethod(y, t1); 23. } 24. } 25. class TreeTest 26. { 27. public static void Main() 28. { 29. Tree t = ...; 30. foreach (object x in t.PreOrder) 31. Console.WriteLine(x); 32. } 33. }

El método runMethod de la hebra es quien llama al delegate con la instrucción it(this) (línea 30) que es quien realmente hace la iteración y usa al propio objeto this (que es de tipo IYield ) para ejecutar las acciones Yield. El método MoveNext queda en espera de la señal calculatedValue (línea 47) que es enviada por el método Yield cuando se ha calculado un nuevo valor (línea 67) o por el propio método runMethod cuando se ha terminado la iteración (línea 32) y no hay más objetos que calcular para la iteración. El método Reset (línea 72) manda a abortar la hebra. Esto ocurriría en el caso en que el código cliente haga un Reset aún cuando no se haya llegado al final de la iteración. Las hebras son recursos limitados de modo que hay que garantizar que si el código cliente aborta un ciclo foreach (por ejemplo con una instrucción break o return) la hebra de la iteración no quede viva innecesariamente, por esta razón la clase CoroutineEnumerator se ha definido como IDisposable . Note que en la definición de Dispose también se manda a abortar la hebra (línea 77).

<<dotNetManía

Código fuente 1. Iterador PreOrder dentro de un árbol

30

asocia al iterador el delegate formado por el método de la línea 15 (que a su vez llama al método de la línea 19). Note la instrucción y.Yield(t.nodeValue); de la línea 21, el lector debe “imaginar” como si aquí se detuviera la ejecución

IEnumerator e = list.GetEnumerator(); while(e.MoveNext()) { object x = e.Current; ... Process x; }

Figura 1. Movimientos en las Torres

Hanoi para 3 discos


<< dnm.lenguajes.net

14. //Inner class 15. class CoroutineEnumerator:IEnumerator, IDisposable,IYield 16. { 17. IteratorMethod it; 18. Thread itThread; 19. object current; 20. bool finish; 21. object proceedIteration = new object(); 22. object calculatedValue = new object(); 23. public CoroutineEnumerator(IteratorMethod iterator) 24. { 25. this.iterator = iterator; 26. } 27. void runMethod() { 28. finish = false; 29. //Initialize a thread with the handler 30. it(this); 31. finish = true; 32. lock(calculatedValue) {Monitor.Pulse(calculatedValue);} 33. }

50. } 51. public object Current 52. { 53. get 54. { 55. if (finish) throw new InvalidOperationException( 56. "Invalid Current. Out of the collection"); 57. else return current; 58. } 59. } 60. void IYield.Yield(object result) 61. { 62. lock (proceedIteration) 63. { 64. lock (calculatedValue) 65. { 66. current = result; 67. Monitor.Pulse(calculatedValue); 68. } 69. Monitor.Wait(proceedIteration); 70. } 71. } 72. public void Reset() 73. { 74. try 75. { 76. if (itThread.IsAlive) 77. itThread.Abort(); 78. } 79. catch(ThreadAbortException){}; 80. itThread = null; 81. finish = true; 82. } 83. ~CoroutineEnumerator() 84. { 85. Dispose(); 86. }

34. public bool MoveNext() 35. { 87. public void Dispose() 36. lock(calculatedValue) 88. { 37. { 89. if (itThread != null) 38. if (itThread == null) 90. { 39. { try 40. itThread = new Thread(new ThreadStart(runMethod )); 91. 92. { 41. itThread.Start(); 93. itThread.Abort(); 42. } 94. } 43. lock (proceedIteration) 95. catch(ThreadAbortException){}; 44. { 96. itThread = null; 45. Monitor.Pulse(proceedIteration); 97. } 46. } 98. } 47. Monitor.Wait(calculatedValue); 99. } 48. } 100.} 49. return !finish; C贸digo fuente 2. Implementaci贸n de Iterator

<<dotNetMan铆a

1.public class Iterator: IEnumerable 2.{ 3. IteratorMethod it; 4. public Iterator(IteratorMethod it) 5. { 6. if (it == null) 7. throw new ArgumentNullException(); 8. this.it = it; 9. } 10. public IEnumerator GetEnumerator() 11. { 12. return new CoroutineEnumerator(it); 13. }

31


<< dnm.lenguajes.net Un iterador para los movimientos de las Torres de Hanoi

new IteratorMethod(HanoiMethod));} } void HanoiMethod(IYield y) { HanoiMethod(y, disks, source, target, aux); }

El juego de Las Torres de Hanoi consiste en disponer de tres torres (como se ilustra en la Figura 1). En una de las torres hay un conjunto de discos de mayor a menor. Se quieren pasar todos los discos desde una torre origen hacia una torre destino, pudiendo usarse la tercera torre como auxiliar, pero con la restricción de que sólo puede moverse un disco por vez y que nunca se puede poner un disco mayor sobre uno menor. Como se muestra a continuación un método para listar todos los movimientos es muy simple usando recursión void Hanoi(int disks, string source, string target, string aux) { if (disks == 1) Console.WriteLine( “Move from “ + source + “ to “ + target); else { Hanoi(disks-1,source, aux, target); Console.WriteLine(“Move from “ + source + “ a “ + target); Hanoi(disks-1,aux, target, source); } }

void HanoiMethod(IYield y, int disks, string source, string target, string aux) { if (disks==1) y.Yield(“Move from “ + source + “ to “ + target); else { HanoiMethod(y, disks-1,source, aux, target); y.Yield(“Move from “ + source + “ to “ + target); HanoiMethod(y, disks-1,aux, target, source); } } }

Pruebe sin embargo el lector a implementar un IEnumerable que devuelva las cadenas de cada uno de

los movimientos. Note que tendría prácticamente que programar toda la “maquinaria” que el proceso recursivo anterior esconde. La solución utilizando el tipo Iterator es muy simple: class Hanoi { int disks; string target, source, aux;

<<dotNetManía

public Hanoi(int disks, string source, string target, string aux) { this.disks=disks; this.source=source; this.target=target; this.aux=aux; }

32

public IEnumerable Movements { get { return new Iterator(

Figura 2. Movimientos de 3 discos en las Torres Hanoi.

La ejecución del siguiente código listaría lo que se muestra en la figura 2. HanoiIterator hanoi = new HanoiIterator(3, “A”, “B”, “C”); foreach (string s in hanoi) Console.WriteLine(s);

No hay ningún problema en anidar el uso de un iterador dentro de otro, el siguiente código listaría lo que se muestra en la figura 3.


<< dnm.lenguajes.net

HanoiIterator hanoi1 = new HanoiIterator(2, “A”, “B”,”C”); HanoiIterator hanoi2 = new HanoiIterator(2, “Left”, “Rigth”, “Center”); foreach (string s1 in hanoi1) { Console.WriteLine(s1); foreach (string s2 in hanoi2) Console.WriteLine(“—- “ + s2); }

Conclusiones La uniformidad que propone .NET en el uso de enumeradores es importante para el tratamiento de colecciones y es usado ampliamente en sus propias bibliotecas. El aporte del ciclo foreach es una comodidad sintáctica considerable y menos propensa a errores. La anunciada inclusión de iteradores para C#2.0 será muy bienvenida porque aumentará la expresividad y elegancia del código. Sería deseable que se considerase la inclusión de operadores lógicos forall y exists asociados a este concepto de iteradores ya que esto redundaría en beneficio de las capacidades de abstracción y diseño utilizando C#. En C# 2.0 esta capacidad de iteradores se potencia al máximo cuando sea utilizada en combinación con la genericidad, nuevo esperado recurso que también está incluido en C# 2.0. Con genericidad un iterador podrá devolver un objeto de tipo IEnumerator<T> lo que

significa que el compilador estáticamente garantizará que el objeto que devuelva un yield return tenga que ser de tipo T y que los elementos sobre los que se itere en el foreach sean de tipo T. Lo cual redunda en mayor legibilidad, más robustez y más eficiencia al disminuir la necesidad de operaciones

La anunciada inclusión de iteradores para C# 2.0 será muy bienvenida porque aumentará la expresividad y elegancia del código. Sería deseable que se considerase la inclusión de operadores lógicos forall y exists asociados a este concepto de iteradores... de casting en tiempo de ejecución. La genericidad es también un tema muy importante a tratar por futuros artículos de dotNetManía pero que por razones de espacio no ha sido abordado ahora aquí. No creemos que una solución usando hebras, como la que hemos desa-

Referencias [1] The C# 2.0 Specification, http://download.microsoft.com/download/8/1/6/81682478-4018-48fe-9e5ef87a44af3db9/SpecificationVer2.doc [2] Katrib M, Martínez I, Collections and Iterators in Eiffel, Journal of Object Oriented Programming, Vol 6, No 7, Nov/Dec 1993. [3] Coira J, Katrib M, Improving Eiffel assertions using quantified iterators, Journal of Object Oriented Programming, Vol 10, No 7, Nov/Dec 1997. [4] Lowy Juval, Create Elegant Code with Anonymous Methods, Iterators, and Partial Classes, MSDN Magazine, May 2004.

<<dotNetManía

Figura 3. Iteradores anidados.

rrollado en este trabajo, deba ser la solución general de implementación para tener iteradores ya que las hebras son un recurso caro y limitado. Las descripciones sobre cómo será implementado este recurso de iteradores por el compilador de C# 2.0 de VS C# 2005 (ver [1] y [4]) indican que el nuevo compilador de Microsoft para C# 2.0 generará clases anidadas que encapsularán la máquina de estado de la iteración, lo cual es realmente la solución adecuada pero que sólo puede hacerse sobre la base de desarrollar un nuevo compilador. Sin embargo, la implementación mediante hebras que hemos mostrado en este artículo, además de ilustrar la utilización de las hebras, permite que los programadores que no dispongan aún de C# 2.0 puedan mientras tanto irse habituando al útil nivel de expresividad que los iteradores proporcionan, evitando el trabajo y los errores que la complejidad de la programación manual de la maquinaria recursiva de una iteración puede ocasionar. Esta implementación con hebras trabaja de modo síncrono pero pudiera desarrollarse una solución asíncrona. Es decir que, si el código cliente lo desea, entonces el iterador pueda ir calculando asíncronamente el próximo elemento mientras el cliente procesa el anterior lo cual puede ser una alternativa interesante para aplicaciones distribuidas.

33


Por Luis Miguel Blanco Consultant Alhambra-Eidos

Aplicando código y herencia en la configuración visual del control DataGrid Cuando el diseñador visual del control DataGrid no permite que alcancemos nuestros objetivos, es hora de recurrir al código. En este artículo ilustraremos cómo la combinación de código y herencia en la plataforma .NET Framework, aplicadas sobre el mencionado control, nos permitirá extender sus capacidades más allá de las barreras que impone el propio diseñador.

<< Creación por código, un modo alternati-

<<dotNetManía

vo de trabajo con el DataGrid

34

Todos estamos de acuerdo en que el diseñador de formularios es el medio más rápido y fácil para crear un control DataGrid en Windows; sin embargo, existen determinadas situaciones en las que la creación en tiempo de diseño de este control no es adecuada o posible, como por ejemplo, mostrar determinadas celdas del control con valores o configuraciones de color calculados dinámicamente. Ante escenarios de este tipo, y por mucho que nos pese, debemos abandonar las plácidas aguas del diseñador visual, y adentrarnos en los turbulentos mares de la escritura de código para crear nuestro preciado grid. En este artículo abordaremos precisamente esta técnica de creación de controles DataGrid y demás objetos relacionados con el mismo, de forma que podamos observar las diferencias existentes con respecto al medio tradicional, representado por el diseñador de formularios.

ar una fuente de datos que conecte con la tabla Products, de la base de datos Northwind de SQL Server, se mostrará dicha tabla en un DataGrid sobre el que aplicaremos una ligera modificación de aspecto visual, como vemos en el fuente 1. La figura 1 muestra el aspecto del grid obtenido. El objeto DataGrid dispone de una extensa serie de propiedades para modificar su apariencia visual, aunque por cuestiones de simplicidad en este ejemplo sólo hemos utilizado un pequeño subconjunto.

Un DataGrid con algunas sencillas mejoras de imagen

Codificación de estilos para tabla y columna

Primeramente vamos a proceder a la confección de un proyecto de tipo Windows, en el que tras cre-

Cuando manipulamos el control DataGrid con el diseñador, podemos establecer estilos visuales

Figura 1


<<dnm.plataforma.net

Imports System.Data.SqlClient Public Class Form1 Inherits System.Windows.Forms.Form Private oConnection As SqlConnection Private oDataAdapter As SqlDataAdapter Private oDataSet As DataSet Private oGrid As DataGrid '.... '.... Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load ' crear conexión, adapter y dataset oConnection = New SqlConnection("Server=localhost;Database=Northwind;uid=sa;pwd=") oDataAdapter = New SqlDataAdapter("SELECT * FROM Products", oConnection) oDataSet = New DataSet ' llenar el dataset utilizando el adaptador oConnection.Open() oDataAdapter.Fill(oDataSet, "Products") oConnection.Close() ' instanciar objeto DataGrid oGrid = New DataGrid ' añadir a la colección de controles del formulario Me.Controls.Add(oGrid) ' establecer ubicación y tamaño oGrid.Location = New Point(0, 0) oGrid.Size = New Size(Me.Width - 20, Me.Height - 30) oGrid.Anchor = AnchorStyles.Top Or _ AnchorStyles.Left Or _ AnchorStyles.Bottom Or _ AnchorStyles.Right ' establecer el enlace entre el grid y la fuente de datos oGrid.SetDataBinding(oDataSet, "Products") ' modificar configuración visual base del datagrid oGrid.CaptionFont = New Font("Comic Sans MS", 10, FontStyle.Bold) oGrid.CaptionBackColor = Color.DeepSkyBlue oGrid.CaptionForeColor = Color.DarkBlue oGrid.CaptionText = "Listado de la tabla Products" End Sub End Class

para cada tabla y columna de la base de datos que necesitemos mostrar. Un estilo consiste en una configuración personalizada de colores, fuentes, formatos, etc., del elemento sobre el que se aplica, de manera que es posible disponer de combinaciones independientes para las diferentes tablas mostradas por un mismo grid, y lo que es aun más notable, distintas combinaciones de estilo para una misma tabla. Respecto al estilo para la tabla, es necesario instanciar un objeto de la clase DataGridTableStyle, y asignar a su propiedad MappingName una cadena con el nombre de la tabla del DataSet que va a visualizar el DataGrid cuando se utilice este estilo. A continuación asignaremos valores al resto de propie-

dades del estilo relacionadas con el aspecto visual, tales como BackColor , AlternatingBackColor , HeaderFont, etc. Tras el estilo de tabla es necesario también especificar las columnas que van a ser mostradas por dicha tabla. Para esta labor disponemos de la clase abstracta DataGridColumnStyle, que define el comportamiento genérico para crear estilos de columna, y sus clases derivadas DataGridTextBoxColumn y DataGridBoolColumn. Usaremos objetos DataGridTextBoxColumn para crear estilos de columna que visualicen datos de tipo texto, fechas y numéricos. Por otra parte, los objetos DataGridBoolColumn serán adecuados para aplicar a columnas que mues-

<<dotNetManía

Fuente 1

35


<< dnm.plataforma.net tren un valor lógico, puesto que lo que veremos con este tipo de columna será una casilla de verificación marcada o vacía, según el valor de la columna sea verdadero o falso. Estas clases también disponen de la propiedad MappingName, a la que asignaremos el nombre de la columna de tabla que será visualizada en el grid, siendo posible personalizar el título de columna, aplicar un formato al contenido, y otras variadas operaciones, a través del conjunto de propiedades de que disponen. Una vez terminada la creación de los diferentes objetos de estilo de columna, los añadiremos a la colección GridColumnStyles del objeto DataGridTableStyle; e igualmente, una

vez finalizada la creación del estilo de tabla, añadiremos esta a la colección TableStyles del objeto DataGrid . El fuente 2, partiendo del punto en el que dejamos el anterior ejemplo de código, muestra un escenario en el que intervienen las clases que acabamos de comentar.

Figura 2

' crear estilo de tabla para el datagrid Dim oTableStyle As New DataGridTableStyle oTableStyle.MappingName = "Products" oTableStyle.HeaderFont = New Font("Papyrus", 8, FontStyle.Bold) oTableStyle.BackColor = Color.Thistle oTableStyle.AlternatingBackColor = Color.SpringGreen oTableStyle.RowHeadersVisible = False

Tras aplicar este "lavado de cara" al DataGrid, su aspecto será muy distinto del inicial, como vemos en la figura 2.

Aprovechando la herencia para modificar los estilos de columna Si bien la creación por código de columnas para un estilo de tabla nos otorga una mayor flexibilidad, realmente no es una ventaja muy superior con respecto al uso del diseñador, ya que seguimos sin poder manipular plenamente a nuestro capricho algunos aspectos de este tipo de objetos. Supongamos que necesitamos aplicar un color de fondo a una de las columnas del estilo de tabla del DataGrid que estamos desarrollando, por ejemplo ProductName, pero desafortunadamente ninguna de las clases hijas de DataGridColumnStyle nos proporciona una propiedad para establecer tal característica. Actualmente podemos asignar el color pero de forma global al estilo de la tabla, con lo que todas las columnas

' crear columnas para el estilo de la tabla Dim gcsProductID As New DataGridTextBoxColumn gcsProductID.MappingName = "ProductID" gcsProductID.HeaderText = "Código" gcsProductID.Alignment = HorizontalAlignment.Center Dim gcsProductName As New DataGridTextBoxColumn gcsProductName.MappingName = "ProductName" gcsProductName.HeaderText = "Nombre" Figura 3

Dim gcsUnitPrice As New DataGridTextBoxColumn gcsUnitPrice.MappingName = "UnitPrice" gcsUnitPrice.HeaderText = "Precio" gcsUnitPrice.Format = "C2" Dim gcsDiscontinued As New DataGridBoolColumn gcsDiscontinued.MappingName = "Discontinued" gcsDiscontinued.HeaderText = "Disc." ' agregar columnas al estilo oTableStyle.GridColumnStyles.AddRange(New DataGridColumnStyle() _ {gcsProductID, gcsProductName, _ gcsUnitPrice, gcsDiscontinued})

<<dotNetManía

' añadir el estilo al datagrid oGrid.TableStyles.Add(oTableStyle)

36

Fuente 2

mostrarán la misma combinación de colores. Mas no debemos desfallecer en nuestro intento, ya que sortearemos este escollo recurriendo a la herencia, y creando una clase derivada de DataGridTextBoxColumn; en ella reemplazaremos una de las sobrecargas del evento Paint, el cual se produce cada vez que el objeto necesita pintar el valor de un campo correspondiente a la columna de la base de datos con la que está conectado. Así pues, escribiremos la clase DGTxtColColor, y a continuación,


<<dnm.plataforma.net Public Class DGTxtColColor Inherits DataGridTextBoxColumn ' escribir miembros de clase ' para guardar el color personalizado Private mColorFondo As Color Public Property ColorFondo() As Color Get Return mColorFondo End Get Set(ByVal Value As Color) mColorFondo = Value End Set End Property ' cada vez que haya que pintar ' una celda en el grid... Protected Overloads Overrides Sub Paint(ByVal g As System.Drawing.Graphics, _ ByVal bounds As System.Drawing.Rectangle, _ ByVal source As System.Windows.Forms.CurrencyManager, _ ByVal rowNum As Integer, _ ByVal backBrush As System.Drawing.Brush, _ ByVal foreBrush As System.Drawing.Brush, _ ByVal alignToRight As Boolean) ' crear un nuevo objeto Brush con ' el color personalizado y pasarlo ' al método base Dim oBrush As New SolidBrush(mColorFondo) MyBase.Paint(g, bounds, source, rowNum, _ oBrush, _ foreBrush, _ alignToRight) End Sub End Class '-----------------------'.... ' código del formulario ' Dim gcsProductName As New DGTxtColColor gcsProductName.MappingName = "ProductName" gcsProductName.HeaderText = "Nombre" gcsProductName.ColorFondo = Color.Gold

Fuente 3

Si quisiéramos aplicar este mismo efecto sobre una columna con valores lógicos, tendríamos que crear una clase con el código igual que la anterior, pero derivada de DataGridBoolColumn.

Sustituyendo la casilla por un literal Llegados a este punto ya sabemos, tras los ejercicios realizados, que un campo lógico dentro de un DataGrid

<<dotNetManía

modificaremos en el código del formulario la parte correspondiente a la columna ProductName, como se muestra en el código fuente 3. La figura 3 muestra el modo en cómo afecta esta clase a la columna del grid sobre la que es aplicada. Nótese que, independientemente de la configuración de colores del estilo de tabla, la columna correspondiente al objeto DGTxtColColor obliga a la presentación de su propio color.

37


<< dnm.plataforma.net es mostrado como una casilla de verificación. Cuando el grid es editable este comportamiento nos favorece, ya que así podemos modificar el valor del campo, pero si necesitamos presentar los datos como de

sólo lectura, sería más útil mostrar un literal del tipo Verdadero-Falso, Si-No, CorrectoIncorrecto, etc. Al igual que en el apartado anterior, la herencia será nuestra gran aliada para

Public Class DGTxtColLiteral Inherits DataGridTextBoxColumn ' escribir miembros de clase para contener ' los valores de los literales Private msLitVerdadero As String Private msLitFalso As String Public Property LiteralVerdadero() As String Get Return msLitVerdadero End Get Set(ByVal Value As String) msLitVerdadero = Value End Set End Property Public Property LiteralFalso() As String Get Return msLitFalso End Get Set(ByVal Value As String) msLitFalso = Value End Set End Property Protected Overrides Function GetColumnValueAtRow(ByVal source As _ System.Windows.Forms.CurrencyManager, _ ByVal rowNum As Integer) As Object ' obtener el valor de la celda y ' sustituir por el literal Dim oValorCelda As Object oValorCelda = MyBase.GetColumnValueAtRow(source, rowNum) Dim bytValor As Byte bytValor = Convert.ToByte(oValorCelda)

<<dotNetManía

Cuando creamos una columna con una expresión dinámica, la cadena con la expresión debemos pasarla una vez que hayamos añadido la columna al estilo de tabla, y éste último al DataGrid; si no seguimos este orden se producirá un error, ya que la fuente de datos que utiliza el control no estará todavía completamente formada. DataGrid, automáticamente se recorren

todas las filas y columnas de la tabla de datos, por consiguiente, este método es llamado por el propio control al realizar su proceso de muestra de datos. En el código fuente 2 vemos la implementación de esta nueva clase y su aplicación contra la columna Discontinued. En la figura 4 observamos que ahora, la columna Discontinued ya no muestra la

If bytValor = 0 Then Return msLitFalso Else Return msLitVerdadero End If End Function End Class

38

"desfacer este entuerto". En esta ocasión escribiremos una clase hija de DataGridTextBoxColumn, que reemplace el método GetColumnValueAtRow. Tal y como su nombre indica, este método devuelve el valor correspondiente a la celda de la fila en la que estamos posicionados. Cuando se visualiza un

'-----------------------'.... ' código del formulario Dim gcsDiscontinued As New DGTxtColLiteral gcsDiscontinued.MappingName = "Discontinued" gcsDiscontinued.HeaderText = "Disc." gcsDiscontinued.LiteralVerdadero = "VERDADERO-OK" gcsDiscontinued.LiteralFalso = "FALSO-NO VALE" Fuente 4

Figura 4


<<dnm.plataforma.net Public Class DGTxtColExpresion Inherits DataGridTextBoxColumn Private mColumnaExpresion As DataColumn Private mColorExpVerdadera As Color Private mColorExpFalsa As Color ' propiedades para el color de fondo ' según se evalue el contenido de la celda Public Property ColorExpVerdadera() As Color Get Return mColorExpVerdadera End Get Set(ByVal Value As Color) mColorExpVerdadera = Value End Set End Property Public Property ColorExpFalsa() As Color Get Return mColorExpFalsa End Get Set(ByVal Value As Color) mColorExpFalsa = Value End Set End Property Public Sub AgregarColumnaExpresion(ByVal sNombreColumna As String, _ ByVal sExpresion As String) Dim oDataGrid As DataGrid Dim oDataView As DataView ' obtener el DataGrid en el que esta ' columna está contenida oDataGrid = Me.DataGridTableStyle.DataGrid ' obtener la fuente de datos asociada al DataGrid oDataView = CType(oDataGrid.BindingContext(oDataGrid.DataSource, _ oDataGrid.DataMember), CurrencyManager).List ' crear una columna y asignarle la expresión a evaluar mColumnaExpresion = New DataColumn(sNombreColumna) mColumnaExpresion.Expression = sExpresion ' añadir la columna a la fuente de datos del DataGrid oDataView.Table.Columns.Add(mColumnaExpresion) End Sub Protected ByVal ByVal ByVal ByVal ByVal ByVal

oDataRowView As DataRowView oDataRow As DataRow bValorCelda As Boolean oBrushFondo As Brush

' obtener la fila a pintar de la fuente de datos oDataRowView = source.List(rowNum) Fuente 5 (sigue...)

<<dotNetManía

Dim Dim Dim Dim

Overloads Overrides Sub Paint(ByVal g As System.Drawing.Graphics, _ bounds As System.Drawing.Rectangle, _ source As System.Windows.Forms.CurrencyManager, _ rowNum As Integer, _ backBrush As System.Drawing.Brush, _ foreBrush As System.Drawing.Brush, _ alignToRight As Boolean)

39


<< dnm.plataforma.net

oDataRow = oDataRowView.Row ' evaluar el contenido de la expresión ' asociada a la columna que hemos añadido ' dinámicamente a este objeto bValorCelda = CType(oDataRow(mColumnaExpresion.ColumnName), Boolean) ' dependiendo del valor de la columna ' de expresión, seleccionar el color ' que debemos aplicar If bValorCelda Then oBrushFondo = New SolidBrush(mColorExpVerdadera) Else oBrushFondo = New SolidBrush(mColorExpFalsa) End If ' pintar la celda con el color de fondo apropiado MyBase.Paint(g, bounds, source, rowNum, _ oBrushFondo, foreBrush, alignToRight) End Sub End Class '--------------------------' código del formulario '.... ' Dim gcsUnitPrice As New DataGridTextBoxColumn gcsUnitPrice.MappingName = "UnitPrice" gcsUnitPrice.HeaderText = "Precio" gcsUnitPrice.Format = "C2" Dim gcsQPerUnit As New DGTxtColExpresion gcsQPerUnit.MappingName = "QuantityPerUnit" gcsQPerUnit.HeaderText = "Cant. por unidad" gcsQPerUnit.ColorExpVerdadera = Color.LightSkyBlue gcsQPerUnit.ColorExpFalsa = Color.Violet ' agregar columnas al estilo oTableStyle.GridColumnStyles.AddRange(New DataGridColumnStyle() _ {gcsProductID, gcsProductName, gcsDiscontinued, _ gcsUnitPrice, gcsQPerUnit}) ' añadir el estilo al datagrid oGrid.TableStyles.Add(oTableStyle) ' añadir la expresión de columna a evaluar gcsQPerUnit.AgregarColumnaExpresion("ChkPrecio", "UnitPrice < 25")

<<dotNetManía

(...continuación ) Fuente 5

40

casilla de marcado, y en su lugar vemos el literal que hemos asignado al objeto DGTxtColLiteral. Si quisiéramos en estos momentos dar una vuelta más de tuerca a este ejercicio, podríamos ampliar la clase que acabamos de crear y añadirle la funcionalidad de la clase DGTxtColColor, de modo que al mostrarse el literal tuviera un color de fondo para el literal verdadero, y otro para el falso.

Estilos de columna con expresiones En el escenario anterior, el valor reflejado en las celdas de la columna se obtenía tras evaluar el contenido del campo, y en el caso de que se cumpliera una condición, se mostraba un literal predeterminado; esta sería la forma sencilla de abordar el proceso.


<<dnm.plataforma.net

Al crear estilos de tabla y columna por código hemos de ser cuidadosos a la hora de asignar el estilo de tabla al grid, ya que previamente deberemos haber añadido a este último todas las columnas (estilos de columna) necesarias, o de lo contrario se producirá un error.

Pero variemos el enfoque de la situación y supongamos que el escenario a resolver es el siguiente: necesitamos mostrar una columna en la que sus celdas tomen un color de fondo en base al cumplimiento de una condición dada en otra columna de la tabla, y además, la condición a comprobar deberá pasarse dinámicamente en tiempo de ejecución. ¿Complicado?, no se preocupe estimado lector, a continuación demostraremos que "no es tan fiera la celda como la pintan".

La solución que proponemos es la siguiente: obtener la fuente de datos del control DataGrid y añadirle una columna con la expresión a evaluar. Posteriormente, en el evento de pintado de la columna, comprobar si se cumple la condición especificada por la expresión, y en función del resultado obtenido, aplicar el color de fondo pertinente a la celda. Todas estas operaciones las codificaremos igual que en los anteriores casos, dentro de una clase hija de DataGridTextBoxColumn, cuyo código podemos ver en el fuente 5, al igual que su aplicación dentro del formulario. La figura 5 muestra el DataGrid con las dos columnas involucradas en este proceso.

Celdas de cierre

Figura 5

Ilustremos el anterior planteamiento con un ejemplo: al visualizar la tabla Products, cuando el valor del campo UnitPrice sea menor de 25, pondremos un color en la celda correspondiente a la columna QuantityPerUnit; en el caso de que no se cumpla esta condición, utilizaremos un color distinto. Analizando este asunto con detenimiento nos percataremos de que la pieza más complicada de construir en este mecanismo es aquella encargada de evaluar una expresión en una celda distinta de la que estamos manipulando, y si aquella condición es o no cierta, cambiar el color de fondo en la celda en la que nos encontramos actualmente.

>>

<<dotNetManía

Y tras este ejemplo damos por concluido este artículo, a lo largo del cual nos hemos dedicado a programar el control DataGrid desde una perspectiva orientada exclusivamente al código, prescindiendo de los diseñadores, y aplicando la herencia para conseguir una mayor personalización en la presentación de los datos. Esperamos que todo lo aquí comentado sea de utilidad para nuestros lectores.

41


Por Pablo Abbate MCDBA/MCAD/MCSD/MCT Consultor independiente

Extendiendo Sharepoint Portal Server Durante una sesión de asesoramiento, un cliente me preguntó si era factible modificar o extender el comportamiento de Sharepoint Portal Server. La verdad es que no estaba muy seguro acerca de que debía contestarle...

<<dotNetManía

<< He consultado en la Web y he encontrado diversos

42

artículos que indican cómo modificar la apariencia de los sites creados con Sharepoint. En algunos casos, hasta dan pistas de cómo modificar el estilo del mismo portal, pero en ningún caso pude encontrar alguno que me aconsejara como extender la funcionalidad existente. Lo que mi cliente necesitaba, puntualmente, era que en todas las páginas del portal muestren el nombre de usuario que se encuentra actualmente conectado. Le sugerí, en un principio, que construyéramos un Web Part con dicha funcionalidad y lo pegáramos en cada una de las tantas páginas que tenía el portal. Sí, ya se, estarán pensando que no es la mejor solución. Mi cliente opinaba lo mismo. Entonces me puse a investigar como podía expandir este comportamiento a cada página del portal. Aquí comparto algunos de los hallazgos. Lo primero que se me ocurrió fue investigar en el repositorio de documentos de Sharepoint para ver qué es lo que podía descubrir. Entonces, accediendo a la db del portal (en mi SQL Server: miempres1_SITE), encontré la tabla Docs en donde Sharepoint almacena los distintos documentos que conforman dicho portal. Esta tabla, posee algunas columnas de utilidad, a saber: • DirName: esta columna se utiliza para agrupar todos los documentos que pertenecen a una misma área. Cuando esta columna está en blanco hace referencia al nivel principal del site. En cada directorio pueden encontrarse diversos documentos, lo que sugiere que el DirName puede repetirse en varias filas.

• LeafName: contiene el nombre de cada documento. Me llamó la atención encontrar páginas ASPX tales como Default.aspx, DispoForm.aspx, etc. • SetupPath: esta columna fue clave para mi investigación, puesto que me permitió descubrir que algunas filas no son más que referencias a documentos externos alojados en el sistema de archivos dentro de los templates de Sharepoint (C:\Program Files\Common Files\Microsoft Shared\web server extensions\60\TEMPLATE). Esto fue un buen indicio, porque me indicó que el contenido en realidad no es estático, sino que se carga a partir de un modelo.

Sharepoint presenta dos alternativas para encasillar contenido: los WebParts son la opción más difundida y fácil de implementar y la posibilidad de construir Custom Controls que Sharepoint puede utilizar en su capa de presentación...


<< dnm.servidores.sharepoint

Figura 1

• Content: esta columna, cuando se ha personalizado, almacena el contenido de una página .ASPX. Es importante saber que cuando el contenido tiene información, Sharepoint utilizará los datos almacenados en el campo de la db en lugar de utilizar el archivo del file system.

También descubrí que, si modificaba la disposición de los controles dentro de la página y luego navegaba por el área Topics esta última reflejaba mis cambios. Lo único que me restaba era construir mi propio control y colocarlo dentro de cada una de las

Bien, lo que Sharepoint almacena es lo que puede verse en la figura 1. Lo que pude concluir es que Default.aspx en el top level se archiva dentro de Docs, pero el resto de las páginas del portal hacen referencia a un template especificado en la columna SetupPath. Por lo tanto, tomé el área Topics, seleccioné las filas cuyo DirName tuviera ese valor y luego busqué aquella que tuviera el LeafName con el valor Default.aspx. Pude comprobar que la columna SetupPath apuntaba al directorio 1033\SPSTOC\DEFAULT.ASPX, como puede verse en la figura 2. A continuación me fui a la carpeta y abrí el archivo correspondiente. Sólo se trataba de una página .ASPX que, en su interior, tenía una referencia a la clase Sharepoint.WebControls.

páginas que Sharepoint Portal Server utiliza como template. Entonces, desenfundé mi Visual Studio y me puse manos a la obra. Lo primero fue crear un proyecto de tipo Class Library. Dentro de la solución, utilicé el archivo Class1.cs para exponer funcionalidad a mi componente. Me quedó algo así como el fuente 1. Después de compilar, obtuve el assembly ccSharepoint y lo agregué al Default.aspx. Para ello, hice las siguientes tareas: 1) Copié el assembly ccSharepoint.dll en el directorio c:\intepub\wwwroot\bin de mi Servidor Sharepoint Portal. 2) Registré el assembly dentro de la página C:\Program Files\Common Files\Microsoft Shared\web server extensions\60\template\

<<dotNetManía

Figura 2

43


<< dnm.servidores.sharepoint using System; using Microsoft.SharePoint.Portal; namespace ccSharepoint { public class Encabezado : System.Web.UI.WebControls.Label { public Encabezado() { // // TODO: Add constructor logic here // } protected override void Render(System.Web.UI.HtmlTextWriter writer) { this.Text += “<br>Usuario:” + this.Page.User.Identity.Name; base.Render (writer); } } } Fuente 1

1033\SPSTOC\default.aspx. Esto se hace con la directiva Register. Como se ve a continuación: <%@ Register Tagprefix="CC" Namespace="ccSharepoint" Assembly="ccSharepoint" %>

3) Luego busqué el lugar adecuado y agregué el control, aprovechando las clases de estilos existentes en Sharepoint. El fragmento de código es: <CC:Encabezado runat="server" class="ms-pvtbbutton" />

4) Por último, declaré el assembly como seguro dentro del archivo web.config ubicado en wwwroot. La expresión XML que agregué es la siguiente:

<<dotNetManía

SafeControl Assembly="ccSharepoint" Namespace="ccSharepoint" TypeName="*"

44

Safe="True" />

Al refrescar mi navegador en la URL modificada, el aspecto que me quedó es el que se puede ver en la figura 3. En el ángulo superior derecho se puede observar el nombre de usuario generado por mi custom control. Luego, coloqué el mismo control en todos los templates que mi portal utilizaba, para que los distintos modelos de páginas tuvieran una apariencia homogénea. ¡Y asunto terminado!

Conclusión Sharepoint presenta dos alternativas para encasillar contenido. Los WebParts son la opción más difundida y fácil de implementar. Sin embargo, como hemos visto en este artículo, también existe la posibilidad de construir Custom Controls que Sharepoint puede utilizar en su capa de presentación para extender la funcionalidad de sus páginas y así adaptarse a las necesidades del usuario.

Figura3


Por José Manuel Alarcón Aguín ASP y ASP.NET MVP krasis.com

Seguridad de Internet Information Server (II). Autorización.

<< Primera barrera: permisos IIS La autorización, es decir, permitir o denegar el acceso a recursos del sistema, es una de las primeras consideraciones que debemos tener en cuenta a la hora de asegurar adecuadamente un servidor IIS. Cuando IIS recibe una petición para acceder a cualquiera de los recursos bajo su control (archivos, directorios virtuales, imágenes…) permite o deniega los comandos HTTP que se le solicitan en función de los permisos establecidos en su configuración para cada objeto bajo su supervisión. Dentro de la pestaña Directorio Particular del diálogo de propiedades de cualquier objeto (página, gráfico, directorio…) se ajustan los permisos correspondientes a las peticiones Web recibidas. En la figura 1 podemos observar las posibilidades disponibles: lectura, escritura, examen de directorios, registrar visitas, indexar el recurso y acceder al código fuente de las secuencias de comandos. Lo cierto es que son todas bastante obvias y sólo tal vez las dos últimas pueden suscitar alguna confusión. El permiso para indexar el directorio permite que el servicio de búsqueda de archivos, Microsoft Index Server, analice los contenidos del sitio y los añada a su base de datos de búsqueda. Si no tenemos activado este servicio el ajuste no tendrá efecto alguno.

La opción, Acceso al código fuente de las secuencias de comandos, tiene una función obvia pero lo que confunde es averiguar cuándo se puede efectuar este acceso. En realidad esta opción sólo tiene efecto si permitimos el acceso al servidor a través de Webdav, y el usuario que accede tiene permisos de lectura o escritura. No deberíamos activar este permiso salvo por motivos importantes. En realidad el único permiso que necesitaremos habitualmente es el de lectura o de otro modo los gráficos y páginas estáticas no podrían descargarse (luego veremos una excepción). Es bueno también dejar marcada la casilla de verificación de registrar los accesos y realizar una auditoría de accesos de vez en cuando. Otro ajuste importante que podemos controlar desde esta misma pantalla es el permiso de ejecución. En la segunda lista desplegable desde abajo en la figura 1, podemos seleccionar los permisos de ejecución para un directorio. Por defecto todas las carpetas poseen permisos para ejecutar secuencias de comandos (páginas ASP y ASP.NET normalmente), lo cual no es del todo óptimo. Lo mejor que podemos hacer es tratar de concentrar todas las páginas ejecutables dentro de un mismo directorio (o unos pocos) y que sea éste el único al que IIS conceda permisos de ejecución. De este modo minimizamos las posibilidades de que algún cracker pue-

<<dotNetManía

La autenticación de usuarios es tan sólo una pequeña parte de los problemas que debemos resolver en la seguridad de nuestro servidor IIS. Otro de ellos es la autorización.

45


<< dnm.servidores.iis

Figura 1.Los permisos de acceso de IIS regulan cómo éste responde a las peticiones hechas directamente a él, pero no tienen nada que ver con los permisos del sistema de archivos, sólo los complementan.

da llegar a escribir su propio código en un archivo dentro de una carpeta mal configurada en cuanto a seguridad, y luego ejecutarlo con los permisos otorgados por defecto. De hecho, en caso de optar por esta posibilidad, ni siquiera es necesario que el directorio que agrupa a nuestras secuencias de comandos posea permisos de lectura en IIS. Si negamos la lectura, las páginas ASP seguirán ejecutándose perfectamente y restringiremos aún más las posibilidades de que alguien pueda llegar a leer nuestro código, obteniendo contraseñas y otra información valiosa. Sólo hay que procurar guardar únicamente código en ese directorio, y no gráficos o páginas estáticas. Es también recomendable otorgar un nombre no excesivamente obvio a los directorios de este tipo. Si usamos nombres como "scripts", "cgi-bin", "bin" o similares estaremos facilitando su localización. Aunque el oscurantismo no es una buena política para obtener seguridad, nada impide que lo usemos como medida adicional.

<<dotNetManía

Los permisos en el sistema de archivos

46

En la entrega anterior de esta serie sobre seguridad de IIS estudiamos los diversos métodos nativos de los que disponemos para autenticar a los usuarios de una aplicación IIS. Es también posible, claro está, diseñar y emplear cualquier método de autenticación apoyado en código propio para hacerlo (por ejemplo validando credenciales en una base de datos). Sea cual sea el método elegido, el concepto más importante que siempre debemos tener presente es el de la suplantación de usuarios. Tal y como hemos estudiado, tras la autenticación de un usuario frente a IIS

la página solicitada se ejecuta bajo el contexto de seguridad de éste, es decir, suplantando al usuario autenticado. Si el acceso es anónimo o se ha empleado un método propio de autenticación, la página se ejecutará bajo el contexto del usuario IUSER_maquina (o IWAM_maquina) en aplicaciones ASP, o con las mismas atribuciones que la cuenta ASPNET (en Windows 2000 Server) o Servicio de Red (en Windows 2003 Server) en el caso de aplicaciones ASP.net. Repase el artículo anterior para recordar estas nociones. Recuerde que en el caso de aplicaciones ASP.net puede suplantar a cualquier otro usuario modificando el archivo web.config. La importancia de la suplantación de usuarios radica en que, a la hora de acceder a cualquier recurso del sistema, el código de nuestra página dispondrá de los mismos permisos que el usuario al que se está suplantando. Aunque los permisos de IIS que hemos visto sirven como primera barrera de contención ante ataques, no son infranqueables ni mucho menos. Por ejemplo si denegamos los permisos de lectura a una carpeta de nuestra aplicación que contiene archivos de configuración, si alguien intenta acceder a alguno

La importancia de la suplantación de usuarios radica en que, a la hora de acceder a cualquier recurso del sistema, el código de nuestra página dispondrá de los mismos permisos que el usuario al que se está suplantando. de éstos escribiendo directamente su ruta, IIS responderá a la petición con un error 403 de permiso denegado y se le impedirá su lectura. Hasta aquí todo correcto. Ahora bien, si nuestra aplicación tiene un error y alguien logra acceder a los contenidos de la carpeta a través de otros medios no controlados por IIS (por ejemplo mediante uno de nuestros propios archivos ASP o ASP.net que no controle bien la seguridad), el ajuste no nos servirá de mucho. Este caso es mucho más frecuente de lo que pueda parecer, sobre todo debido al uso de malas técnicas de programación por parte de los desarrolladores Web, tema que trataremos en algún artículo posterior. IIS no es más que otra aplicación que se ejecuta sobre el sistema operativo, por lo que es éste último en última instancia el que se ocupa del nivel más bajo de la cadena de la seguridad. En el caso que nos ocupa dicho nivel lo constituye el sistema de archivos.


<< dnm.servidores.iis Así, volviendo al ejemplo anterior, si hemos establecido adecuadamente los permisos NTFS para los usuarios de nuestra aplicación, aunque alguien burle la seguridad de IIS no podrá burlar las listas de con-

La seguridad de acceso a los recursos del disco duro del servidor es una combinación de los permisos de peticiones IIS y los controles de acceso de NTFS. Cuando ambos entran en conflicto siempre prevalecen los más restrictivos. trol de acceso (ACS) del sistema de archivos. Es decir, la seguridad de acceso a los recursos del disco duro del servidor es una combinación de los permisos de peticiones IIS y los controles de acceso de NTFS. Cuando ambos entran en conflicto siempre prevalecen los más restrictivos. Por ejemplo, por mucho que habilitemos los permisos de lectura de una carpeta desde las propiedades de IIS, si el usuario anónimo (o el que suplante la aplicación) no tiene permisos en el sistema de archivos no se le permitirá acceder a ella. Lo mismo ocurre en el caso contrario. De todos modos siempre conviene que ambos controles de acceso se encuentren en sintonía y, en su defecto, los permisos NTFS deberán ser los más restrictivos ya que nos permiten obtener una mayor seguridad. Los permisos de IIS controlan cómo éste responde a las peticiones HTTP, pero los permisos de NTFS controlan al más bajo nivel a dónde puede acceder en el disco duro el propio IIS. De todo lo anterior se evidencia la necesidad de disponer de una adecuada configuración de seguridad en el sistema de archivos. A continuación veremos cómo conseguirlo en la práctica, estableciendo los permisos más adecuados en todo el sistema de archivos del servidor.

nunca FAT o FAT32, ya que estos últimos sistemas de archivos no ofrecen mecanismos de control de acceso a los recursos que albergan. Otra medida imprescindible en un servidor de producción es eliminar del disco duro todos los directorios virtuales y todas las aplicaciones creados por defecto. Nada de dejar IISHelp y compañía, y mucho menos la administración vía Web del servidor. Utilice el administrador de IIS para hacerlo. Posteriormente sería recomendable también que eliminara del disco duro los archivos correspondientes a estos recursos, y que residen en las siguientes carpetas: · · · · · · · ·

%DirectoriodeSistema%\inetsrv\iisadmin %DirectoriodeSistema%\inetsrv\iisadmpwd %DirectoriodeSistema%\help\iishelp\iis %DirectoriodeSistema%\web\printers Los contenidos por defecto de inetpub\wwwroot inetpub\scripts inetpub\samples inetpub\adminscripts

Otra recomendación fundamental es desactivar en las propiedades avanzadas del directorio raíz (wwwroot o como le hayamos llamado) la característica de herencia de permisos, desmarcando la primera de las casillas destacadas en la figura 2. De este modo impedimos que la carpeta herede permisos inadecuados cuando se modifican para algún directorio de los que la contienen. Proceda a ajustar permisos restrictivos para los usuarios anónimos antes mencionados en la carpeta que contiene los archivos del Web. Por defecto el gru-

El directorio raíz de IIS Figura 2. Debemos retirar la herencia de permisos de seguridad para el directorio raíz del servidor Web o podríamos desconfigurarlo inadvertidamente si tocásemos en alguno de sus directorios padre.

po Todos, al que pertenece cualquier usuario, tiene acceso de lectura y ejecución. Anule este último. Añada a la lista de acceso de la carpeta permisos para el usuario ASPNET o Servicio de Red (o IWAM o IUSR en el

<<dotNetManía

Como primera y fundamental recomendación cabe señalar que no debemos utilizar una carpeta en la partición raíz del sistema (C:\, para entendernos) como base para los archivos del servidor Web. Ello se debe a que gran parte del código de explotación de vulnerabilidades que existe se basa precisamente en que, por defecto, los archivos del servidor residen en una carpeta de dicha raíz. Como es obvio a estas alturas, la partición que utilicemos deberá ser de tipo NTFS,

47


<< dnm.servidores.iis caso de aplicaciones ASP tradicionales), y restrinja sus facultades exclusivamente a la lectura de archivos. Debe otorgar permisos de ejecución en la carpeta Bin de su aplicación ASP.net, pero no a las demás. En el caso de aplicaciones ASP tradicionales no es necesario otorgar permisos de ejecución en el sistema de archivos, ya que es IIS quien las ejecuta leyéndolas del disco. Sólo debe permitir otros permisos en casos muy

En Windows Server 2003 debemos indicar explícitamente que queremos permitir la ejecución de aplicaciones ASP o ASP.net. Una sabia decisión.

código se ejecuta como si formase parte del propio código original del servidor, y por lo tanto está en su mismo contexto de seguridad. Por ello debemos restringir en la medida de lo posible las capacidades de la cuenta System. Por omisión ésta tiene control total sobre el directorio raíz de IIS, como se observa en la figura 3. Conviene quitar los permisos de acceso de esta cuenta a los directorios donde residen los contenidos Web (incluso el permiso de lectura). Debemos recordar que si necesitamos hacer uso de los servicios de Index Server éste necesita permisos de lectura para System, así que cuidado. Conviene retirar todo acceso de las cuentas de usuario anónimo a aquellos directorios que no se usen para los contenidos del Web y especialmente a los del sistema operativo (windows y windows\system32). Igualmente es recomendable que la cuenta del sistema no tenga permisos de ejecución de algunos programas potencialmente peligrosos y fáciles de ubicar, como por ejemplo cmd.exe (el intérprete de línea de comandos), tftp.exe o telnet.exe.

puntuales. Por ejemplo puede que destine un directorio para que los usuarios de Internet puedan subir archivos al servidor. En éste deberá asignar permisos de escritura, aunque no de ejecución. Si una aplicación utiliza alguna DLL o ejecutable propios que se encuentran en un subdirectorio del raíz de IIS deberá conceder a los usuarios anónimos permisos de ejecución. Lo que se suele hacer en este caso es otorgar este permiso para el directorio completo, pero se trata de una mala política. Lo mejor es conceder el permiso exclusivamente para la DLL o EXE en cuestión y no sobre el directorio completo. De esta forma aunque alguien lograse copiar ahí un programa malicioso no sería capaz de ejecutarlo. Sólo esta medida hubiera evitado los miles de servidores infectados por el famoso gusano CodeRed y sus variantes, puesto que se basaban precisamente en obtener permisos de ejecución por defecto en un cierto directorio tras copiar un ejecutable. Tenga en cuenta también que si copia archivos desde una unidad NTFS a otra, éstos conservan los permisos originales, por lo que conviene verificar tras una copia que todo esta ajustado como es debido.

<<dotNetManía

Permisos NTFS en lugares estratégicos

48

Aparte de establecer adecuadamente los permisos del directorio raíz de IIS, existen otros lugares del sistema sobre los que conviene actuar. Como se ha estudiado en la anterior entrega, IIS propiamente dicho se ejecuta en el contexto de seguridad del sistema (usuario System). Algunas de las vulnerabilidades que se descubren de vez en cuando, como por ejemplo los desbordamientos de buffer, consiguen "inyectar" código en el servidor Web. Este

Figura 3. IIS se ejecuta bajo el contexto de seguridad del usuario de sistema, el cual por defecto tiene acceso total a los directorios de contenidos del web. Conviene eliminar sus permisos para este directorio evitando así muchos ataques por desbordamiento.

El grupo Todos debe disponer únicamente de permisos de lectura en los directorios de IIS, y en c:\windows, c:\windows\system32 y en c:\Archivos de programa\Archivos comunes.


<< dnm.servidores.iis

Figura 4.Algunos de los filtros y extensiones de IIS son potencialmente peligrosos y si no los necesitamos haríamos bien en eliminarlos.

Otras medidas de seguridad referentes al sistema de archivos Una medida adicional que aumenta la seguridad es deshabilitar todas aquellas extensiones y filtros ISAPI que no necesitemos. Uno que tradicional-

Figura 5.Windows 2003 trata de modo más específico a los filtros de IIS, ofreciendo una zona de la configuración dedicada a gestionarlos.

mente ha ofrecido múltiples problemas de seguridad ha sido el que se encarga de las extensiones de Frontpage. Si no las necesita en el servidor de producción deshabilítelas, y si no puede prescindir de ellas hágalas de sólo lectura. Otros filtros que se pueden eliminar sin problemas son el de autenticación mediante MD5, el de compresión HTTP y, si no lo necesitamos, el de comunicaciones SSL. Sobre todo los dos primeros pueden ser fuente de problemas de seguridad. Puede deshabilitarlos yendo a las propiedades de Sitios Web y quitándolos con la herramienta a tal efecto que se ve en la figura 4. También puede eliminar sus correspondientes archivos del disco duro para evitar que se puedan volver a añadir. Los nombres de las DLL que los implementan se ven con la herramienta de la figura 4. En Windows Server 2003 se ha creado una sección dedicada por entero a gestionar estos filtros y por defecto vienen todos deshabilitados, hasta tal punto que debemos indicar explícitamente que queremos permitir la ejecución de aplicaciones ASP o ASP.net. Una sabia decisión. Algo que se debe hacer siempre es auditar todos los accesos fallidos y los exitosos que no sean de lectura a los directorios de IIS. Esto permite obtener una gran ventaja a la hora de descubrir posibles ataques y averiguar cómo se han efectuado. Estos registros de auditoría se deben revisar con frecuencia.

En resumen En estos dos artículos hemos estudiado los fundamentos para obtener una buena protección de nuestro servidor IIS. Sólo con estos pocos consejos y técnicas su sistema puede ganar mucho en seguridad, pero todavía se pueden hacer más cosas. En el próximo artículo de esta serie veremos cómo complementar de forma gratuita la seguridad de IIS usando algunas herramientas indispensables.

<<dotNetManía

En el caso de aplicaciones ASP.net el usuario anónimo (o el que se suplante para cualquier ejecución) debe tener permisos de lectura en el directorio que alberga las bibliotecas de la plataforma .net y que se encuentra en C:\WINDOWS\Microsoft.NET\Framework\vx.x.xxxx, en donde las 'x' representan la versión de la plataforma instalada en el sistema. Este pequeño detalle es muy importante cuando se usa la etiqueta <identity> en web.config para suplantar una cuenta específica. Es frecuente que al usarla la aplicación deje de funcionar. No es el primero que se desespera intentando encontrar una solución antes de darse cuenta de que sólo es una cuestión de permisos sobre el mencionado directorio. Otro punto altamente conflictivo es el directorio raíz del sistema. Si no se establece otra cosa, todo el mundo tiene control absoluto sobre los contenidos de este directorio, en gran parte debido a que en él reside el archivo de intercambio de memoria del sistema operativo (la memoria virtual). Si un usuario se autenticase y no pudiese acceder a esta memoria virtual se podría quedar bloqueado. Dado que los usuarios anónimos nunca van a autenticarse localmente no necesitan permisos tan liberales en este lugar. Modifique pues los permisos de c:\ (o el directorio raíz que usted utilice) para que los usuarios de su aplicación Web sólo tengan acceso de lectura.

49


Por Manuel Imaz Consultor Independiente imaz@acm.org

Los avatares de las arquitecturas En nuestro artículo anterior (“Arquitecturas. Algunos fundamentos”, dotNetManía, Nro. 6, julio/agosto) hablábamos de las diversas concepciones que implica el término arquitectura de acuerdo al autor que emplea el concepto.

<<dotNetManía

<< Cambios de significado

50

En particular nos referimos a cómo el Proceso Unificado consideraba en algún momento a la arquitectura como un plano para construir el sistema y al mismo tiempo como un prototipo de ese mismo sistema, generando confusión porque abría el concepto de tal forma que abarcaba una diversidad de cosas distintas. Nuestra idea es coincidente con la del creador del Proceso Unificado, Ivar Jacobson, que lo denominó en sus comienzos Objectory, sugiriendo la idea de una fábrica de objetos. En este momento comparte con su hija una empresa llamada Jaczone que se dedica a producir herramientas que utilizan agentes inteligentes y, por esa razón, se denominan activas. En el sitio web de la empresa tiene una serie de postales que envía desde distintos puntos a los que se traslada en sus frecuentes viajes y -en una de ellas- nos dice que: “En RUP a cada vista se le ha dado un nombre. Éstas son la vista de casos de uso, la vista lógica, la vista del proceso, la vista de implementación y no se corresponden exactamente con los modelos. Esto significa que los desarrolladores tienen que tratar al mismo tiempo con los nombres de las vistas y los de los modelos. Es confuso. Me gustaría encontrar un desarrollador o representante técnico de Rational que entienda a la vez la estructura de las vistas y la de los modelos. Incluso algunos altos expertos de Rational nunca llegan a explicar cómo estas estructuras están vinculadas entre sí. La mayoría sólo entiende la estructura de las vistas o la de los modelos” (http://www.jaczone.com/postcards, Architecture in RUP) Una cosa que sorprende, porque siempre nos imaginamos que a ese nivel de empresa productora de herramientas y expertos en desarrollo de software, al menos la idea de arquitectura debería estar relativamente estabilizada. Pero no, este concepto al igual

que otros en informática -y otros dominios en general- tiene significados cambiantes en función de quien lo dice, de su experiencia, del momento en que se dice, etc. Pero lo realmente interesante es que esa acumulación de significados distintos -los lingüistas denominan a dicho fenómeno polisemia- se presentan al mismo tiempo y no necesariamente a lo largo del tiempo. Veamos algunos ejemplos. En el caso de los requisitos, podemos ver que un mismo libro puede hablar de "captura de requisitos", "extracción", "construcción" u otros. Cada término tiene su origen en una metáfora distinta: en el caso de captura nos remite al dominio de la caza, en el que se trata de coger una presa que está huyendo y se deja difícilmente capturar, con lo cual es probable que lo que capturamos es sólo una pieza deformada por el proceso. El término extracción se refiere a la actividad minera, en la cual debemos remover mucho material a fin de poder separar el

Figura 1. Distintos puntos de vista y una única arquitectura


<< dnm.arquitectura

La mayoría de los significados son figurados La pregunta anterior surge de un supuesto: existe algo definido, delimitado y con características esenciales que tenemos que enunciar. Como últimamente se utiliza mucho el concepto en la literatura informática, diríamos que el concepto hace referencia a algo de la realidad que tiene una entidad ontológica, es decir una existencia en sí e independiente del sujeto que intenta conocerlo. Es debido a este supuesto -enraizado en lo más profundo de nuestra forma de ver el mundo- que nos resulta chocante oír hablar de metáforas o lenguaje figurado, es decir nos impacienta que se intente rizar el rizo cuando deberíamos llamar pan al pan y dejarnos de rodeos. Eso es lo que nos llevó a utilizar en nuestro artículo anterior el gráfico de la figura 1. El supuesto nos lleva a pensar que la arquitectura tiene realmente un determinado aspecto, una forma definida, y que simplemente observamos las sombras que se proyectan en diferentes planos -lo que la gente de Rational llama vistas- razón por la cual si miramos la pirámide de costado podemos ver una proyección en forma de triángulo, pero si la observamos desde abajo -o arriba- veríamos un cuadrado. Esas diferencias no nos inquietan porque -de acuerdo a nuestra concepción habitual- son las deformaciones propias de toda perspectiva, pero que corresponden a una misma realidad, la pirámide o arquitectura. Pero lo que queremos plantear en este artículo es que no existe una forma

específica de la arquitectura, que lo que llamamos puntos de vista son a su vez metáforas, porque corresponden a perspectivas conceptuales y, en consecuencia, cada perspectiva conceptual está concibiendo la cosa con una forma, aspecto y funcionalidad diferentes. Si nos resulta chocante pensar que el lenguaje de la informática es fundamentalmente figurado, observemos los conceptos más básicos -por ejemplo instrucción, proceso, procedimiento, sentencia y ejecución- y tengamos en cuenta hasta qué punto corresponden a conceptos del dominio jurídico (después del proceso una sentencia nos puede llevar a la ejecución, felizmente no en todos los países), o cómo se puede matar una tarea, o abortar una ejecución para pensar en el origen médico de otros términos, o sin ir más lejos en la biología que nos ha llenado de virus o gusanos con sus reproducciones y código genético. Ya la jerga de la arquitectura nos ha acostumbrado mucho a los conceptos que se derivan de dicha disciplina por lo cual se habla de capas o de niveles, dando origen a estructuras de aplicaciones con patrones cuyos nombres derivan de dichos conceptos. O del entorno financiero, de cuyos brokers hemos tomado las ideas para armar otras arquitecturas (Corba). O del entorno de juegos de niños, cuyas cajas de arena -sandbox- nos han servido para crear espacios seguros donde ejecutar programas Java, al estilo de los sitios reservados para jugar sin afectar a los que están alrededor. Si alguien insiste y nos dice que esos términos se han utilizado un poco al azar y que se podrían haber elegido otros, le contestaríamos que eso demuestra que: • No hay términos específicos -literales- que correspondan a los conceptos a los que nos referimos. • Que si se hubieran elegido otros términos, el argumento seguiría siendo tan válido. Sólo cambiaría el dominio del cual hubieran sido extraídos. Pero la consecuencia es aún más profunda: eso significa que el término utilizado contamina constantemente el discurso que utilizamos para referirnos al área concreta de la que queremos hablar. En realidad deberíamos cambiar

la figura 1 y presentarla como algo parecido a la figura 2. Esto se corresponde mejor con las descripciones que hemos hecho de los requisitos ("captura", "extracción", "construcción", etc), dado que cada metáfora hace alusión a diversos aspectos que no necesariamente son compartidos por las otras. De vez en cuando aparece una nueva palabra -metáforaque transpone el esquema tradicional y nos permite ver el problema de una forma totalmente nueva (es lo que a veces se denomina desplazamiento de paradigma, o revolución en alguna teoría).

Figura 2. Puntos de vista conceptuales

Esto es así por diversas razones: • Estamos considerando un concepto abstracto y no uno referido a una entidad o fenómeno concreto. Pero incluso aunque así fuera, también tenemos una amplia variabilidad conceptual como lo han demostrado las diversas teorías científicas. • Nos estamos refiriendo a la concepción de una disciplina en plena evolución. Eso quiere decir que los diversos puntos de vista conceptuales no sólo intentan capturar un concepto que se refiere a una entidad estática, sino que el propio fenómeno está en plena evolución y lo que se capta en un determinado momento puede no ser válido con posterioridad, así como la aparición de nuevas características obligan a cambiar la imagen que habíamos armado hasta ese momento. • La caracterización de un concepto está determinada además por las metas, intereses, y finalidades de la actividad en la que estamos inmer-

<<dotNetManía

metal precioso de lo que no nos sirve. Es una tarea laboriosa en la que también podemos dejar escapar parte del metal con el resto de la tierra desechada. O si nos referimos a los requisitos en términos de construcción, será algo que no existe previamente y que debemos elaborar en base a la materia prima que vamos logrando de los usuarios y clientes. En este punto la pregunta que nos asalta es la siguiente: está muy bien esta idea de hablar de distintas metáforas pero ¿cuál es en realidad la idea más adecuada en relación con los requisitos? ¿O la más precisa, la que se ajusta realmente al concepto que estamos considerando?

51


<< dnm.arquitectura sos. El grado de participación y el sitio que ocupamos en un determinado proceso nos determina la visión conceptual que tenemos del mismo, por lo que no existe un único punto de vista desde el cual podamos abarcar -de forma absoluta- la totalidad del fenómeno que queremos describir. Dicho esto deberemos acostumbrarnos a tener imágenes parciales de los conceptos, lo cual no impide que intentemos integrar la mayor cantidad de imágenes en las definiciones generales. Y cada imagen parcial será útil en función de las metas e intereses que perseguimos en un determinado momento. La otra conclusión que podríamos extraer es que tampoco se trataría -en el ejemplo aportado por Jacobson en relación con la correspondencia entre vistas y modelos- de perder demasiado tiempo en encajar los elementos de uno de los esquemas en el otro. Tampoco sabemos a ciencia cierta si el problema tiene una solución satisfactoria: de lo que se trata es de que exista un grupo de modelos que se ajuste a las necesidades de cada uno de los participantes en el desarrollo. Al fin y al cabo, el concepto de vista es una metáfora más, y no necesariamente aporta más compresión del problema respecto de los modelos.

<<dotNetManía

Diversas formas de concebir los modelos

52

El los últimos años, con la aparición de las Metodologías Ágiles, se ha puesto en tela de juicio la utilidad de los modelos. Una de las razones -la más aparentees que lo que de verdad interesa es el sistema software funcionando, es decir el código en condiciones de ofrecer una solución adecuada. En la disyuntiva de elegir entre los modelos de análisis y diseño y el código funcionando, el peso lo tiene este último. La segunda razón, más profunda, tiene que ver con la concepción que han adoptado las metodologías ágiles en relación con el conocimiento. Ya existían algunos antecedentes en relación con esta idea, como en la afirmación de Dwight Eisenhower: “Siempre consideré que los planes son inútiles, pero que la planificación es indispensable”.

Es decir que no interesa tanto el producto final -entendámonos que se trata de un subproducto o producto semi-elaborado- como el proceso que lleva a elaborarlo. En el caso de la planificación, no interesa tanto el plan -considerado en la frase anterior como inútil- sino el ejercicio de producirlo, de coordinar las ideas y energías de un equipo a fin de poder elaborar el artefacto plan. Un equipo bien entrenado, con buenos niveles de comunicación, es capaz de extraer -o, en su defecto, de sobrentender- una cantidad de información que no necesariamente está disponible en el producto que ha producido. Por esa razón, los partidarios de las metodologías ágiles consideran que no es tan importante el conjunto de modelos que se han elaborado como productos intermedios, sino la dinámica del equipo que pudo construirlos.

torio (con carpetas, documentos, papeleras, etc) o la de las ventanas a través de las cuales tenemos visiones parciales y detalladas de los elementos que nos interesa observar (carpetas, documentos, etc). Este ejercicio de plantearnos el concepto de arquitectura a tales niveles de profundidad es muy importante -aunque sólo lo hagamos de vez en cuando- ya que nos permite lograr una comprensión de lo que estamos hablando que no nos lo ofrecen las ideas habituales sobre dicho concepto. Pero también es útil porque nos ofrece una base sobre la cual construir la continuación de estos artículos que intentarán ofrecer puntos de vista diversos e incluso contradictorios entre sí. Los razonamientos construidos bajo la apariencia de una contradicción pueden ser un mecanismo útil para la producción de nuevos pensamientos, tal como lo

El los últimos años, con la aparición de las Metodologías Ágiles, se ha puesto en tela de juicio la utilidad de los modelos. En este dirección apunta la concepción de la programación extrema -XPde utilizar metáforas como equivalentes de una arquitectura. Veamos lo que nos comenta Ken Beck en "Extreme Programming Explained" (AddisonWesley, 2000): "La metáfora, en XP, reemplaza en gran medida lo que otras personas llaman 'arquitectura'… Necesitamos enfatizar el objetivo de la arquitectura, que es el de brindar a todos una historia coherente con la cual poder trabajar, una historia que pueda ser compartida fácilmente tanto por la gente del negocio como por los técnicos. Al pedir una metáfora, tendremos probablemente una arquitectura que es fácil de comunicar y elaborar" (p. 56-57). Y a los que pueda sorprenderles esta manera de concebir la arquitectura como una metáfora tendríamos que recordarles que es algo que se ha hecho -y sigue haciéndose- con mucha frecuencia en el diseño de interfaces gráficas de usuario, sin ir más lejos con la metáfora del escri-

muestra la idea de que "los planes son inútiles, pero la planificación indispensable". La frase, tomada tal cual y con un enfoque estático, nos parece una contradicción en los términos, pero es un enfoque dinámico el que permite pensar en forma abierta, como en espiral, y en la cual aparecen nuevos datos que nos brindan la solución a la aparente contradicción: lo más útil es el ejercicio de planificar dado que la realidad es más compleja de lo que puede prever cualquier plan y, en consecuencia, esa capacidad que desarrollamos nos permitirá ir haciendo adaptaciones sobre la marcha para ajustarnos a nuevos requisitos, nuevas exigencias y necesidades por parte de los usuarios. De esta manera, el concepto de arquitectura se irá produciendo gradualmente, en base a una serie de consideraciones y puntos de vista diferentes. Incluso con las contradicciones que podemos observar en otros dominios, es decir con todas las características de un ser viviente, y sin ánimo de caer en otra metáfora…


dnm.trucos

Automatizar la importación/exportación de datos en SQL Server Por Pedro Pozo (clikear.com) Los DTS (Data Transformation Services) son muy prácticos a la hora de automatizar la importación o exportación de datos en SQL Server. Aquí presentamos una

forma sencilla de ejecutar un DTS, se trata de ejecutarlo desde un procedimiento almacenado, esto nos será de utilidad si queremos llamar al DTS desde una aplicación externa que estemos desarrollando, ya que nos evitaremos complicadas llamadas a Microsoft DTSPackage Object Library y podremos ejecutar el DTS con una simple llamada a un procedimiento almacenado.

CREATE Procedure EjecutaDtsRun @ServerName varchar(30), @UserName varchar(30), @Password varchar(30), @DtsName varchar(30), @DtsPassword varchar(30), @GlobleVariableList varchar(500) — lista de variables globales que tendrán el formato siguiente — <GlobalVariableName>:<datatypeid>=<GlobalVariableValue> AS DECLARE @ERROR int DECLARE @CMD varchar(1000)

— Para almacenar el numero de error — Instruccion para ejecutar el DTS

BEGIN — Ponemos el error a 0 SET @ERROR = 0 IF( @GlobleVariableList IS NULL ) — Si no tiene variables globales BEGIN SET @CMD = ‘dtsrun /S ‘+@ServerName+’ /U ‘+@UserName + ‘ /P ‘+@Password+’ /N ‘+@DtsName+’ /M ‘+ @DtsPassword END ELSE BEGIN — si tiene variables globales SET @CMD = ‘dtsrun /S ‘+@ServerName+’ /U ‘+@UserName + ‘ /P ‘+@Password+’ /N ‘+@DtsName+’ /M ‘+@DtsPassword+’ ‘+@GlobleVariableList+’’ END EXECUTE @ERROR = master..xp_cmdshell @CMD — Comprueba si se ha producido algún error SELECT @ERROR = COALESCE( NULLIF ( @ERROR, 0 ), @@ERROR ) IF @ERROR <> 0 BEGIN ROLLBACK TRANSACTION RETURN @ERROR END END — Devuelve el error RETURN @ERROR GO

Cómo saber cuando se inició SQL SERVER Por Pedro Pozo (clikear.com) Una forma de saber cuando se inició SQL Server es consultar en las tablas de sistema de SQL Server la fecha de creación de la base de datos TEMPDB. TEMPDB es una base de datos temporal que es creada siempre por SQL Server al iniciar el servicio.

SELECT crdate FROM master.dbo.sysdatabases WHERE name = 'tempdb'

Así mediante esta consulta sabremos la fecha y hora en la que fue iniciado SQL Server por última vez.

<< dotNetManía

trucos.trucos.trucos

<< dnm.trucos

53


dnm.comunidad.net

<<

dnm.comunidad.net

Clikear Hoy en día son muchos los portales temáticos que han surgido como consecuencia de la aparición de Visual Studio .NET y que por tanto merecen nuestra atención. Internet da la posibilidad a cualquiera de nosotros de ser emisores de información y esto hace que cada día haya más y más información. Pero no es oro todo lo que reluce...

<<dotNetManía

y analizar muchos de ellos hemos encontrado << Después de buscar varios (en castellano) que iremos valorando en esta

54

sección. Empezamos con el sorprendente clikear.com. Un portal que surge con vocación de comunidad, destinado a desarrolladores de nuevas tecnologías en entornos Microsoft y totalmente en castellano. Si abrimos nuestro navegador y tecleamos www.clikear.com lo primero que nos encontramos es su mascota, un ratón llamado Julio C# que nos da la bienvenida. A medida que nos adentramos en sus contenidos descubrimos una distribución de lo más peculiar. Cada sección es un tipo de portal encaminado a perfeccionarse y mantenerse por sí mismo, con sus propios manuales, foros, lista de eventos, tutoriales, software, ejemplos de código proporcionado por colaboradores, etc. Entre sus temas principales nos encontramos con secciones específicas de Visual Studio .NET como ASP.NET, XML, VB.NET, C#, WinForms, Script y Web Services. Y otras específicas de servidores como Sharepoint, Biztalk, Exchange, SQL Server, Commerce Server, Arquitectura .NET. Por si esto fuera poco, CLIKEAR se ha ido extendiendo por varios países de habla hispana fundando comunidades. Ellas mismas controlan el contenido de los portales de CLIKEAR en sus países. Estas comunidades de forma particular informan de los eventos que se producen en sus países, resuelven las dudas que se les presentan a sus miembros a través de sus propios foros y, de forma general, aportan ejemplos de código, aplicaciones y manuales a todas las comunidades. A continuación os ofrecemos un listado de las comunidades: http://argentina.clikear.com http://colombia.clikear.com http://ecuador.clikear.com http://mexico.clikear.com http://chile.clikear.com

Hemos contactado con sus creadores y nos han puesto al tanto de algunos de los proyectos que quieren llevar a cabo muy pronto. Destacamos uno que por su carácter solidario merece una mención aparte. Se trata del hospedaje gratuito de páginas pertenecientes a ONGS y formar un anillo solidario con el fin de recoger material informático en desuso para distribuirlo en países del tercer mundo. Para ello están buscando patrocinadores que ayuden a llevar a buen puerto esta iniciativa. Otro de sus proyectos es seguir desplegando sus tentáculos por todo el mundo fundando comunidades en más países; según tenemos entendido ya tienen contactos con nuevos colaboradores para formar parte de la comunidad CLIKEAR. Resumiendo, nos encontramos ante un portal indispensable para completar tus conocimientos en .NET y poder adentrarte en una comunidad de desarrolladores en castellano a nivel mundial. ¿Merece la pena? Desde esta revista pensamos que sí y estamos dispuestos a prestarles todo nuestro apoyo.


<< dnm.opensource

Fernando Nogueras

HealthMonitor HealthMonitor es una herramienta de monitorización de sistemas Windows escrita enteramente en plataforma .NET.

<< Trabaja como un servicio Windows y se encarga de chequear cíclicamente el estado del sistema, tales como eventos, servicios, memoria, procesador, etc… y notifica al administrador cualquier suceso vía e-mail. Tiene la ventaja de que posee una interfaz gráfica la cual nos permite configurar este servicio de chequeo del sistema. El código fuente está bien organizado y nos sirve como un buen ejemplo del uso de WMI (Windows Management Instrumentation) desde .NET a través del namespace System.Diagnostics. Además HealthMonitor es de esos proyectos no demasiado largos y bien organizados que se prestan a modificarlo y adaptarlo fácilmente a nuestras necesidades. En mi trabajo diario necesitaba un programita que controlara determinados servicios de Windows y si alguno de ellos fallaba o no estaba cargado, desco-

nectara la máquina de un clúster de servidores al cual pertenecía. Echando un vistazo al código pude ver que el módulo llamado Checks.vb contiene las implementaciones de cada uno de los chequeos o supervisiones que se pueden hacer sobre el sistema y que Notifications.vb contiene las posibles acciones a realizar una vez que falla alguno de los chequeos. Todo lo que tuve que hacer es añadir una nueva función en Notifications.vb y algunos pocos cambios más y ya tuve listo mi programa para que desconectara la máquina del clúster de servidores con su interfaz gráfica y todo. Recordad, antes de ponerse a picar código mejor buscar si alguien lo ha hecho ya y reutilizarlo en beneficio propio y de la propia comunidad. Podemos bajarnos los fuentes y la documentación desde http://healthmonitor.sourceforge.net/.

Noticias Open Source Mono 1.0: Por fin disponible la primera versión estable de Mono

<<dotNetManía

"Novell y la comunidad de desarrolladores del proyecto Mono se complacen en anunciarles el lanzamiento de la versión 1.0 de Mono, una implementación de código abierto del .NET Framework, para uso en Linux, Unix, Mac OS X y Windows". Así es como se anuncia la versión 1.0 desde la nueva Web del proyecto Mono, www.mono-project.com, desde la cual podemos realizar la descarga y probar esta interesante alternativa al framework de Microsoft.

56

Comunidad Open Source para en .NET Compact Framework OpenNETCF (www.opennetcf.org) es una web que cumple las funciones de como repositorio de infor-

mación y de proyectos open source, específicamente para .NET Compact Framework. Esta web contiene un interesante proyecto llamado Smart Device Framework que es una especie de librería que extiende el Compact Framework y nos facilita la tarea de programar para estos dispositivos móviles.

Interesante artículo sobre el uso de herramientas Open Source en .NET El artículo de Aaron Junod nos muestra recomendaciones sobre que herramientas usar a la hora de desarrollar para conseguir un código conciso y de calidad. Además contiene referencias a muchas herramientas Open Source interesantes que se recomiendan en el mismo, tales como nUnit, nAnt, nDoc, FxCop, etc. Incluso podemos descargar un único archivo que contiene todas estas herramientas utilizadas. Ver en http://www.15seconds.com/issue/040621.htm.

noticias.noticias.noticias.noticias


<< dnm.biblioteca.net

dnm.biblioteca.net

The C# Programming Language Anders Hejlsberg, Scott Wiltamuth, Peter Golde Editorial Addison-Wesley ISBN: 0321154916 Páginas: 645 Publicado: Octubre-2003 Esta obra nace con la intención de convertirse en un clásico. Hasta el título recuerda la inmortal "The C programming language", de Kernighan & Ritchie. No obstante, no se trata de una obra introductoria al lenguaje. Los autores del libro, son los 3 autores principales del propio lenguaje C#, y la obra es más una obra de referencia, donde cada explicación es una lección y cada ejemplo, un modelo a seguir.

Enterprise Development with Visual Studio .NET, UML and MSF John Erik Hansen y Carsten Thomsen Editorial: APress ISBN: 1590590422 Páginas: 955 Publicado: Mayo-2004 Sencillamente, se trata de una obra que hacía falta en el panorama del desarrollo con .NET. La creciente importancia de las técnicas asociadas al ciclo de vida de las aplicaciones, junto a su estandarización “de iure” (UML) y “de facto” (MSF), pedía a gritos una obra explicativa del proceso y su integración en el desarrollo empresarial. Los autores, expertos en modelado con UML y en las técnicas de MSF, introducen las técnicas de UML como basamento del trabajo posterior, para continuar analizando las -todavía poco comprendidas- herramientas empresariales que permiten al desarrollador de .NET abordar adecuadamente una aplicación. La obra continúa con el análisis de dos proyectos completos, siguiendo estás técnicas, compara las herramientas de Microsoft con las de IBM Racional Rose, y concluye examinando los pormenores de la última versión de MSF (3.0). Muy recomendable, especialmente para jefes de proyecto.

<<dotNetManía

<<

Además, esta edición incluye algunos de los aspectos más importantes de la nueva versión del lenguaje (2.0), tales como clases genéricas (Generics), métodos anónimos, etc., que aparecerán con Visual Studio 2005. Una obra imprescindible, para desarrolladores profesionales.

57


noticias.noticias

<< dnm.desvan Marino Posadas

Ethernet a 10 Gb Esa es la oferta. Varios estándares nuevos que se encuentran en desarrollo tienen esa velocidad por objetivo. Además, parece que este desarrollo se está llevando a cabo en dos versiones: una para hilo de cobre, con cobertura de 10-20 m. entre nodos y otro de fibra óptica, que supera los 200 m. Para más datos, consultar el artículo de Nicholas Cravotta, de EDN.com: http://www.reed-electronics.com/ednmag/article/CA431144.

Un nuevo lenguaje de marcas para síntesis de voz alcanza el estatus de Proposed Recommendation en la W3C El documento completo puede ser descargado del sitio oficial de la W3C http://www.w3.org/TR/2004/PR-speech-synthesis-20040715 y recoge el estado actual de esta tecnología, como parte de una iniciativa más amplia, llamada “Voice Browser Activity”. http://www.w3.org/Voice

Documentos en la Red Lo que debemos conocer sobre el Spyware: Artículo publicado por Microsoft acerca de esta variante de los virus destinada a extraer información de nuestros ordenadores e informar a potenciales “espías”. Se encuentra en http://www.microsoft.com/athome/security/spyware/devioussoftware.mspx. Cleaning out your PC es otro documento relacionado con consejos sobre cómo limpiar totalmente nuestros ordenadores. La página de Microsoft que la contiene (http://support.microsoft.com/default.aspx?scid=%2fdirectory%2fworldwide%2fen-gb%2fCleaningout.asp) incluye además otros interesantes enlaces relacionados.

Tour of Microsoft Research's Next Media group es una demo de Curtis Wong en formato Streaming Media, sobre la nueva herramienta Microsoft, en fase de investigación, para el manejo de imágenes y vídeo. http://channel9.msdn.com/ShowPost.aspx?PostID=14275#14275. Espectacular.

La tecnología Object Spaces no formará un elemento separado,sino que estará integrada en WinFS (Longhorn: Windows File System) En declaraciones a dotNetManía el pasado mes de junio, Andrew Conrad del equipo de desarrollo de BB.DD. en Redmond nos comentaba que “Object Spaces es lo que -básicamente- entendemos por una tecnología relacional de correspondencia entre objetos. El proyecto ha surgido como consecuencia del trabajo en WinFS, y no existirá más como un mecanismo aislado. Como dije antes, WinFS va a erigirse en una tecnología base importantísima de carácter Objeto/Relación (por similitud con el concepto Entidad/Relación), y dispondrá de su propio modelo de datos, que es un nivel de abstracción superior al de las bases de datos relacionales clásicas”. En octubre, en un especial sobre Visual Studio 2005 beta 1 publicaremos una entrevista completa con Andrew Conrad.

Utilidades del mes TaskBar Repair Tool Plus: Repara y permite configurar los iconos del área de notificación de Windows, de la “barra de tareas” y del “inicio rápido” de aplicaciones. Disponible en : http://www.kellys-korner-xp.com/taskbarplus!.htm.

WinCln (Windows Cleaner): Limpia, fija y da esplendor a nuestro sistema operativo, liberándolo de los indeseables restos de desinstalaciones, ficheros temporales, etc. Diseñado también para proteger la privacidad, eliminando los datos sensibles, como logs del sistema, sitios web visitados, etc. http://www.simtel.net/product.download.mirrors.php?id=62216.

KeySpy 1.01: El nombre lo dice todo. (Espía del teclado). Disponible en: http://www.max2k.com/programs.php?id=38

Enlaces del mes

<<dotNetManía

Pinvoke.net: Es un excelente sitio para desarro-

58

lladores de .NET. Complementa la documentación existente en el MSDN relativa a las llamadas al API de Windows, con ejemplos, artículos y demos. Cubre un gran conjunto de llamadas. Diseñada originalmente por Ivan Medvedev, uno de los desarrolladores adscritos a la división de seguridad para el CLR, en Redmond. http://www.pinvoke.net

vbAccelerator: Interesante sitio para desarrolladores, no sólo de VB, sino también de C#. Incluye código fuente, controles, recursos, utilidades, etc. http://vbaccelerator.com

Nihil Khotari Weblog: Un “monstruo” de ASP.NET (es el único Blog personal recomendado por el autor de ASP.NET, Scott Guthrie) que nos aconseja y anticipa muchas de las características del nuevo ASP.NET 2.0 http://www.nikhilk.net


Suscripción a dotNetManía ❑ Deseo suscribirme a dotNetManía por un año (11 ejemplares) y beneficiarme de la oferta del 10% de descuento por un importe total de 60 € para España; o por 75 € para el resto de Europa; o por 90 € para el resto del mundo (IVA incluido). ❑ Deseo suscribirme a dotNetManía por un año (11 números) por un importe de 45 € por ser estudiante (IVA incluido). Aporto fotocopia del carné de estudiante o sello del centro académico (IMPRESCINDIBLE). OFERTA VÁLIDA SÓLO PARA ESTUDIANTES RESIDENTES EN ESPAÑA. IMPORTES VÁLIDOS HASTA NUEVA OFERTA DATOS DE FACTURACIÓN CIF/NIF . . . . . . . . . . . . . . . . . . . . .Empresa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nombre y apellidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dirección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Población . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Código Postal . . . . . . . . . . . . . . . . . . . Provincia . . . . . . . . . . . . . . . . . . . . . . . . . Teléfono . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fax . . . . . . . . . . . . . . . . . . . . . . . . . . . email . . . . . . . . . . . . . . . . . . . . . . . . . . . . DATOS DE ENVÍO (sólo si son distintos de los datos de facturación) CIF/NIF . . . . . . . . . . . . . . . . . . . . .Empresa . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nombre y apellidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Dirección . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Población . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Código Postal . . . . . . . . . . . . . . . . . . . Provincia . . . . . . . . . . . . . . . . . . . . . . . . . Teléfono . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Fax . . . . . . . . . . . . . . . . . . . . . . . . . . . email . . . . . . . . . . . . . . . . . . . . . . . . . . . .

FORMA DE PAGO ❑ Talón nominativo a nombre NETALIA, S.L. ❑ Giro postal a nombre NETALIA, S.L. ❑ Transferencia bancaria a nombre de NETALIA, S.L. a: La Caixa Número de cuenta 2100 4315 48 2200014696 (Indique su nombre en la transferencia) ❑ Domiciliación Bancaria Indique su número de cuenta: ❑ Tarjeta de crédito ❑ VISA ❑ MASTERCARD ❑ AMERICAN EXPRESS Número de su tarjeta: Fecha de caducidad: / (Imprescindible) Firma y sello (imprescindible) a

❑ Nº1

❑ Nº2

❑ Nº3

Envíe este formulario por email a la dirección suscriptores@dotnetmania.com, o al fax (34) 91 499 13 64 También puede enviarlo por correo postal a la siguiente dirección:

de

❑ Nº4

de 20

❑ Nº5

Usted autoriza a la mecanización de estos datos. El responsable y destinatario de éstos es Netalia, S.L. Usted tiene derecho a acceder a sus datos, modificarlos y cancelarlos cuando lo desee. Sus datos no serán cedidos en ninguna de las formas posibles a terceras partes y no se utilizarán más que para el buen funcionamiento de su suscripción a la revista dotNetMania y para informarle de las actividades comerciales que realice la editorial Netalia, S.L. Si no desea recibir información comercial de esta empresa marque la casilla siguiente ❑

❑ Nº6

C/ Robledal, 135 28529- Rivas Vaciamadrid Madrid (España)


dotNetManía #007  
Read more
Read more
Similar to
Popular now
Just for you