Integrándonos continuamente Integrándonos continuamente

Transcripción

Integrándonos continuamente Integrándonos continuamente
www.dotnetmania.com
nº 50 julio-agosto 2008 6,50 €
Visual Basic • C# • ASP.NET • ADO.NET • AJAX • Silverlight • .NET Framework
dotNetManía
dedicada a los profesionales de la plataforma .NET
Integrándonos continuamente
Opinión
Factorías de software. Justificación y ventajas
Laboratorio.NET
Aspose.Total for .NET
Dan Fernandez
TodotNet@QA
Plantillas ASP.NET e inferencia de tipos en C#
Developer and Platform Evangelism Team
Microsoft Corp.
entrevista
El Marco de trabajo de entidades de ADO.NET v3.5 (VII) • Paneles para el mundo 3D • Frameworks para Javascript •
Componentes de uso general • Extender o no extender... Los métodos extensores en Visual Basic 2008
editorial
dotNetManía
Dedicada a los profesionales de la plataforma .NET
Vol. III •Número 50 • Julio-agosto 2008
Precio: 6,50 €
¡Podemos!
Editor
Paco Marín
([email protected])
Redactor jefe
Marino Posadas
([email protected])
Editor técnico
Octavio Hernández
([email protected])
Redacción
Dino Esposito, Guillermo 'Guille' Som, Luis
Miguel Blanco y Miguel Katrib (Grupo Weboo)
Empresas colaboradoras
Alhambra-Eidos
Krasis
Plain Concepts
Raona
Solid Quality Mentors
Además colaboran en este número
Antonio Quirós, Daniel Seara, Iskander
Sierra, Juan Carlos Viñas, Luis Fraile, Mario
del Valle y Unai Zorrilla.
Ilustraciones
Portada: Javier Roldán
Atención al suscriptor
Pilar Pérez
([email protected])
Edición, suscripciones y publicidad
.netalia
c/ Robledal, 135
28522 - Rivas Vaciamadrid (Madrid)
www.dotnetmania.com
Tf. (34) 91 666 74 77
Fax (34) 91 499 13 64
Imprime
Gráficas MARTE
Bienvenido al número 50, de julio/agosto de
2008, de dotNetManía.
¡Podemos! Éste ha sido el lema para la
selección española, campeona de Europa
2008. Es un lema simple, pero que dice más
de lo que parece: dice que nos enfrentamos
ante un reto difícil pero conseguible; que
somos capaces, más capaces de lo que muchos
creen; puede interpretarse como un mensaje
de auto-convencimiento o auto-motivación;
aunque también parece querer infundir tranquilidad a los nerviosos aficionados. Con este
ejemplar, van 50 dotnetmanías, y no es que signifique nada en especial, pero cumplir números redondos parece que haya que celebrarlo
de alguna forma, y qué mejor que hacer nuestro ese lema de la selección de española de
fútbol y decir ese ¡podemos! para afrontar los
próximos 50.
Para ayudarnos en tarea tan motivante
nos hemos aliado con Alhambra-Eidos para
conformar, difundir y participar en planes formativos para desarrolladores .NET y también
para profesionales TI, siempre en el entorno
de las tecnologías de Microsoft. Empezamos
con un laboratorio de dos días de Silverlight
2.0, impartido por nuestro redactor jefe Marino Posadas, del que puede ver más información en www.alhambra-eidos.es. Marino está
actualmente trabajando en el primer libro de
Silverlight 2.0 en castellano, que publicaremos como nuestro Cuaderno Técnico nº9
y que regalaremos con el patrocinio de Microsoft Ibérica en el número de octubre.
Siverlight 2.0, que ya está en Beta 2 y
con licencia Go Live, será –a mi entender–
una auténtica revolución para los programadores de la plataforma .NET, en tanto
que tendremos la posibilidad de programar con interfaces que hasta ahora eran
impensables. Nosotros mismos ya estamos
rediseñando nuestra página Web usando
Silverlight 2.0. Espero que puedan ver el
resultado entre septiembre y octubre de
este año.
Este mes entrevistamos a Dan Fernandez, Lead Product Manager para herramientas no profesionales en Microsoft y antiguo
Product Manager de C#. Empezamos a
hablar con él de las versiones Express y acabamos hablando de XNA, pasando por Silverlight y PopFly.
El artículo de portada “Integrándonos
continuamente” de Luis Fraile, a quien doy
la bienvenida en su primera intervención,
nos muestra como crear un entorno base de
integración continua con Visual Studio 2008
Team System no es nada complicado.
Por último, aunque no tenemos aún en
WPF una organización jerárquica en el mundo 3D similar a la ya existente en 2D y mientras esperamos a que el equipo de desarrollo de WPF complete la jerarquía para futuras versiones, publicamos el artículo “Paneles para el mundo 3D” del Grupo Weboo
de la Universidad de La Habana, que muestra cómo programar paneles tridimensionales y dotar a las aplicaciones de un aspecto
similar a los paneles de Windows Vista.
Y nada más. Como siempre, ¡dentro hay
mucho más! Espero que nuestro trabajo de
este mes sea de su agrado.
Paco Marín
ISSN
1698-5451
<< dotNetManía
Depósito Legal
M-3.075-2004
3
sumario 50
Factorías de software. Justificación y ventajas
10-11
El momento de evolución tecnológica y económica en que nos encontramos está fomentando la
creación de un nuevo modo de enfrentarse al problema de desarrollar software. Se trata de la
organización de los equipos de desarrollo en factorías que, típicamente, están deslocalizadas respecto
a los lugares en los que se generan las necesidades tecnológicas de los clientes..
Entrevista a Dan Fernandez
12-15
Otra de las entrevistas que pudimos “arañar” al programa oficial en el pasado Tech-Ed 2007 de
Barcelona fue con Dan Fernandez, del Developer and Platform Evangelism Team en Microsoft
(Redmond), quien nos atendió amablemente durante más de media hora en una de las salas
eventualmente vacías, para charlar de “productos y proyectos” de desarrollo, y también de “planes
vs. realidades”.
El Marco de trabajo de entidades de ADO.NET v3.5 (VII)
18-21
Continuando con nuestra serie dedicada a presentar las posibilidades que ofrecerá el Marco de
entidades de ADO.NET 3.5 para el modelado conceptual, en esta entrega mostraremos otros
mecanismos disponibles para la implementación de vistas, presentando la vía para mapear las vistas
definidas en el almacén relacional, así como las que tenemos a nuestra disposición para definir y
operar con vistas personalizadas expresadas en Entity SQL.
Paneles para el mundo 3D
22-27
Este artículo debe resultar un útil divertimento para la ya creciente familia de seguidores de WPF.
Aquí se verá cómo programar paneles tridimensionales para facilitar la distribución de diferentes
cuerpos en un Viewport3D. A la vez, el lector encontrará detalles de algunos elementos de la API
3D de WPF que le deberán resultar de utilidad.
Integrándonos continuamente
28-35
Una de las prácticas más recomendadas a la hora de realizar proyectos y empezar a asegurar la
calidad pronto es la de integrar todo el código lo más frecuentemente posible. Esto es especialmente
importante a la hora del desarrollo iterativo, mediante el cual gestionaremos los cambios de
requisitos de una forma más efectiva, entregando nuevas funcionalidades del software en cortos
periodos de tiempo.
Frameworks para Javascript
36-38
Las técnicas de desarrollo en Javascript, así como los usos que se le dan a esta tecnología, han
cambiado notablemente en los últimos años. Hoy en día, nos es difícil pensar que una aplicación Web
moderna pueda prescindir de esta tecnología para finalidades tan diversas como la comunicación
asíncrona entre cliente y servidor, la aplicación de efectos visuales a nuestro código HTML o
mantener nuestro propio sistema de caché en el lado del cliente.
Componentes de uso general
40-44
Otro mes, y otro componente. Veamos cómo nuestros otros componentes pueden relacionarse con el
sistema operativo, la seguridad, los usuarios… el Directorio activo.
Extender o no extender... Los métodos extensores en Visual Basic 2008
45-49
La versión de Visual Basic que se incluye con .NET Framework 3.5 o Visual Studio 2008 permite
que creemos métodos que extiendan la funcionalidad de clases existentes sin necesidad de tener acceso
al código fuente de éstas. En este artículo veremos cómo crear este tipo de métodos y qué
consideraciones debemos tener en cuenta para usarlos de la forma más adecuada.
dnm.todotnet.qa
50-52
Plantillas ASP.NET e inferencia de tipos en C#
Abordamos este mes un examen en profundidad de las propiedades de las plantillas de ASP.NET, y
concluiremos explicando los pros y contras del uso de la nueva palabra reservada var en C#.
dnm.laboratorio.net
Aspose.Total for .NET
54-56
Este mes presentamos Aspose.Total for .NET, una potente suite de componentes reutilizables creados y
comercializados por la empresa australiana Aspose, que pueden ser adquiridos conjuntamente o por separado
para ser incorporados a todo tipo de aplicaciones .NET.
dnm.biblioteca.net
57
Programming Microsoft LINQ
Professional C# 2008
dnm.desvan
58
<<dotNetManía
noticias noticias noticias noticias
noticias
6
Visual Studio 2008 y .NET Framework 3.5
Service Pack 1
Durante los meses de verano, se pondrá a disposición del público la versión final del
Service Pack 1 de Visual Studio 2008 y .NET Framework 3.5, de los que se liberaron
recientemente versiones beta.
Visual Studio 2008 y .NET
Framework 3.5 Service Pack
1 dan continuidad a la inversión de Microsoft en herramientas de desarrollo líderes
del mercado. El Service Pack 1 resuelve problemas encontrados tanto
mediante pruebas internas como gracias a información recibida de clientes y partners. En general, el Service
Pack 1 ofrece a sus usuarios tanto nuevas características como mejoras en la
estabilidad y el rendimiento de Visual
Studio 2008 y .NET Framework 3.5.
Descripción general
Con el Service Pack 1, Visual Studio
2008 introduce una gran cantidad de
nuevas características para el desarrollo
de aplicaciones para Windows, Office
y la Web. Los desarrolladores que creen aplicaciones basadas en .NET disfrutarán de un rendimiento superior del
diseñador de WPF, nuevos componentes para Visual Basic y Visual C++, así
como un ribbon de Office 2007 para las
MFC. Los desarrolladores de aplicaciones Web encontrarán múltiples
mejoras en el soporte para la programación de scripts del lado del cliente,
incluyendo el IntelliSense para Javascript. Adicionalmente, el soporte total
para SQL Server 2008, la incorporación de ADO.NET Entity Framework
y las mejoras de rendimiento del entorno hacen que el Service Pack 1 sea un
producto sumamente atractivo.
Por su parte, .NET Framework
3.5 Service Pack 1 ofrece más controles, una configuración más dirigida y mejoras en el rendimiento durante el arranque, así como potentes
características gráficas para el desa-
pleto de Javascript, herramientas mejoradas para AJAX y el
acceso a datos, y mejoras en el
despliegue de sitios Web.
rrollo de aplicaciones cliente y nuevas posibilidades de acceso a datos,
soporte mejorado para AJAX y otras
mejoras para el desarrollo Web. Adicionalmente, introduce ADO.NET
Entity Framework y ADO.NET Data
Services, que permitirán simplificar
aún más el código de acceso a datos
en las aplicaciones ofreciendo un
modelo conceptual extensible para
representar datos provenientes de
diversas fuentes y permitiendo que
este modelo refleje de un modo más
cercano los requisitos de negocio.
Visual Studio 2008 Service Pack 1
incluye:
• Diseñadores mejorados para el
desarrollo de aplicaciones WPF.
• Soporte completo para SQL Server 2008.
• La introducción del Diseñador de
entidades de ADO.NET.
• Componentes y herramientas para
Visual Basic y Visual C++ (incluyendo un ribbon de Office 2007
para MFC).
• Mejoras en Team Foundation Server para responder al feedback de
los clientes acerca de la usabilidad
y rendimiento del control de versiones, y ofrecer una mejor integración del e-mail con el seguimiento de ítems de proyectos y
soporte completo para la utilización de SQL Server 2008.
• Mejoras para el desarrollo Web,
incluyendo un soporte más com-
.NET Framework 3.5 Service Pack 1
incluye:
• Mejoras de rendimiento de entre
un 20 y un 45% en las aplicaciones WPF –sin necesidad de hacer
cambio alguno al código–.
• Mejoras en WCF que ofrecen más
control al desarrollador sobre el
acceso a datos y servicios.
• Experiencia de instalación mejorada para aplicaciones cliente.
• Mejoras en la plataforma de acceso
a datos, como ADO.NET Entity
Framework, ADO.NET Data Services y soporte para las nuevas características de SQL Server 2008.
Detalles adicionales
Adicionalmente, el Service Pack 1 de
.NET Framework y Visual Studio
2008 incluye otras varias nuevas características:
Cambios en WPF y el diseñador visual
• Mejoras en el rendimiento del
“arranque en frío” de entre un 20 y
un 45%, dependiendo del tamaño
de la aplicación, sin modificación
alguna al código.
• Características adicionales de WPF
para un mejor rendimiento del texto (especialmente cuando se utiliza
en elementos Visual y DrawingBrush), los gráficos (efectos como
DropShadow y Blur, antes implementados por software, ahora se
<< dnm.directo.noticias
Cambios en WCF y WF
• Nuevo Asistente de alojamiento para
los proyectos de servicio WCF.
• Mejoras en el Cliente de pruebas,
tales como el soporte para contratos
de mensajes y tipos anulables.
• Expansión del alcance del serializador de contratos de datos, relajando
la necesidad de aplicar los atributos
DataContract y DataMember en los tipos
y soportando un mecanismo interoperable para tratar las referencias a
objetos.
• Experiencia mejorada de depuración
en entornos de confianza parcial, con
soporte para el Registro de sucesos.
• Soporte para entidades de ADO.NET
Entity Framework en contratos WCF.
• Múltiples mejoras para crear servicios REST, como el soporte para la
publicación y consumo de ServiceDocument o un mayor control y usabilidad de UriTemplate.
• Mejoras significativas en el trabajo
con proyectos grandes basados en
WF dentro de Visual Studio.
• Mejora considerable en la escalabilidad de los servicios WCF alojados en
el modo integrado en IIS7.
.NET Framework 3.5 Optimized Client
Runtime
• .NET Framework 3.5 SP1 ofrece una
versión de instalación de .NET Fra-
mework optimizada para el desarrollo
de aplicaciones cliente. El tamaño final
de este runtime optimizado es menor
de 20 Mb.
Nuevos marcos de ADO.NET
.NET Framework 3.5 SP1 incluye dos
nuevos marcos de trabajo, ADO.NET
Entity Framework y ADO.NET Data Services, para ofrecer a los desarrolladores una
mayor flexibilidad y más opciones para el
desarrollo de aplicaciones que acceden y
utilizan datos.
ADO.NET Entity Framework
• El Marco de entidades de ADO.NET
(tecnología sobre la que vienen escribiendo desde hace varios meses nuestros columnistas Unai Zorrilla y Octavio Hernández) permitirá elevar el
nivel de abstracción en el manejo de
datos, y haciendo posible que la estructura de la base de datos evolucione sin
provocar un impacto significativo sobre
el código de la aplicación.
• En vez de programar contra filas y
columnas, el Marco de entidades permite la definición de un modelo de
datos de entidades sobre el almacén
relacional, y entonces programar en
términos de ese modelo. Los desarrolladores trabajarán con representaciones de los datos que tienen sentido para
la aplicación, y que se expresan en un
vocabulario más rico que incluye conceptos como la herencia, tipos complejos y relaciones explícitas.
• Un nuevo “sabor” de LINQ, LINQ
to Entities, permitirá definir consultas
fáciles de mantener que recuperan
objetos o entidades de negocio fuertemente tipados.
ADO.NET Data Services
• Los Servicios de datos de ADO.NET
ofrecen una infraestructura de primera clase para desarrollar la siguiente ola
de aplicaciones de Internet dinámicas,
permitiendo exponer los datos como
servicios REST que utilizan una sintaxis de URI uniforme y los verbos estándar de HTTP y que pueden ser consumidos por todo tipo de aplicaciones
clientes (por ejemplo ASP.NET, AJAX
y Silverlight).
• Los Servicios de datos ofrecen un marco de trabajo para crear servicios de
datos sobre almacenes relacionales
como SQL Server, MySQL, DB2 u
Oracle, utilizando el soporte del Marco de entidades, o para fuentes de datos
no relacionales utilizando el modelo de
proveedores suministrado.
Mejoras en Team Foundation Server
Varias mejoras y características adicionales han sido añadidas a Visual Studio Team System 2008 Team Foundation Server, incluyendo:
Control de versiones
• Mejoras en la experiencia de usuario
a través de nuevos diálogos.
• Mejoras en el Explorador de control
de código fuente.
• Soporte para ficheros no pertenecientes a una solución.
Seguimiento de Work Items
• Integración con Office 2007 mediante el ribbon estándar.
• Integración del correo electrónico en
el ciclo de desarrollo.
Herramienta de migración de Visual SourceSafe
• Numerosas mejoras en el rendimiento y fiabilidad de la herramienta.
Rendimiento y escalabilidad
• Múltiples mejoras en el rendimiento de diferentes tareas.
• Un mismo servidor puede soportar
muchos más proyectos; además, mejora sensiblemente la experiencia de usuario de un cliente que se conecta a un servidor con gran cantidad de proyectos.
Características adicionales
• Soporte para la utilización de SQL
Server 2008.
• Mejoras en Team System Web
Access, que mejoran la experiencia
de usuario de los clientes que no utilizan Team Explorer.
• Soporte para la creación de proyectos mediante scripts.
<<dotNetManía
realizan mediante aceleración de hardware) y los multimedios.
• WriteableBitmap muy mejorado, que
hace posible las actualizaciones de
mapas de bits en tiempo real desde
una superficie de software.
• Mejoras en la escalabilidad de los
datos, como el reciclado de contenedores y la virtualización de TreeView,
para ofrecer un mejor soporte para
la edición de datos.
• Múltiples novedades en el diseñador
de WPF, como el soporte para los
eventos en la ventana de propiedades, soporte del Cuadro de herramientas en el modo de código fuente, entre otras.
7
<< dnm.directo.noticias
Alhambra-Eidos llega a un acuerdo con dotNetManía para la
difusión conjunta de su oferta formativa
Alhambra-Eidos y dotNetManía ofrecerán conjuntamente cursos y libros especializados
en la plataforma .NET y otras tecnologías Microsoft como Windows Server, SQL Server,
SharePoint Server, BizTalk Server, Exchange Server, etc.
La compañía Alhambra Eidos y la
revista dotNetManía han alcanzado
un acuerdo estratégico por el cual dicho
medio, el más prestigioso entre la
comunidad de desarrolladores
de software en .NET en el
mundo de habla hispana, se alía
con la empresa de servicios,
líder en formación para desarrolladores especializados en productos Microsoft y centro de formación autorizado por Microsoft (CPLS).
Como consecuencia de este acuerdo, la revista difundirá la oferta formativa de Alhambra-Eidos y, a su vez, personal de dotNetManía asesorará y colaborará con el área de Formación de Alhambra-Eidos, optimizando y elaborando conjuntamente cursos y seminarios especializados en tecnologías .NET y otras tecnologías de
Microsoft como Windows Server, SQL Server, SharePoint, BizTalk, Exchange Server, etc.
Otro aspecto de
este acuerdo es la
colaboración de
Netalia –la editorial
de dotNetManía y
de los Cuadernos
Técnicos de dotNetManía– en la
edición y distribución de los libros editados por Alhambra-Eidos.
Además, Alhambra-Eidos ofrecerá a los lectores de dotNetManía ventajas exclusivas como descuentos permanentes en
cursos y libros.
Como comienzo de esta colaboración, Marino Posadas,
redactor jefe de dotNetManía, impartirá el laboratorio “Silverlight 2.0” de diez horas de duración, durante los días 16
y 17 de julio. Los lectores de dotNetManía tendrán un 10%
de descuento. Para más información sobre este laboratorio:
http://www.alhambra-eidos.es/ES/Formacion.
Liberada Beta 2 de Silverlight 2
<<dotNetManía
En el marco del pasado Tech-Ed USA celebrado en Orlando,
Microsoft liberó la Beta 2 de Silverlight 2, así como
actualizaciones de las herramientas de Visual Studio 2008 y
Expression Blend 2.5 que le dan soporte.
8
La Beta 2 añade una gran
cantidad de
nuevas características, no
obstante a lo
cual, es una
descarga de
solo 4.6 MB
que tarda menos de 10 segundos en instalarse. No requiere la presencia de
.NET Framework ni ningún otro software para funcionar, y todas sus características funcionan a través de diferentes navegadores en máquinas Windows
y Mac. Estas características también
estarán soportadas en Linux gracias a
Moonlight 2.
Silverlight 2 Beta 2 ofrece una
licencia Go Live que permite utilizar
y desplegar Silverlight 2 en aplicaciones comerciales, aunque se espera que
se produzcan algunos cambios en las
API entre la Beta 2 y el producto final,
por lo que muy probablemente habrá
que “retocar” esas aplicaciones cuando la versión final esté disponible.
Las nuevas características incorporadas a la Beta 2 pueden agruparse en
las siguientes categorías:
• Mejoras en interfaz de usuario y
controles.
• Mejoras en el soporte multimedios.
• Mejoras en el soporte de red.
• Mejoras en el manejo de datos.
Para más información y descargas,
visite http://www.silverlight.net.
Windows Server 2008 Hyper-V
disponible
Después del lanzamiento de Windows
Server 2008 sin la tan esperada característica de virtualización, por fin
Windows Server 2008 Hyper-V está
disponible para su descarga desde
http://www.microsoft.com/Hyper-V o vía
Windows Update desde el 8 de julio.
Hyper-V es la tecnología de virtualización basada en servidor de Microsoft,
que viene como una característica totalmente integrada en Windows Server
2008, disponible para ciertas ediciones
del sistema operativo.
Hasta la fecha se han distribuido algo
menos de 1,5 millones de copias de la beta
de Hyper-V, lo que demuestra el gran interés suscitado por la tecnología de virtualización, que competirá con los productos de VMWare.
Adicionalmente, System Center Virtual Machine Manager es la solución para
la centralización de la gestión de la infraestructura de virtualización. Más información en: http://www.microsoft.com/systemcenter/virtualmachinemanager.
opinión
Antonio Quirós
Factorías de software
Justificación y ventajas
El momento de
evolución tecnológica y
económica en que nos
encontramos está
fomentando la creación
de un nuevo modo de
enfrentarse al problema
de desarrollar software.
Se trata de la
organización de los
equipos de desarrollo en
factorías que,
típicamente, están
deslocalizadas respecto
a los lugares en los que
se generan las
necesidades tecnológicas
de los clientes.
Antonio Quirós es
colaborador habitual
de dotNetManía. Cofundador de las revistas clippeRManía, FOXManía y Algoritmo.
Actualmente es director de operaciones en
Alhambra-Eidos.
<< A la base de esta nueva situación se encuentran las siguientes consideraciones:
• La mayor demanda de soluciones de software se ubica en
los países con un alto desarrollo económico y un tejido
empresarial fuerte. El personal especializado en estos países es escaso y de alto coste, por lo que frecuentemente
no alcanza a satisfacerse la demanda con los recursos internos que se poseen.
• Dentro de los propios países desarrollados existen áreas
de muy fuerte desarrollo empresarial y otras que se caracterizan por un menor nivel del mismo. Sin embargo, en
Europa al menos, la inversión pública lleva hacia todas las
áreas prácticamente por igual el proceso de difusión de la
enseñanza técnica especializada, existiendo, por tanto,
exceso de capacidad productiva en ciertas áreas demográficas y falta de la misma en otras.
• El desarrollo de software es un proceso altamente industrializable y, por tanto, sometido a reglas similares a las
que se están dando en otras partes de la industria y la tecnología. Nos referimos al fenómeno de la deslocalización
de la producción.
Todo esto nos está llevando a una situación en la cual la
parte de la producción que se encuentra cercana al cliente (planificación, diseño funcional, implantación y parte del mantenimiento) se realizan en ubicaciones cercanas a donde se
encuentran los usuarios, mientras que la construcción o incluso el diseño técnico tienden a realizarse en ubicaciones remotas donde es más fácil cubrir la demanda de personal técnico
especializado.
Extremando esta argumentación, estaríamos en el proceso
del off-shoring, es decir, la construcción de software en países
con economías emergentes (típicamente India, Rusia o Latinoamérica). Sin embargo, incluso dentro de nuestro país está
<< dnm.opinión
En esto se sigue el mismo esquema
(o al menos uno similar) al que en la
producción industrial ordinaria se establece entre la ingeniería y la producción o línea de montaje. Lo importante de este sistema es que permite deslindar y formalizar, por tanto, mejor,
procesos que no siempre han estado
bien separados. Es decir, que de la propia necesidad del mercado en recorrer
este camino se sigue la necesidad para
los informáticos de encapsular mejor
cada uno de sus procedimientos a fin
de que puedan ser realizados de forma
separada por equipos diferentes y que
pueden estar alejados por el espacio
miles de kilómetros unos de otros y por
el tiempo según la diferencia horaria
que corresponda.
La industria informática lleva años
trabajando fuertemente en el planteamiento de modelos que mejoren la
encapsulación de sus procedimientos
y en metodologías que permitan protocolizar los distintos pasos que se dan
De la propia necesidad del mercado en recorrer este
camino se sigue la necesidad para los informáticos de
encapsular mejor cada uno de sus procedimientos a
fin de que puedan ser realizados de forma separada
por equipos diferentes
en todo el ciclo de vida de producción
de software. Esto nos lleva a un escenario donde podemos decir que el
estadio de desarrollo en que nos
encontramos respecto a este proceso
de (valga la redundancia) mejora de
procesos es bastante alto. Prueba de
esto son estándares comúnmente aceptados como UML (Lenguaje Unificado de Modelado) o MDA (Arquitectura Guiada por Modelos), ambos en
el área de diseño, o metodologías
como CMMI, ITIL, COBIT o las
basadas en ISO 9001 respecto a la protocolización de la construcción de software, la prestación de servicios
informáticos o el gobierno de las actividades TIC de las empresas.
En nuestro país existe un fuerte
desequilibrio interregional respecto al
volumen de negocio que el mundo TIC
mueve en cada una de las comunidades, de forma que éste se concentra
mayoritariamente en Madrid y Cataluña donde existe más demanda de titulados de los que las facultades de
informática pueden proveer, estando
por otra lado el resto de las Comunidades donde lo normal es que la universidad genere más titulados de los
que el mercado puede absorber. Este
desequilibrio fomenta un fenómeno de
inmigración tecnológica interna, donde los titulados de ciertas universidades han de verse forzados a emigrar a
Madrid o Barcelona para poder abastecer la demanda de titulados de dichos
mercados, así como para garantizar los
niveles salariales adecuados al desempeño de dichos puestos. Este fenóme-
no, desde luego, no contribuye al equilibro económico y demográfico del país
sino que es una semilla continua que
abona el crecimiento de los grandes
polos de atracción económica española en el área TIC (Madrid y Barcelona), consiguiendo cada vez más que el
talento que otras universidades regionales liberan no se quede “en casa”,
fijando la población al territorio y equilibrando la demografía y la renta de las
distintas áreas de población.
Sin embargo, el mundo del software abre expectativas más que interesantes para atajar este problema. Desde hace algunos años en España se está
dando la tendencia, entre las empresas
más grandes del sector TIC, a abrir
Factorías de Software para deslocalizar
su producción y aprovecharse de las
posibilidades de captar talento directamente en sus fuentes (las universidades
de las comunidades donde se instalan).
Este fenómeno va a contribuir muy
positivamente al equilibrio económico
y demográfico a nivel interregional, a
la vez que auspicia que las empresas tengan que mejorar sus procesos para
garantizar que esta producción deslocalizada se realice con los niveles de eficiencia y calidad que nuestro mundo
demanda. Hay que ser conscientes también de que el desarrollo de esta mecánica nos ayudará como país a mejorar
nuestro posicionamiento en cuanto a
productividad y generación de valor
añadido, epígrafes en los que nos
encontramos no demasiado bien posicionados respecto a muchos de nuestros socios europeos.
<<dotNetManía
creciendo fuertemente la tendencia de
que compañías que tienen que atender
una importante demanda nacional e
incluso internacional, y que se encuentran ubicadas en Madrid y/o Barcelona, están creando factorías de software en provincias donde no existe
demanda empresarial para dar salida a
los titulados de las facultades de
Informática y donde les resulta mucho
más fácil acceder a dicho mercado de
personal técnico. De este modo, los
equipos de desarrollo de software tienden a desdoblarse en dos áreas claramente diferenciadas:
a) Aquella que atiende las necesidades de relación inmediata con
el cliente y que por tanto se
encarga de actividades normalizadas de diseño, tales como toma
de requisitos, casos de uso, validación de actas con el cliente,
planificación, etc., así como las
de despliegue, implantación, formación, etc.
b) La que se encarga de realizar los
diseños técnicos, así como la
construcción del software y las
pruebas sobre el mismo.
11
entrevista
Marino Posadas
entrevista a
Marino Posadas
es consultor independiente y redactor jefe de dotNetManía. Puedes leer
su blog en
http://www.
elavefenix.net.
Dan Fernandez
Otra de las entrevistas que pudimos “arañar” al programa oficial en
el pasado Tech-Ed 2007 de Barcelona fue con Dan Fernandez, del
Developer and Platform Evangelism Team en Microsoft (Redmond),
quien nos atendió amablemente durante más de media hora en una
de las salas eventualmente vacías, para charlar de “productos y proyectos” de desarrollo, y también de “planes vs. realidades”.
<< dnm.directo.entrevista
¿Cómo defines el propósito de
Microsoft –lo que se tiene en mente– a la hora de crear este tipo de
herramientas que, por definición,
son gratuitas?
Nuestro propósito es crear un
conjunto de herramientas que faciliten la “democratización del desarrollo de software”. Con Visual Studio
Express queríamos conseguir algo
sencillo y fácil de utilizar, que estuviera disponible para principiantes y
entusiastas del desarrollo no profesional. Y lo que hacemos ahora es la
continuación lógica de ese esfuerzo
inicial. Ahora disponemos de muchos
más recursos como el sitio Web
“Beginner Developer Learning Center” ( http://msdn.microsoft.com/
en-us/beginner/default.aspx) y
otros recursos que suponen inversiones importantes en ese sentido, como
los libros de programación para novatos, de forma que cualquiera que no
Nuestro propósito es crear un conjunto de
herramientas que faciliten la “democratización
del desarrollo de software”
Y antes de esto estabas relacionado directamente con el equipo de
desarrollo de C#, si no recuerdo
mal.
Sí, antes de este trabajo fui Visual
C# Product Manager, y tuve la oportunidad de trabajar con muchos de los
creadores del producto como Anders
Hejlsberg o Scott Nonnenberg, y
otros.
tenga experiencia pueda también
acercarse al mundo del desarrollo y
encontrar fácilmente un punto de
partida.
¿Tiene esto que ver con el movimiento de introducción o presentación del entorno .NET en el mundo universitario, donde habitualmente se ignoran las herramientas
de desarrollo de Microsoft?
Sí. De hecho, podemos decir que
los principales usuarios de estos productos son estudiantes, que lo obtienen de diversas formas, y al comenzar a trabajar se ven necesitados de
los recursos adicionales que te comentaba hace un momento. Son elementos importantes para que puedan
empezar enseguida a ver algunos
resultados.
Cambiando un poco “el tercio”,
algunas empresas están empezando
a requerir la figura del diseñador de
aplicaciones, además del desarrollador clásico. ¿Apreciáis vosotros
igualmente esa tendencia en vuestros análisis de mercado?
Sí, eso es notable también en
EE.UU. Ciertamente, muchas veces
depende del volumen de la empresa
de que se trate: cuanto más grande,
más roles especializados te encuentras, porque eso permite no “repetir
el conocimiento” de forma innecesaria y avanzar más en el concepto de
“factoría de software”. Ése es el lugar
exacto de la suite Expression, el de servir de enlace entre estos roles, ofreciéndoles un lugar común de conexión de sus trabajos.
¿Cuál piensas que es el período
adecuado entre dos versiones de
Visual Studio (ya sean profesionales o Express)? ¿Dos años, como
afirman algunos analistas?
Entiendo muy bien la pregunta y
creo que también el porqué. Pero
debo decir que para nosotros las
fechas no son una prioridad. Nosotros establecemos los contenidos de
una versión basándonos principalmente en el feedback de nuestros
clientes, de foros de desarrollo, de
nuestros partners, etc. Y cuando se
decide cuáles van a ser los contenidos de la siguiente versión, en lo único que se piensa es en que, cuando
salga al mercado, esté lo suficientemente probada, estable y productiva
como para suponer un aliciente de
actualización.
<<dotNetManía
Ya es tradicional que el entrevistado para dotNetManía se presente
a sí mismo. De esa forma matamos
dos pájaros de un tiro, porque más
de una vez mi interlocutor ya no
ocupaba la posición que aparecía
en su sitio Web o en su blog… Así
que, aunque me dijiste ayer que
trabajabas con un antiguo entrevistado por mi revista (Arturo
Toledo), prefiero que sigas con la
tradición…
Por supuesto, mi nombre es Dan
Fernandez, y soy Lead Product
Manager para herramientas no profesionales en Microsoft. Eso significa especialmente las herramientas que
genéricamente conocemos como
herramientas “Express”. En realidad,
como sabes, es una suite completa de
herramientas de desarrollo, cada una
especializada en una tecnología (Web
Developer o SQL Server) o un lenguaje (C#, VB.NET, C++).
13
<< dnm.directo.entrevista
Millones de programadores en VB.NET y C# podrán reutilizar su
código para colocarlo en aplicaciones Silverlight, y eso sí que es
totalmente novedoso en el mundo del desarrollo Web
<<dotNetManía
¿Se espera un crecimiento paralelo de las capacidades de la suite Expression con respecto a
Visual Studio? ¿O van a seguir una progresión
independiente?
Más que funcionar en paralelo, lo que pensamos es que deben tener un mismo objetivo común,
y que aquellas cosas necesarias para el diseño de
una aplicación (independientemente de la versión
de Visual Studio de que se trate), siempre estén
soportadas en la suite Expression. Y especialmente, que ambos productos siempre funcionen muy
bien juntos.
14
El gran “boom” que se espera en el mundo del
desarrollo Web (o al menos así nos lo están
presentando los principales fabricantes) tiene
que ver con las aplicaciones RIA (Rich Internet Applications). ¿Cuál es tu opinión acerca
de la importancia de Silverlight en este contexto?
Para mí, la palabra para definir Silverlight es
“emocionante”. Y lo digo especialmente porque he
estado implicado directamente en el desarrollo de
PopFly, y he podido ver la demanda y la aceptación que esa herramienta de construcción de sitios
sin necesidad de código fuente ha tenido desde el
comienzo y sigue teniendo. Y eso ya llevaba algunos elementos que no eran sino la promesa de lo
que empieza a ser Silverlight. Una increíble interfaz de usuario (por fin), pero desde el punto de vista de desarrollador que soy, hay dos cosas que me
gustan especialmente: una es la posibilidad de que
las aplicaciones se construyan (al menos en buena
parte) en tiempo de ejecución, como algo dinámico, dependiendo de otros factores que tienen que
ver con el uso que le da a la aplicación el navegante Web, y aprovechando las capacidades del lenguaje XAML.
La otra cosa que resulta especialmente interesante es la posibilidad de tener VB.NET o C# en
el cliente, sin que ello dependa de que se esté utilizando un navegador o una plataforma concreta,
sino que podamos usarlo para cualquier navegador, o en Linux o Mac. Creo que esa riqueza y esa
capacidad de cobertura es lo más importante. Esa
posibilidad de utilizar C#, por ejemplo, puede producir ganancias, como la que demostró Scott
Guthrie recientemente con un juego de ajedrez
escrito en Javascript tradicional y su equivalente
en C# casi ¡1000 veces! más rápido en la versión
escrita en C#.
Además, existen millones de programadores en
VB.NET y C# que podrán reutilizar su código para
colocarlo en aplicaciones Silverlight, y eso sí que
es totalmente novedoso en el mundo del desarrollo Web.
Ya que lo mencionas, dinos algo más acerca de
PopFly… en los foros hay mucha gente comentando sus experiencias…
PopFly es una aproximación distinta al mundo
del desarrollo Web. Asumimos que todo el mundo
tiene que tener el derecho a hacer su sitio Web sin
necesidad de hacer una carrera, y suministramos
las herramientas prefabricadas para que esto sea
así. Se trata, fundamentalmente, de permitir que
el creador utilice estos “elementos prefabricados”
en una labor similar a la de un puzle, hasta conseguir en cada caso el efecto deseado. Lo que hemos
hecho es analizar los distintos tipos de blogs que
mantiene la gente, qué les gusta utilizar, cómo les
gusta compartir su información, etc. Y a partir de
todo eso se ha construido PopFly.
Ya que estamos hablando de software que hace
fácil la vida al desarrollador, es inevitable citar
una herramienta por
la que existe mucha
expectación en el
mundo de los aficionados a los videojuegos (que es enorme).
Me refiero a XNA.
¿Cómo ves este producto y que expectativas de crecimiento
y aceptación popular
le concedes?
XNA es sin duda
una de mis herramientas favoritas. La
meta es permitir que
cualquiera se pueda
adentrar en el mundo
del desarrollo de juegos (y también de
Marino Posadas y Dan Fernandez en un momento de la entrevista
aplicaciones de multimedia y otros tipos)
de una forma muy
sencilla, y crear aplicaciones para la XBOX o para
te siente la necesidad de verse expresado en la Web.
Windows y distribuirlas con facilidad. El único
Este movimiento va a continuar porque la gente lo
requisito es tener conocimientos de programación
demandará cada vez más y más. Y se producirá una
en lenguaje C#, si bien, dependiendo del juego,
segunda “explosión Web” asociada a esta demopueden requerirse conocimientos gráficos, aunque
cratización. Explosión que ya está implícita de forXNA Studio hace que esa labor sea muchísimo más
ma clara en lo que llamamos Web 2.0. Y esto no es
fácil.
solo para las típicas aplicaciones como blogs o sitios,
De hecho, ahora puede incluirse como “add-in”
sino también para otros tipos de aplicaciones Web,
de Visual C# Express. Incluso te diré más: existen
como programas de mensajería instantánea, redes
universidades norteamericanas (y alguna extranjeP2P, etc.
ra) que están preparando lo que se denomina
El reto es construir software que permita que
“Currículo XNA”, para capacitar en la construcese escenario sea del tipo “dos líneas de código”
ción de este tipo de aplicaciones. Es otro concepsin perder (en la medida de lo posible) las posibito del aprendizaje que allí se denomina “Learning
lidades de personalización que cualquier autor
by having fun” (aprendizaje mediante el entreteninecesita.
miento). Y está teniendo una repercusión que nosotros mismos no imaginábamos en principio.
A la vista de tu apellido, hay una pregunta que
tengo que hacerte para concluir esta entrevisSiempre tratamos de visualizar cuál será el
ta: ¿es la primera vez que vienes a España?
panorama del desarrollo dentro de unos pocos
¡Qué va! De hecho, tengo familia aquí. Un tío
años, sobre todo para la toma de decisiones
y un primo míos viven aquí, en Barcelona, y ya he
empresariales que nos ayuden a no pasar por
estado otras veces, especialmente en vacaciones de
alto cosas importantes. ¿Cómo ves tú este panoAño Nuevo. Mis padres son originalmente colomrama?
bianos, aunque mi abuelo era español.
Pues creo que esta democratización del software va a continuar. La diferencia es que no todo el
Pues nos alegramos. Muchas gracias en nommundo siente la necesidad de construir un puente,
bre de todos los lectores por tu tiempo y tus
pongamos por caso, pero, cada vez más, mucha geninteresantes propuestas.
<<dotNetManía
<< dnm.directo.entrevista
15
eventos
eventos
Solid Quality
SUMMIT
Con enorme éxito de crítica y público, Solid Quality™
Iberoamérica (www.solidq.com/ib) organizó y llevó a buen
fin en Madrid, España, el cuarto Solid Quality Summit, durante los días 23, 24 , 25, 26 y 27 de junio.
Durante esos cinco días, los asistentes –entre los que
se incluyeron representantes de empresas y organizaciones de reconocido renombre en España– tuvieron la
oportunidad de asistir a más de 60 sesiones, distribuidas
en los tres tracks ofrecidos en el evento (Inteligencia de
Negocios, Motor Relacional y Tecnologías .NET). Este
año, como novedad, los asistentes pudieron confeccionar de antemano sus propias agendas, lo que les permitió planificar mejor su tiempo y aprovecharlo al máximo. Asimismo, se puso a disposición de los participantes un portal exclusivo, repleto de contenidos como las
presentaciones realizadas, vídeos y código de las demostraciones y libros electrónicos, entre otros.
Alejandro Leguizamo, director de marketing,
declaró: “Este cuarto año ha sido una experiencia muy
positiva para nosotros. Hemos logrado nuestra consoli-
dación como el punto de referencia en el mercado
español alrededor de SQL Server y .NET. Hemos sido
los primeros en la industria (aparte de Microsoft) en estar
integrados desde fases muy tempranas en el proceso de
desarrollo de SQL Server (tanto 2005 como 2008) con
el grupo de producto en Redmond, y durante este evento hemos podido entregar ese conocimiento de primera mano a nuestros clientes, garantizando la mejor formación”.
<<dotNetManía
MAD.NUG reinicia sus actividades
16
El pasado jueves 26 de junio
tuvo lugar, en las oficinas de
Microsoft, el evento con el
que MAD.NUG (el Grupo
de usuarios .NET de Madrid) retomó sus actividades
después de unos meses de pausa. Como complemento
al evento, los asistentes pudimos disfrutar del emocionante segundo tiempo del partido de semifinales de la
Eurocopa '08 entre España y Rusia, para lo que Microsoft gentilmente suministró la infraestructura y los aperitivos necesarios.
El evento, bajo el título “Déjate engatusar por
Visual Studio 2008”, tuvo como objetivo mostrar algunas de las posibilidades que pone esta nueva versión del
entorno de desarrollo a disposición de los desarrolladores. Después de la introducción general a cargo de
David Herráiz (cuya presentación de las novedades en
ASP.NET 3.5 finalmente quedó para una próxima ocasión), David Carmona (Microsoft) nos mostró la nue-
va versión de MSDN Vídeo, desarrollada desde cero
con .NET 3.5 y VS 2008, haciendo énfasis en la arquitectura del sistema y en los momentos en los que se hace
utilización de nuevas tecnologías como LINQ, WPF o
WCF. Posteriormente entraron en acción Luis Fraile
y Bruno Capuano para hablar sobre algunas de las nuevas posibilidades incluidas en VS Team System para el
análisis y control de calidad del código. Y finamente,
Jorge Serrano y Octavio Hernández hicieron un recorrido por las novedades de Visual Basic 9.0, incluyendo
las relativas a LINQ.
El próximo evento de MAD.NUG ya tiene fecha:
será el jueves 24 de julio, y en él Roberto González,
MVP de Biztalk, tendrá a su cargo la charla “Integración de WCF y WF en .NET 3.5”.
Más información en http://info.madriddotnet.com.
Octavio Hernández
Unai Zorrilla
Octavio Hernández
plataforma.net
El Marco de trabajo de entidades
de ADO.NET v3.5 (VII)
Continuando con nuestra serie dedicada a presentar las posibilidades
que ofrecerá el Marco de entidades de ADO.NET 3.5 para el modelado conceptual [1], en esta entrega mostraremos otros mecanismos disponibles para la implementación de vistas, presentando la vía para mapear las vistas definidas en el almacén relacional, así como las que tenemos a nuestra disposición para definir y operar con vistas personalizadas expresadas en Entity SQL.
Unai Zorrilla es
Development Team
Leader de Plain Concepts
y tutor de campusMVP.
MVP desde 2004,
colabora activamente con
Microsoft en eventos de
arquitectura y
desarrollo, así como en
giras de productos. Autor
del libro "Modelando
procesos de negocio con
Workflow Foundation".
Octavio Hernández es
Mentoring Team Leader
de Plain Concepts, editor
técnico de dotNetManía y
tutor de campusMVP.
Es MVP de C# desde
2004, MCSD y MCT.
Autor del libro "C# 3.0 y
LINQ".
Como ya es habitual, basaremos los ejemplos de
este artículo en la base de datos FUTBOL2006 del libro
“C# 3.0 y LINQ” [2]. El código de ejemplo asociado a este artículo (que puede ser descargado
desde www.dotnetmania.com) ha sido creado utilizando el Service Pack 1 de Visual Studio 2008, que
incluye una versión cercana a la definitiva del Marco de Entidades.
Mapeo de vistas de servidor
Del mismo modo que el Marco de entidades ofrece una vía para mapear de manera declarativa los
procedimientos almacenados y funciones definidas por el usuario que devuelven conjuntos de
datos, como hemos descrito en el artículo anterior de la serie, ofrece asimismo una vía directa
para “sacar a la superficie” de un modelo de entidades las vistas definidas directamente en el
almacén de datos mediante el lenguaje del almacén
(Transact-SQL, en el caso de SQL Server, PLSQL en el caso de Oracle, etc.). Por ejemplo, suponiendo que se ha definido sobre la base de datos
FUTBOL2006 la vista RealMadrid que se presenta en
el listado 1, ésta quedaría reflejada en el modelo
del almacén generado por el asistente integrado
en Visual Studio mediante el fragmento de docu-
mento SSDL que se muestra en el listado 2. Observe cómo los atributos store:Type, store:Schema y
store:Name y el elemento <DefiningQuery> reflejan
en el modelo las propiedades básicas de la vista. A
su vez, a nivel conceptual se modelaría la definición anterior mediante la creación de la entidad
que se muestra en el fragmento de documento
CSDL del listado 3. Y por último, la correspondencia entre las entidades lógica y física quedaría
expresada mediante el mapeo que muestra el fragmento MSL del listado 4. El resultado final será
que podremos acceder al conjunto de resultados
que la vista produce utilizando la consulta LINQ
que se muestra en el listado 5.
CREATE VIEW [dbo].[RealMadrid]
AS
SELECT TOP (100) PERCENT dbo.Futbolista.Id,
dbo.Futbolista.Nombre,dbo.Futbolista.Posicion,
dbo.Pais.Nombre AS NombrePais
FROM dbo.Futbolista INNER JOIN dbo.Pais
ON dbo.Futbolista.CodigoPaisNacimiento =
dbo.Pais.Codigo
WHERE dbo.Futbolista.CodigoClub = ‘RMA’
ORDER BY dbo.Futbolista.Posicion,
dbo.Futbolista.Nombre
Listado 1.Vista de servidor definida en Transact-SQL.
<< dnm.plataforma.net
Listado 2. Fragmento de documento SSDL.
<edmx:ConceptualModels>
<Schema Namespace=”FUTBOL2006” Alias=”Self”
xmlns=”http://schemas.microsoft.com/ado/2006/04/edm”>
<EntityContainer Name=”FUTBOL2006Context”>
<!— ... —>
<EntitySet Name=”RealMadrid” EntityType=”FUTBOL2006.RealMadrid” />
<!— ... —>
</EntityContainer>
<!— ... —>
<EntityType Name=”RealMadrid”>
<Key>
<PropertyRef Name=”Id” />
<PropertyRef Name=”Nombre” />
<PropertyRef Name=”Posicion” />
<PropertyRef Name=”NombrePais” />
</Key>
<Property Name=”Id” Type=”Int32” Nullable=”false” />
<Property Name=”Nombre” Type=”String” Nullable=”false” MaxLength=”75”
Unicode=”false” FixedLength=”false” />
<Property Name=”Posicion” Type=”String” Nullable=”false” MaxLength=”1”
Unicode=”false” FixedLength=”true” />
<Property Name=”NombrePais” Type=”String” Nullable=”false” MaxLength=”50”
Unicode=”false” FixedLength=”false” />
</EntityType>
<!— ... —>
</Schema>
</edmx:ConceptualModels>
Listado 3. Fragmento de documento CSDL.
El Marco de entidades
ofrece una vía directa
para “sacar a la superficie”
de un modelo de
entidades las vistas definidas
directamente en el
almacén de datos
Vistas de consulta
Además de permitir “sacar a la superficie” las vistas definidas directamente en la base de datos, el Marco de entidades ofrece una potente infraestructura basada en vistas de cliente, una de
cuyas ventajas principales es la de hacer
posible encapsular la complejidad asociada a la obtención de los datos, de
forma que ésta no permee el código de
las aplicaciones. Estas vistas funcionan
totalmente en el lado del cliente, de
modo que los desarrolladores pueden
crear vistas adecuadas a las necesidades concretas de cada aplicación sin
afectar a la base de datos u otras aplicaciones. El lenguaje en el que se
expresan estas vistas es Entity SQL,
un lenguaje muy similar en estructura
al SQL tradicional, pero preparado
para manejar los nuevos conceptos que
introduce el Marco de entidades como
la herencia, las relaciones, los tipos
complejos, etc. Del análisis de las sentencias Entity SQL y su traducción al
dialecto de SQL del almacén subyacente se encarga el Proveedor de
Entidades (Entity Provider), un nuevo proveedor de ADO.NET que interactúa con el proveedor “nativo”
correspondiente para ejecutar la sentencia traducida y recuperar los resultados (figura 1).
<<dotNetManía
<edmx:StorageModels>
<Schema Namespace=”FUTBOL2006Model.Store” Alias=”Self”
Provider=”System.Data.SqlClient” ProviderManifestToken=”2005”
xmlns=”http://schemas.microsoft.com/ado/2006/04/edm/ssdl”>
<EntityContainer Name=”dbo”>
<!— ... —>
<EntitySet Name=”RealMadrid” EntityType=”FUTBOL2006Model.Store.RealMadrid”
store:Type=”Views” store:Schema=”dbo” store:Name=”RealMadrid”>
<DefiningQuery>SELECT
[RealMadrid].[Id] AS [Id],
[RealMadrid].[Nombre] AS [Nombre],
[RealMadrid].[Posicion] AS [Posicion],
[RealMadrid].[NombrePais] AS [NombrePais]
FROM [dbo].[RealMadrid] AS [RealMadrid]</DefiningQuery>
</EntitySet>
<!— ... —>
</EntityContainer>
<!— ... —>
<EntityType Name=”RealMadrid”>
<Key>
<PropertyRef Name=”Id” />
<PropertyRef Name=”Nombre” />
<PropertyRef Name=”Posicion” />
<PropertyRef Name=”NombrePais” />
</Key>
<Property Name=”Id” Type=”int” Nullable=”false” />
<Property Name=”Nombre” Type=”varchar” Nullable=”false” MaxLength=”75” />
<Property Name=”Posicion” Type=”char” Nullable=”false” MaxLength=”1” />
<Property Name=”NombrePais” Type=”varchar” Nullable=”false” MaxLength=”50” />
</EntityType>
<!— ... —>
</Schema>
</edmx:StorageModels>
19
<< dnm.plataforma.net
<edmx:Mappings>
<Mapping Space=”C-S”
xmlns=”urn:schemas-microsoft-com:windows:storage:mapping:CS”>
<EntityContainerMapping
StorageEntityContainer=”dbo”
CdmEntityContainer=”FUTBOL2006Context”>
<!— ... —>
<EntitySetMapping Name=”RealMadrid”>
<EntityTypeMapping TypeName=”IsTypeOf(FUTBOL2006.RealMadrid)”>
<MappingFragment StoreEntitySet=”RealMadrid”>
<ScalarProperty Name=”Id” ColumnName=”Id” />
<ScalarProperty Name=”Nombre” ColumnName=”Nombre” />
<ScalarProperty Name=”Posicion” ColumnName=”Posicion” />
<ScalarProperty Name=”NombrePais” ColumnName=”NombrePais”/>
</MappingFragment>
</EntityTypeMapping>
</EntitySetMapping>
<!— ... —>
</EntityContainerMapping>
</Mapping>
</edmx:Mappings>
Listado 4. Fragmento de documento MSL.
private void button1_Click(object sender, EventArgs e)
{
using (var ctx = new FUTBOL2006Context())
{
var q = from f in ctx.RealMadrid
where f.Posicion == “P”
orderby f.Id
select f.Nombre;
foreach (var r in q)
MessageBox.Show(r);
}
}
<<dotNetManía
Listado 5. Consulta LINQ que utiliza la vista de servidor mapeada.
20
La definición estática de vistas del
lado cliente (conocidas como vistas de
consulta) se realiza mediante el elemento <QueryView> del fichero de
mapeo (MSL), que encapsula una sentencia Entity SQL definida sobre el
modelo del almacén. Estas vistas luego se mapean igualmente en el CSDL
como entidades de pleno derecho. El
listado 6 muestra un fragmento del
documento combinado EDMX en el
que se define una vista de consulta que
permite recuperar los porteros del Real
Madrid. Por su parte, el listado 7 muestra cómo obtener esos datos utilizando
una consulta LINQ, la vía más natural,
que podríamos utilizar, por ejemplo,
como fuente de enlace a datos.
<?xml version=”1.0” encoding=”utf-8”?>
<edmx:Edmx Version=”1.0”
xmlns:edmx=”http://schemas.microsoft.com/ado/2007/06/edmx”>
<edmx:Runtime>
<!— SSDL content —>
<edmx:StorageModels>
<!— ... —>
</edmx:StorageModels>
<!— CSDL content —>
<edmx:ConceptualModels>
<Schema Namespace=”FUTBOL2006” Alias=”Self”
xmlns=”http://schemas.microsoft.com/ado/2006/04/edm”>
<EntityContainer Name=”FUTBOL2006Context”>
<!— ... —>
<EntitySet Name=”PorteroRealMadrid”
EntityType=”FUTBOL2006.PorteroRealMadrid” />
<!— ... —>
</EntityContainer>
<!— ... —>
<EntityType Name=”PorteroRealMadrid”>
<Key>
<PropertyRef Name=”Id” />
<PropertyRef Name=”Nombre” />
</Key>
<Property Name=”Id” Type=”Int32” Nullable=”false” />
<Property Name=”Nombre” Type=”String” Nullable=”false”
MaxLength=”75” Unicode=”false” FixedLength=”false” />
</EntityType>
</Schema>
</edmx:ConceptualModels>
<!— C-S mapping content —>
<edmx:Mappings>
<Mapping Space=”C-S”
xmlns=”urn:schemas-microsoft-com:windows:storage:mapping:CS”>
<EntityContainerMapping StorageEntityContainer=”dbo”
CdmEntityContainer=”FUTBOL2006Context”>
<!— ... —>
<EntitySetMapping Name=”PorteroRealMadrid”>
<QueryView>
SELECT VALUE FUTBOL2006.PorteroRealMadrid(F.Id, F.Nombre)
FROM dbo.Futbolista AS F
WHERE F.Posicion = ‘P’ AND F.CodigoClub = ‘RMA’
</QueryView>
</EntitySetMapping>
</EntityContainerMapping>
</Mapping>
</edmx:Mappings>
</edmx:Runtime>
<edmx:Designer
xmlns=”http://schemas.microsoft.com/ado/2007/06/edmx”>
<!— ... —>
</edmx:Designer>
</edmx:Edmx>
Listado 6. Fragmentos relevantes en la definición de una vista de consulta.
private void button2_Click(object sender, EventArgs e)
{
using (var ctx = new FUTBOL2006Context())
{
var q = from f in ctx.PorteroRealMadrid
select f;
foreach (var r in q)
MessageBox.Show(r.Nombre);
dataGridView1.DataSource = q.ToList();
}
}
Listado 7. Consulta integrada contra la vista de consulta.
<< dnm.plataforma.net
Aunque no lo mostraremos aquí
para no repetirnos, es de destacar que
a estas “entidades virtuales” (tanto las
vistas de servidor mapeadas como las
vistas de consulta) en principio también
se les puede asociar en el modelo procedimientos almacenados para la inserción, modificación y borrado, de un
modo similar a como lo hicimos en
nuestro artículo anterior para las funciones.
Consultas ad-hoc
Figura 1. El Entity Provider traduce las sentencias
Entity SQL al dialecto del almacén.
private void button3_Click(object sender, EventArgs e)
{
using (var con = new EntityConnection(ConfigurationManager.
ConnectionStrings[“FUTBOL2006Context”].ConnectionString))
{
con.Open();
using (EntityCommand cmd = con.CreateCommand())
{
cmd.CommandText =
@”SELECT F.Id AS Id, F.Nombre AS Nombre, F.Posicion AS Posicion,
F.Pais.Nombre AS NombrePais
FROM Futbol2006Context.Futbolista AS F
WHERE F.Club.Codigo = @Club AND F.Posicion = @Pos
ORDER BY F.Posicion, F.Nombre”;
cmd.Parameters.AddWithValue(“Club”, “RMA”);
cmd.Parameters.AddWithValue(“Pos”, “P”);
using (DbDataReader rdr =
cmd.ExecuteReader(CommandBehavior.SequentialAccess))
{
while (rdr.Read())
{
MessageBox.Show((string)rdr[“Nombre”]);
}
}
}
}
Listado 8. Ejecución de una consulta parametrizada usando Entity Provider.
La existencia de Entity SQL simplifica en gran medida la especificación
no solo de las típicas vistas estáticas
que acabamos de ver, sino también de
consultas dinámicas (construidas en
tiempo de ejecución) y parametrizadas. El listado 8 muestra un ejemplo
de ejecución de una consulta parametrizada que produce un resultado
similar al de los ejemplos anteriores.
Observe cómo en este caso la sentencia Entity SQL se construye no
sobre el modelo del almacén, sino
sobre el modelo conceptual.
Conclusiones
En este artículo hemos mostrado los
mecanismos que ofrecerá el Marco de
entidades de ADO.NET para acceder a vistas del lado del servidor y
definir vistas del lado del cliente, incidiendo en la importancia conceptual
de éstas últimas. Después del verano
(y ya con la versión definitiva del
Marco de entidades a nuestra disposición), volveremos con más detalles
relacionados con el lenguaje Entity
SQL. ¡Que lo paséis bien!
Bibliografía
[ 1 ] Zorrilla, U. y Hernández, O. “El Marco de trabajo de entidades de ADO.NET 3.5” (partes I a VI), en dotNetManía nº
[ 2 ] Hernández, O. “C# 3.0 y LINQ”, Segunda edición, Krasis Press, mayo de 2008.
[ 3 ] "The ADO.NET Entity Framework Overview", en http://msdn.microsoft.com/en-us/library/aa697427(VS.80).aspx.
[ 4 ] Blog del equipo de ADO.NET Entity Framework, en http://blogs.msdn.com/adonet/.
<<dotNetManía
44-49, enero-junio de 2007.
21
Mario del Valle,
Miguel Katrib,
Iskander Sierra
plataforma.net
Paneles para el mundo
3D
Este artículo debe resultar un útil divertimento para la ya creciente
familia de seguidores de WPF. Aquí se verá cómo programar paneles
tridimensionales para facilitar la distribución de diferentes cuerpos en
un Viewport3D. A la vez, el lector encontrará detalles de algunos elementos de la API 3D de WPF que le deberán resultar de utilidad.
Jerarquía de tipos del mundo
bidimensional
Miguel Katrib es doctor y
profesor jefe de programación del departamento de
Ciencia de la Computación de la Universidad de
La Habana. Miguel es líder
del grupo WEBOO, dedicado a la orientación a
objetos y la programación
en la Web. Es entusiasta de
.NET y redactor de dotNetManía.
Mario del Valle e Iskander
Sierra son instructores de
programación en C# de la
cátedra de Programación e
Ingeniería de Software del
departamento de Ciencia
de la Computación de la
Universidad de La Habana.
Son desarrolladores del
grupo WEBOO dedicados
a la tecnología .NET.
visualmente a través de plantillas definidas a partir de todos los tipos de elementos de WPF. Finalmente, tenemos los paneles (herederos de Panel),
que no tienen expresión visual propia sino que tienen la tarea de distribuir y mantener organizados
a los elementos en el espacio bidimensional.
El familiar espacio bidimensional, tal y como lo
conocemos en WPF, está organizado fundamentalmente a partir de los tipos base que se muestran en la figura 1. Visual es la clase base de todos
los elementos WPF que se pueden visualizar,
mientras que su heredera UIElement es la base de
¿Está el mundo tridimensional igual de
todos los elementos que permiten interacción con
organizado?
el usuario, o sea, que tienen eventos de teclado y
ratón. FrameworkElement, por su parte, es la única
La respuesta es: ¡aún no! Lamentablemente, en el
implementación existente de UIElement y es a su
mundo 3D todavía no se cuenta con tipos equivavez la clase base de casi todos los elementos de
lentes a los de la figura 1. Por ejemplo, al igual que
interacción. Los herederos
de Shape representan a las
figuras geométricas más utilizadas en la ornamentación
de las ventanas y en la definición de las plantillas de
controles. Los decoradores
(herederos de Decorator) se
utilizan con el propósito de
decorar elementos, aprovechando la característica de
los decoradores de poder
contener otro elemento en
su interior. Los controles
(herederos de Control) son
los protagonistas de la inteFigura 1. Síntesis de la jerarquía de los principales elementos 2D de WPF.
racción, que se expresan
<< dnm.plataforma.net
Figura 2. Planos mostrados en forma de stack en Windows Vista.
Situando la clase Panel3D
La figura 3 muestra la jerarquía de clases 3D que se tiene actualmente con
.NET Framework 3.5. El tipo Visual3D
representa a todos los elementos que se
Figura 3. Jerarquía de elementos tridimensionales de WPF.
muestren en un espacio tridimensional
(Viewport3D). ModelVisual3D es un contenedor que permite agrupar elementos de todos los tipos en el espacio 3D.
UIElement3D es la base de todos los elementos tridimensionales que permiten
interacción con el usuario, mientras que
Viewport2DVisual3D permite envolver
un cuerpo 3D con un elemento 2D, de
¿Qué es un Panel?
En [1] y [2] se describen las características que definen a un Panel bidimensional en WPF. Hagamos una abstracción de este concepto para ayudarnos a
determinar cuáles son los problemas
principales que se pretende que un
Panel3D resuelva.
1 En la versión 3.5 de .NET Framework introdujeron un tipo UIElement3D, cuyo parecido con UIElement no es pura coincidencia, y se amenaza
con más para la versión 4.0.
2 En un artículo anterior en dotNetManía [1], describimos un panel circular que nos permitía ubicar los elementos circularmente con perspectiva,
pero se desplegaban de forma plana.
<<dotNetManía
existen figuras geométricas básicas que
forman parte de la jerarquía de Shape, sería
deseable poder contar con algo similar
para las tres dimensiones, es decir, disponer de tipos para esferas, cubos, conos,
planos, etc. Tampoco hay aún alguna
suerte de Control3D (imagine un botón
en forma de esfera), ni tampoco un
Panel3D. La buena noticia es que el entusiasta equipo de desarrollo de MS WPF
ya ha dado señales de sus intenciones de
completar la jerarquía para futuras versiones1. Pero mientras esperamos por ello,
con este artículo queremos presentarle
un acercamiento a lo que podría ser un
Panel3D que permita darle a su aplicación
una visualización tan familiar como la que
ofrece Vista (figura 2)2.
modo que la interacción se haga con el
elemento bidimensional. Como herederos de UIElement3D se tiene a ContainerUIElement3D, que permite agrupar a
varios elementos de tipo UIElement3D, y
a ModelUIElement3D, que facilita la incorporación de las cualidades interactivas
en los elementos primitivos (elementos
de tipo Model3D).
La propuesta y definición de un
Panel3D podría basarse en aquellos
tipos que permiten agregar más de un
elemento en su interior, a saber ContainerUIElement3D y ModelVisual3D. Para
ser consecuentes con el modelo seguido en el mundo 2D (figura 1), el más
indicado sería ContainerUIElement3D;
sin embargo, la mala noticia es que
esta clase por ahora está sellada (sealed en C#), así que, por decantación,
es ModelVisual3D el ganador del sorteo.
23
<< dnm.plataforma.net
Posicionamiento
Imagine que en el mundo bidimensional solo se pudiera contar con un Canvas. En ese caso, ubicar varios controles
uno debajo del otro se traduciría en hacer
y rehacer una secuencia de cálculos
matemáticos basados en el tamaño de
los controles involucrados para lograr el
efecto. Cuando hubiera tenido que repetir un código similar, seguro que intentaría encapsularlo en algún tipo al que
se le podría llamar StackPanel; luego
haría lo mismo para la ubicación o posicionamiento de los controles en forma
de tabla y entonces le llamaría Grid, y así
sucesivamente. Todos estos tipos tienen,
después de todo, un factor común, y es
que se ocupan de determinar en qué
posición deben aparecer los elementos
que ellos contienen. Esta es precisamente
una de las funciones principales de los
paneles en WPF.
Dimensionamiento
Al igual que el posicionamiento, los
paneles también pueden ayudar para
determinar qué tamaño es el más apropiado para cada control según la distribución que el panel esté aplicando.
public abstract class Panel3D : ModelVisual3D, INotifyPropertyChanged {
protected Rect3D bounds;
protected virtual void Move(Visual3D visual, Vector3D offset) {
Transform3D transform = visual.Transform;
if (object.Equals(transform, MatrixTransform3D.Identity))
visual.Transform = new TranslateTransform3D(offset);
else {
Transform3DGroup group = new Transform3DGroup();
group.Children.Add(new TranslateTransform3D(offset));
group.Children.Add(transform);
visual.Transform = group;
}
}
protected override void OnVisualChildrenChanged(DependencyObject visualAdded,
DependencyObject visualRemoved) {
base.OnVisualChildrenChanged(visualAdded, visualRemoved);
Arrange((Visual3D) visualAdded, bounds);
}
protected abstract void Arrange(Visual3D visual, Rect3D rect);
}
Listado 1. Definición de Panel3D.
public partial class StackPanel3D : Panel3D {
protected override void Arrange(Visual3D visual, Rect3D rect) {
int index = this.Children.IndexOf(visual);
if (index >= 0) {
Vector3D offset = new Vector3D(0, 0, index * Gap);
Move(visual, offset);
}
}
public double Gap {
get { return (double)GetValue(GapProperty); }
set { SetValue(GapProperty, value); }
}
public static readonly DependencyProperty GapProperty =
DependencyProperty.Register(“Gap”, typeof(double), typeof(StackPanel3D),
new UIPropertyMetadata(0.1));
Arrange y Measure
<<dotNetManía
La clase Panel que encapsula el sistema de distribución (posicionamiento y
dimensionamiento) de WPF resuelve
ambos problemas a partir del empleo de
los métodos Arrange y Measure que contiene UIElement. Measure se ocupa de indicar cuál es el tamaño recomendable para
un elemento (a partir, claro está, de información que el propio elemento debe ofrecer) y Arrange se ocupa de ubicarlo y redimensionarlo si fuera necesario, tomando
en consideración el tamaño recomendado por Measure. Pero UIElement3D no tiene tales métodos y aparentemente no hay
intenciones de que los tenga (figura 4).
Aunque lamentablemente no se puede contar entonces con tan esenciales
métodos para 3D, se puede utilizar un
24
}
Listado 2. Definición de StackPanel3D.
recurso de WPF que aliviará el problema. Si bien no se podrá redimensionar,
al menos sí se podrán posicionar los elementos de una manera más asequible que
calculando las posiciones a base de pura
matemática.
Transformaciones
tridimensionales
Al igual que en el mundo bidimensional, para 3D existen varios tipos de
transformaciones lineales que se pue-
Figura 4. Recorte tomado de la documentación MSDN de UIElement3D.
den aplicar a todos los elementos de
tipo Visual3D, Model3D y Camera. Todos
estos elementos pueden ser rotados,
escalados y trasladados a través de su
propiedad Transform, a la que se le puede dar como valor cualquier transformación heredera de Transform3D. Las
más relevantes a los efectos del StackPanel3D que queremos desarrollar son
TransformGroup, que permite agrupar
varias transformaciones de modo que
una se aplica sobre los efectos de la
anterior, y TranslateTransform, que permite trasladar en un desplazamiento por
los ejes X, Y y Z. Además, usted puede
utilizar RotateTransform3D y ScaleTransform3D para rotar y escalar objetos tridimensionales.
<< dnm.plataforma.net
Definición de un Panel3D
Definición de un
StackPanel3D
Un StackPanel3D lo definimos como
heredero de Panel3D y redefiniendo el
método Arrange, como se muestra en el
listado 2.
Obsérvese que en el listado 2 se ha
agregado además la definición de la
propiedad de dependencia Gap, que
establece la separación entre un cuerpo y otro en el panel.
Organizando el espacio 3D en
un stack
El listado 3 muestra una definición
XAML de un espacio 3D en el que se
utiliza el StackPanel3D para distribuir
seis planos y una esfera como se muestra en la figura 5. El tipo SphereBuilder
que se usa en este listado el lector lo
puede encontrar en [3], y por economía
de espacio no lo hemos repetido aquí.
El tipo PlaneBuilder, al igual que SphereBuilder, se utiliza para construir una
malla (MeshGeometry3D) que represente
un plano.
Figura 5.Visualización de un StackPanel3D.
<Window x:Class=”Panel3DApp.Window1”
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
xmlns:this=”clr-namespace:Panel3DApp” Title=”Window1”>
<Window.Resources>
<MaterialGroup x:Key=”material1”>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush ImageSource=”Images/No41.png”>
<ImageBrush.RelativeTransform>
<ScaleTransform ScaleY=”-1” CenterY=”0.5”/>
</ImageBrush.RelativeTransform>
</ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
<SpecularMaterial Brush=”ForestGreen” SpecularPower=”85”/>
</MaterialGroup>
<MaterialGroup x:Key=”material2”>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush ImageSource=”Images/No42.png”>
<ImageBrush.RelativeTransform>
<ScaleTransform ScaleY=”-1” CenterY=”0.5”/>
</ImageBrush.RelativeTransform>
</ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
<SpecularMaterial Brush=”RoyalBlue” SpecularPower=”85”/>
</MaterialGroup>
<MaterialGroup x:Key=”material”>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush ImageSource=”Images/vsnet_green1.jpg”>
<ImageBrush.RelativeTransform>
<ScaleTransform ScaleY=”-1” ScaleX=”-1” CenterX=”0.5” CenterY=”0.5”/>
</ImageBrush.RelativeTransform>
</ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
<SpecularMaterial Brush=”RoyalBlue” SpecularPower=”85”/>
</MaterialGroup>
<MaterialGroup x:Key=”material3”>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush ImageSource=”Images/No43.png”>
<<dotNetManía
El listado 1 muestra una variante de lo
que sería la clase base Panel3D.
El valor por defecto de la propiedad
Transform de un Visual3D es MatrixTransform3D.Identity, por ello en el
método Move del listado 1 se pregunta si
la propiedad Transform del Visual3D que
recibe como parámetro es igual a este
valor por defecto para determinar si aún
no se la ha asignado ningún valor.
Por lo demás, la definición de
Panel3D sólo se ocupa de invocar al
método Arrange en los herederos cuando se agregue un nuevo elemento al
panel (código de OnVisualChildrenChanged), y de definir un método Move que
puede resultar muy útil en la mayoría
de los controles; su función es precisamente desplazar (mover) el cuerpo
según el desplazamiento indicado por
el parámetro offset.
25
<< dnm.plataforma.net
<ImageBrush.RelativeTransform>
<ScaleTransform ScaleY=”-1” CenterY=”0.5”/>
</ImageBrush.RelativeTransform>
</ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
<SpecularMaterial Brush=”#bFA8” SpecularPower=”85”/>
</MaterialGroup>
<MaterialGroup x:Key=”material4”>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush ImageSource=”Images/No44.png”>
<ImageBrush.RelativeTransform>
<ScaleTransform ScaleY=”-1” CenterY=”0.5”/>
</ImageBrush.RelativeTransform>
</ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
<SpecularMaterial Brush=”#bFA8” SpecularPower=”85”/>
</MaterialGroup>
<MaterialGroup x:Key=”material5”>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush ImageSource=”Images/No45.png”>
<ImageBrush.RelativeTransform>
<ScaleTransform ScaleY=”-1” CenterY=”0.5”/>
</ImageBrush.RelativeTransform>
</ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
<SpecularMaterial Brush=”#bFA8” SpecularPower=”85”/>
</MaterialGroup>
<MaterialGroup x:Key=”material6”>
<DiffuseMaterial>
<DiffuseMaterial.Brush>
<ImageBrush ImageSource=”Images/No46.png”>
<ImageBrush.RelativeTransform>
<ScaleTransform ScaleY=”-1” CenterY=”0.5”/>
</ImageBrush.RelativeTransform>
</ImageBrush>
</DiffuseMaterial.Brush>
</DiffuseMaterial>
<SpecularMaterial Brush=”#bFA8” SpecularPower=”85”/>
</MaterialGroup>
<this:SphereBuilder x:Key=”sphere1” Radius=”0.6” WidthSegments=”40” HeightSegments=”40”/>
<this:PlaneBuilder x:Key=”plane” WidthSegments=”20” HeightSegments=”20” Width=”3” Height=”3”/>
</Window.Resources>
<<dotNetManía
<Grid>
<Viewport3D>
<Viewport3D.Camera>
<PerspectiveCamera Position=”-8,4,8” LookDirection=”8,-4,-8”
UpDirection=”0,1,0” FieldOfView=”45”/>
</Viewport3D.Camera>
<ModelVisual3D>
<ModelVisual3D.Content>
<Model3DGroup>
<AmbientLight Color=”#8FFF”/>
<DirectionalLight Color=”White” Direction=”1,0,-1”/>
</Model3DGroup>
</ModelVisual3D.Content>
<ModelVisual3D.Children>
<this:StackPanel3D
<this:StackPanel3D x:Name=”cube”
x:Name=”cube” Gap=”1.5”>
Gap=”1.5”>
<this:Panel3D.Children>
26
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D Geometry=”{Binding Geometry, Source={StaticResource plane}}”
Material=”{StaticResource material1}”/>
</ModelVisual3D.Content>
</ModelVisual3D>
<< dnm.plataforma.net
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D Geometry=”{Binding Geometry, Source={StaticResource plane}}”
Material=”{StaticResource material2}”/>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D Geometry=”{Binding Geometry, Source={StaticResource plane}}”
Material=”{StaticResource material3}”/>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D Geometry=”{Binding Geometry, Source={StaticResource plane}}”
Material=”{StaticResource material4}”/>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D Geometry=”{Binding Geometry, Source={StaticResource plane}}”
Material=”{StaticResource material5}”/>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D Geometry=”{Binding Geometry, Source={StaticResource plane}}”
Material=”{StaticResource material6}”/>
</ModelVisual3D.Content>
</ModelVisual3D>
<ModelVisual3D>
<ModelVisual3D.Content>
<GeometryModel3D Geometry=”{Binding Geometry, Source={StaticResource sphere1}}”
Material=”{StaticResource material}”/>
</ModelVisual3D.Content>
</ModelVisual3D>
</this:Panel3D.Children>
</this:StackPanel3D>
</this:StackPanel3D>
</ModelVisual3D.Children>
</ModelVisual3D>
</Viewport3D>
</Grid>
</Window>
Listado 3. Empleo de un StackPanel3D en un espacio tridimensional.
Conclusiones
El equipo de WPF trabaja para ampliar las capacidades y primitivas 3D a incluir en WPF. Mientras
tanto, los lectores pueden descargar este código e
ir dando sus primeros pasos en este terreno, apoyándose en el concepto de panel que hemos desarrollado aquí.
Un tema interesante a desarrollar en el futuro es
cómo sería un Grid3D.
Bibliografía
[ 2]
[ 3]
pios paneles personalizados en WPF”, dotNetManía No. 35, marzo de 2007.
Katrib Miguel, Del Valle Mario, Sierra Iskander, Hernández Yamil, Windows Presentation Foundation. dotNetManía Cuaderno Técnico No. 7, Netalia 2007.
Del Valle Mario, Sierra Iskander, Hernández Yamil, Katrib Miguel. “Entrando en la tercera dimensión”, dotNetManía No. 37, mayo de 2007.
<<dotNetManía
[ 1 ] Del Valle Mario, Sierra Iskander, Hernández Yamil, Katrib Miguel. “Cómo definir nuestros pro-
27
metodologías
Luis Fraile
Integrándonos
continuamente
Una de las prácticas más recomendadas a la hora de realizar proyectos y
empezar a asegurar la calidad pronto es la de integrar todo el código lo más
frecuentemente posible. Esto es especialmente importante a la hora del desarrollo iterativo, mediante el cual gestionaremos los cambios de requisitos de
una forma más efectiva, entregando nuevas funcionalidades del software en
cortos periodos de tiempo.
Luis Fraile
es MVP de Team System y colabora activamente en MAD.NUG
(grupo de usuarios de
.NET de Madrid).
Actualmente es director técnico en Multidomo Networks,
donde desarrollan un
producto de software
para la gestión de dispositivos domóticos y
cámaras de vigilancia
a través de Internet
mediante interfaces
web, teléfonos móviles, Media Center, etc.
Puede consultar su
blog en
http://ww.lfraile.net.
Seguro que muchos de nosotros nos hemos
encontrado con la siguiente situación: el equipo de desarrollo lleva generando código un
tiempo “razonable”; en el mejor de los casos, si
estamos siendo “iterativos”, puede que ese tiempo no exceda de las 2 ó 3 semanas. Tenemos
nuestro repositorio de código fuente, donde los
desarrolladores actualizan y obtienen la última
versión, y en el entorno ideal, los desarrolladores obtienen con frecuencia la última versión,
antes de subir código, para compilarla y probarla. ¡Incluso tenemos pruebas unitarias que
prueban el código! Sin embargo, el final de la
iteración (o proyecto) se acerca, los tiempos se
aprietan por otras muchas razones, el entorno
ideal empieza a desmontarse, y los desarrolladores, más concentrados en sacar la funcionalidad, olvidan buenas prácticas como obtener la
última versión para comprobar al menos que
compila.
¿Qué ocurre entonces? Que puede llegar, y
casi seguro llegará, un momento en el que alguien
obtendrá la compilación (cuanto más cerca del día
de entrega más posibilidades hay, según el algoritmo del Dr. Stono Compilani Decoña), y se
comprobará que todo el conjunto de código ni
siquiera compila: han cambiado interfaces, se ha
cambiado código al que se hacen referencias, y
como resultado el código de nuestro repositorio
no compila.
¿Qué hemos hecho mal en este caso? Simplemente, olvidarnos de una de las buenas prácticas de los equipos de desarrollo: la integración continua.
Pero, ¿qué es la integración? La integración
del código consta en varios pasos:
1. Obtener la última versión de todo el código.
2. Compilar esta última versión.
3. Ejecutar el set de pruebas unitarias junto
con la cobertura de código.
4. Dejar al alcance de todo el equipo esta última versión integrada de los ejecutables, así
como los informes de la compilación y ejecución de las pruebas.
Siguiendo esta práctica, podremos comprobar
en cualquier momento el estado de nuestro código. No hay que olvidar que uno de los principales problemas que se nos presenta en los últimos
momentos de la entrega es precisamente esta integración, cuando surgen problemas de código que
puesto todo junto no compila correctamente por
cambios en interfaces durante el desarrollo, o fallos
en la ejecución del software.
Esta integración nos va a dar el “latido” del
proceso, ya que se convertirá el indicador del progreso de nuestro equipo, permitiéndonos comprobar, durante la iteración, que las tareas se están
realizando, promocionando entre el equipo de
<< dnm.plataforma.net
Repositorio de código fuente
Lo primero que necesitamos es un repositorio de código fuente, donde los desarrolladores ponen en común
su código, exponiéndolo al resto del equipo, y de donde pueden obtener asimismo el código fuente creado
por los otros desarrolladores del equipo.
En los entornos Microsoft, tradicionalmente se ha
venido usando Visual Source Safe como repositorio.
Sin embargo, desde la aparición de Visual Studio Team
System, cada vez más equipos usan la nueva herramienta
de control de código fuente integrada en Team Foundation Server y que se llama Team Foundation Server Version Control; ésta es en la que nos vamos a
basar para este artículo, sin entrar no obstante a explicar en detalle todas sus funcionalidades y herramientas, ya que ello no es objeto de este artículo.
Herramienta de compilación
automatizada
La siguiente herramienta que necesitaremos es la
encargada de realizar la integración. Esta herramienta será la encargada, de modo automático, de obtener
la última versión de las soluciones de Visual Studio
que deseemos compilar, realizar la compilación de las
soluciones, ejecutar el conjunto de pruebas unitarias
que configuremos, y dejar los resultados del proceso
en un recurso compartido accesible por el equipo de
desarrollo. La herramienta que vamos a utilizar para
esta tarea es la que viene integrada en Team System,
y que se integra con Team Foundation Server; esta
herramienta es Team Build.
Tipos de integración
También hay que definir los dos tipos de integración
que podremos llevar a cabo, y entre los que, en función de nuestra experiencia, equipo y tipo de proyecto, podemos escoger una para nuestro proyecto.
Desde la aparición de Visual Studio
Team System, cada vez más equipos
usan la nueva herramienta de
control de código fuente integrada
en Team Foundation Server y que
se llama Team Foundation
Server Version Control
Integración continua
La integración continua es el tipo de integración
que se ejecuta automáticamente cada vez que un desarrollador actualiza el código fuente de nuestro repositorio (lo que solemos llamar “hacer un check-in”).
En este tipo de integración, cada vez que un desarrollador realiza esta acción, se lanza la integración
configurada en la herramienta, con todos sus pasos.
Como se puede ver, este tipo de integración es la
ideal, ya que en todo momento y en cada actualización de código, que se tiene que realizar con bastante frecuencia (varias veces al día) obtenemos una versión de los ejecutables completamente integrada. Aún
siendo la ideal, tenemos que tener cuidado a la hora
de ponerla en práctica, ya que en soluciones muy grandes puede generarnos bastante sobrecarga en la máquina dedicada a la ejecución del proceso. Por esta razón,
la solución o soluciones para esta integración, así como
la ejecución de pruebas, deben ser configuradas cuidadosamente para que no se produzcan problemas a
la hora de la integración.
Integración frecuente
Por otra parte, la integración frecuente, a diferencia de la integración continua, solo se ejecuta con
una frecuencia determinada por nosotros en la herramienta; por ejemplo, a mitad del día y al final de la tarde. Este tipo de integración nos proporciona menos
resultados y menos “latidos”, pero también nos genera menos sobrecarga en las máquinas de compilación.
Por tanto, la integración frecuente es ideal cuando
<<dotNetManía
desarrollo la cultura de subir código fuente que compile y funcione con frecuencia, favoreciendo así el
desarrollo iterativo.
Sin embargo, para que esta práctica sea realmente efectiva y no genere una sobrecarga de trabajo,
necesitamos herramientas que nos ayuden a la hora
de integrar.
29
<<dotNetManía
<< dnm.metodologías
30
tenemos procesos muy pesados de compilación o pruebas unitarias; pero, aún
así, deberemos procurar compilar un
mínimo de una o dos veces diarias, para
que podamos tener la certeza de que el
código se está integrando correctamente
y no tener problemas a la hora de finalizar la iteración; normalmente, deberemos fijar una periodicidad de, al
menos, una compilación diaria.
El decidir qué tipo de integración
queremos usar también nos hace pensar en algo que es muy importante a
la hora de trabajar con Visual Studio,
que es el decidir cuántas soluciones
vamos a utilizar en nuestro proyecto
y qué proyectos de Visual Studio queremos tener en cada una de esas soluciones. Pudiendo disponer así de una
única solución, si nuestro proyecto no
es muy grande, o varias soluciones con
un conjunto pequeño de proyectos
cada una, que nos ayuden a manejar
de un modo más cómodo nuestro
código.
Es muy importante recordar además
que siempre debemos llevar a cabo una
planificación de entregas iterativa, ya
que junto con la integración continua,
éste es un modo de generar nueva funcionalidad que minimiza la posibilidad
de fallos a la hora de entregar funcionalidad a nuestros clientes.
Estas compilaciones se tienen que
convertir en el “latido” del corazón de
nuestro proyecto; algo que nos indica
que diariamente estamos incrementando nuestras funcionalidades, con la adición de nuevo código con sus pruebas
unitarias funcionando correctamente,
y ayudándonos a detectar los fallos tan
pronto como éstos surjan.
Por último, hay que recordar también que cuanto más complicado sea el
proyecto, más complicada y costosa
será su compilación y ejecución de
pruebas, de modo que la automatización de la integración nos salvará de
muchos disgustos y nos ahorrará
mucho tiempo, especialmente en las
fases más delicadas del proyecto: el final
de las iteraciones.
Configuración de la
integración en TFS
Ahora que ya conocemos los beneficios
que nos ofrece la integración y los dos
tipos de integración posibles, aprenderemos a configurar la herramienta para
la realización de compilaciones automatizadas. Solo recordar que, para
hacer esto, necesitaremos un Team
Foundation Server (TFS) como repositorio de código fuente para la realización de compilaciones con Team
Build. TFS es la herramienta de servidor que pertenece al conjunto de herramientas para la gestión del ciclo de vida
del software de Microsoft, y que nos
sirve como repositorio de código fuente, tareas (Work Items), documentación
(en SharePoint) y que nos proporciona
métricas a nivel de proyectos.
Instalación del agente
de compilaciones
Para la realización de compilaciones
con TFS, lo primero que necesitamos es
un agente de compilaciones. Este agente lo podemos instalar en cualquier ordenador con conexión a nuestro TFS. Puede ser incluso un ordenador del equipo
de desarrollo, si bien se recomienda que
se instale en un ordenador independiente,
en equipos pequeños,
con pocos proyectos y
por tanto pocas compilaciones automatizadas. Podemos instalar
el agente en el mismo
servidor TFS, pero
para equipos grandes o
con muchos proyectos
se recomienda un servidor aparte para las
compilaciones.
En ese equipo,
necesitaremos además
una versión de Visual
Studio Team System
para la compilación y
ejecución de las prue-
bas. Ésta debería ser al menos Visual Studio Team System Tester Edition, para
permitirnos ejecutar pruebas de todo
tipo, o mejor aún Visual Studio Team
System Team Suite. Hay que señalar que
para el agente de compilaciones no necesitamos una licencia extra de Visual Studio, sino que podemos utilizar una misma licencia ya usada en otra de nuestras
máquinas de desarrollo.
La instalación del agente de compilaciones se realiza desde el CD/DVD
de TFS. Una vez insertemos el
CD/DVD de TFS en la máquina elegida, se nos mostrará la pantalla inicial
de instalación, y deberemos seleccionar
la opción “Team Foundation Build”.
Aquí se nos mostrará un asistente
(wizard) de instalación, en el que podremos seleccionar el directorio de instalación, y lo más importante, la cuenta
de ejecución del servicio del agente, que
será la cuenta con la que éste se conectará al TFS. Esta cuenta deberá tener
permisos de acceso al TFS, y en caso
de existir un dominio, debe pertenecer
a éste. Puede ser la misma cuenta que
utilizamos para la instalación de los servicios de TFS: TFSService (asegurar
antes que habéis configurado esta cuenta en la instalación), si bien esta cuenta hay que tenerla controlada, ya que
Figura 1
<< dnm.metodologías
tiene acceso completo a TFS, algo que puede no ser
adecuado en muchos entornos (ver figura 1).
Con esto, ya podemos completar la instalación
de nuestro agente de compilaciones. Tampoco
vamos a detenernos más en este punto; si necesitáis
más información al respecto, en el CD/DVD de
TFS disponéis de las guías completas de administración y configuración, accesibles desde la primera pantalla.
Configuración de la compilación
Una vez tengamos el agente instalado, ya estamos listos para configurar nuestra primera compilación automatizada. El primer paso es abrir nuestro
Visual Studio 2008 y, en la ventana de Team Explorer, seleccionar la rama “Builds” y obtener su menú
contextual.
Configurando el agente
El primer paso es agregar el agente de compilaciones que hemos instalado anteriormente, para esto, seleccionamos la opción “Manage Build Agents” del menú.
En la ventana de gestión “Build Agents” tenemos el listado de agentes configurados; para añadir uno nuevo,
pulsamos “New…”. Simplemente le pondremos un
nombre significativo para referirnos a él, una descripción (opcional), e indicaremos el nombre de máquina
dónde hemos instalado previamente el componente de
Team Foundation Build y el puerto (dejaremos el por
defecto). Adicionalmente, podemos seleccionar si queremos que se use comunicación HTTPS, y el estado
inicial (dejaremos el por defecto también).
Un punto importante a configurar en esta pantalla es el directorio de trabajo: “Working directory”.
Por defecto, .NET no admite rutas de disco mayores de 256 caracteres, por lo que os recomiendo crear un directorio con nombre corto, por ejemplo C:\B,
y utilizar ese directorio en sustitución del directorio temporal, quedando así: C:\B\$(BuildDefinitionPath).
<<dotNetManía
Configuración de la definición de la compilación
32
El siguiente paso es crear la definición de nuestra
compilación automatizada. Para ello, seleccionamos la
opción “New Build Definition” en el menú contextual
de “Builds”. En la pantalla inicial de configuración de
una compilación, debemos prestar especial atención a
los puntos marcados con el icono de advertencia, que
son los principales. Vamos a ver punto por punto.
• General
En este punto debemos de introducir el nombre
con el que vamos a referirnos a esta compilación, así
como, opcionalmente, una descripción. También disponemos de una casilla para desactivar la definición
de esta compilación.
• Workspace
El workspace es el espacio de trabajo que usará el
agente de compilaciones. Aquí podemos definir qué
ramas del repositorio de control de fuentes obtendrá
el agente a la hora de realizar una compilación; al ser
éste un ejemplo básico, lo dejaremos con los valores
por defecto.
Esto se usaría también para separar en distintas
compilaciones de integración continua las distintas
partes de nuestro proyecto, para evitar así compilar
siempre todo al completo en caso de proyectos muy
grandes. Para ello, definiríamos aquí qué espacios de
trabajo queremos que lancen la compilación a la hora
de un check-in.
• Project File
En este punto vamos a generar el fichero que se
encargará de definir qué soluciones y qué pruebas
queremos ejecutar. Lo primero es indicar en qué rama
del repositorio de fuentes vamos a crear el fichero de
definición. Este fichero se llamará TFSBuild.proj, y es
un documento XML que podremos editar para personalizar los pasos de nuestras compilaciones. Para
crear uno nuevo, pulsamos el botón “Create…”.
El primer paso es seleccionar qué solución o soluciones vamos a compilar. Por defecto, no podemos
seleccionar proyectos por separado; solo lo podemos
hacer a nivel de solución. Una vez seleccionadas las
soluciones que queremos compilar, podremos pasar
al siguiente paso (figura 2), en el que deberemos especificar qué configuraciones queremos compilar.
Una misma solución la podemos compilar con
diferentes configuraciones (Debug, Release o configuraciones personalizadas), así como para diferentes
plataformas (x86, x64, Any CPU). Pero estas configuraciones tienen que ser configuraciones existentes
en nuestras soluciones, ya que si no la compilación
no funcionará correctamente; estas configuraciones
las podemos comprobar abriendo la solución en Visual
Studio y comprobando el gestor de configuraciones
(Configuration Manager).
El siguiente paso es decidir qué pruebas vamos a
ejecutar, lo que se hace en la ventana de opciones.
Para decidir qué pruebas ejecutar, seleccionaremos la
opción “Run tests”, y del listado de ficheros de listas
<< dnm.metodologías
recomiendo hacerlo antes, fuera de las
compilaciones automatizadas.
Una vez que pulsemos “Finish”,
se nos creará el fichero TFSBuild.proj
con las configuraciones elegidas, volviendo a la pantalla de configuración
de la compilación. De estas configuraciones no disponemos a posteriori
de un editor gráfico integrado, aunque sí existen herramientas de terceros, como los sidekicks de Attrice
(http://www.attrice.info).
de pruebas unitarias (ficheros con
extensión .vsmdi) seleccionamos el que
tiene la lista de pruebas que queremos
ejecutar; entonces en la ventana de listas de pruebas podremos seleccionar las
que queremos ejecutar.
Si no estamos trabajando con listas
de pruebas, podemos seleccionar ejecutar automáticamente todas las pruebas que estén en una DLL que cumpla
con un determinado patrón; ésta es la
segunda opción que se nos muestra, y
como patrón se nos propone que la
DLL de pruebas comience con la palabra “Test”. Es importante tener en
cuenta que el sistema detectará y ejecutará todas las pruebas de esa DLL;
por tanto, es importante mantener aisladas en ella únicamente las pruebas que
deseemos ejecutar, siendo preferible
que sean únicamente pruebas unitarias
o de ejecución muy simple.
En esta última pantalla también
podemos especificar si queremos que
se ejecute el analizador de código estático para cada proyecto según la configuración detallada en cada proyecto.
Este analizador comprobará que nues-
tro código cumple los conjuntos de
reglas de diseño y codificación, como
si de un revisor de código se tratase. Si
no estáis familiarizados con esto, os
Figura 3
<<dotNetManía
Figura 2
• Políticas de retención
Volviendo a la pantalla principal de
configuración de la compilación, tenemos el apartado de políticas de retención.
Como podemos suponer, el compilar diariamente una o varias veces y
dejar compartidos los binarios, resultados de pruebas unitarias, etc. genera
muchos ficheros que pueden llegar a
crear un problema de almacenamiento en la máquina de compilación. Para
evitar que esto suceda, podemos especificar qué resultados queremos guardar mediante políticas de retención
(figura 3).
33
<< dnm.metodologías
Los posibles resultados de la compilación son:
• Failed: la solución no se ha compilado correctamente.
• Stopped: hemos detenido la compilación durante su ejecución.
• Partially succeeded: cuando una
compilación da este resultado,
alguna o todas las pruebas unitarias han fallado.
• Succeeded: la compilación y sus
pruebas unitarias han funcionado correctamente.
Y lo que podemos especificar en la
figura 3 es qué número de resultados de
compilación queremos conservar. Es recomendable especificar cuántas queremos
guardar para cada uno de los resultados,
ya que si bien podremos eliminar los resultados manualmente, lo más cómodo es
que TFS lo haga por nosotros.
<<dotNetManía
• Build defaults
En la figura 4 podemos ver la pantalla de selección del agente de compilación, y el recurso compartido (en este
caso \\tfsdemos\deploy) en el que se
almacenarán los resultados de esta
generación. En el listado de agentes de
compilación, seleccionaremos el que
hemos configurado previamente.
Es importante tener en cuenta que
en ese recurso compartido de red, el
usuario que configuramos durante la
instalación del agente de compilación
(TFSService) debe tener acceso de lectura y escritura para dejar los binarios.
En ese recurso compartido se creará
una carpeta con el nombre de la compilación y un número de identificación
(basado en la fecha y orden de compilación) que contendrá los resultados de
cada compilación, manteniendo así un
histórico de compilaciones. Estos directorios se borrarán dependiendo de las
políticas de retención.
34
• Triggers (disparadores)
Por último, podemos configurar la
periodicidad de nuestra compilación.
Figura 4
Las opciones que tenemos a nuestra disposición son:
• Check-ins do not trigger a new
build: si seleccionamos esta configuración, la compilación solo se
lanzará cuando la lancemos
manualmente desde el menú
“Builds” de Team Explorer, seleccionando la compilación y en su
menú contextual, la opción
“Queue Build”.
• Build each check-in: con esta
opción, cada vez que un usuario
haga check-in de código fuente
se lanzará esta compilación. Ésta
es la opción que nos proporciona una compilación de integración continua.
• Accumulate check-ins…: con
esta opción, también tendremos
un entorno parecido al de integración continua, pero para evitar un número excesivo de compilaciones, especialmente en
equipos grandes, podemos acumular check-ins, hasta que la
compilación previa acabe, y
además especificar que no se lance la compilación más de una vez
cada X minutos, para, por ejemplo, no compilar más de una vez
cada hora.
• Build every week on…: con esta
configuración, especificaremos
una periodicidad para nuestras
compilaciones. Podemos especificar qué días de la semana queremos compilar, a qué hora, y
especificar si queremos compilar
independientemente de que haya
cambios o no en el repositorio de
código. Esto nos daría un entorno de integración frecuente.
Una vez configuradas todas las
opciones, pulsando el botón “Ok”, se
creará nuestro nuevo tipo de compilación, que aparecerá en el listado
“Builds” de nuestro Team Explorer.
Con esto ya tendremos nuestro entorno listo, y la compilación se ejecutará
según la periodicidad elegida, o bien
manualmente mediante la opción
“Queue build” del menú contextual de
cada tipo de compilación.
<< dnm.metodologías
Para comprobar los resultados de una compilación, haremos doble clic en el listado de tipos de compilación de Team Explorer. La ventana que obtendremos nos mostrará si hay alguna compilación realizándose actualmente, en cuyo caso podremos hacer
doble clic en ella y ver los pasos que se están ejecutando, como obtención de fuentes, compilación, ejecución de pruebas…
En esta ventana también podemos seleccionar,
mediante los filtros de la parte superior, otros agentes de compilación u otros tipos de compilación para
ver el estado de compilaciones encoladas.
Para ver los resultados de compilaciones pasadas, en
esta misma pantalla, en la parte inferior, tenemos la pestaña “Completed”, que nos mostrará la ventana de la
figura 5, en la que vemos el listado de las compilaciones
finalizadas. Por defecto, los filtros muestran solo las compilaciones finalizadas en un día; si queremos ver más
resultados, podemos cambiar el filtro de fecha. En ese
listado se nos muestra además el resultado de cada compilación mediante el icono de la izquierda, siendo éste
Crear un entorno base de
integración continua con Visual
Studio 2008 Team System no es
nada complicado
1
Nota del Editor: ¡Por supuesto que sí!
Conclusiones
Como veis, crear un entorno base de integración continua con Visual Studio 2008 Team System no es nada
complicado, y lo podemos lograr mediante unos sencillos pasos basados en asistentes. Si bien hemos mostrado solo los fundamentos, y dependiendo de nuestro proyecto, podemos extenderlo para conseguir una
mayor funcionalidad de nuestro entorno de integración continua.
Muchas de estas funcionalidades se comentan en
diferentes blogs, e iré presentando algunas de ellas en
el futuro, tanto a través de artículos en esta revista (si
me dejan ☺ ) 1 como a través de mi blog en
http://www.lfraile.net. El lector también puede
encontrar más información en otros blogs (en inglés)
relacionados con Team System: http://msdn2.microsoft.com/en-us/teamsystem/aa718761.aspx.
Y esto es todo por ahora. Os animo a todos los
que ya estéis usando TFS a sacar un poco más de partido a vuestro servidor, utilizando los entornos de integración continua/integración frecuente.
<<dotNetManía
Resultados de compilación
una “pelotita” verde para
indicar que todo ha sido
correcto, o roja, para indicar que no se ha podido realizar la compilación. Si
tenemos una pelotita roja y
una verde, es que el resultado ha sido parcialmente
Figura 5
correcto (fallo en pruebas unitarias).
También podremos ver
los detalles de cualquier compilación haciendo doble clic sobre ella. En la ventana
emergente obtendremos los resultados detallados de
esa compilación, así como un enlace directo al recurso compartido donde están los binarios resultantes y
los logs de la compilación, para que todo el equipo del
proyecto tenga disponible la última versión.
Otra de las funcionalidades que obtenemos directamente de Team System es la alerta de compilaciones completadas, que nos permite disponer de datos
al momento de la finalización de la compilación. Para
ello, en el menú contextual del proyecto en el Team
Explorer debemos seleccionar la opción “Project
Alerts”. Podemos configurar distintos tipos de alertas, introduciendo nuestra dirección de correo electrónico; la que nos interesa para estar al tanto de las compilaciones es la opción “A build completes”.
35
plataforma.net
Juan Carlos Viñas
Frameworks para Javascript
Las técnicas de desarrollo en Javascript, así como los usos que se le dan
a esta tecnología, han cambiado notablemente en los últimos años. Hoy
en día, nos es difícil pensar que una aplicación Web moderna pueda prescindir de esta tecnología para finalidades tan diversas como la comunicación asíncrona entre cliente y servidor, la aplicación de efectos visuales a nuestro código HTML o mantener nuestro propio sistema de caché
en el lado del cliente.
Cuando implementamos una aplicación Web, a
menudo necesitamos escribir partes de código que
han de ser ejecutadas en el navegador. Este código debe ser escrito en Javascript, y es aquí cuando nos puede ser útil la ayuda de una librería de
código en este lenguaje. Es por esto que en los
últimos años han comenzado a aparecer librerías
que han cambiado la forma de incorporar código
Javascript a una aplicación Web: ya no necesitamos escribir todo el código nosotros mismos, porque existen multitud de recursos que nos ayudarán
a programar más rápida y fácilmente de lo que
nunca antes había sido posible. Estas librerías son
recursos que podremos conectar a nuestros frameworks de desarrollo y que ofrecen soluciones a
los problemas más comunes con los que se encuentra un programador Web a la hora de escribir código Javascript.
Eligiendo la librería que más nos conviene
Juan Carlos Viñas
Custom Development
Manager de Raona. Juan
Carlos es MCSD .NET
La mayoría de las librerías Javascript más populares comparten muchas de sus funcionalidades. Es
por eso que elegir qué librería es la que mejor se
adapta a nuestras necesidades no siempre es fácil.
Estos son algunos de los criterios que no podemos dejar de tener en cuenta a la hora de elegir
nuestra librería:
• ¿Dispone de todas las funcionalidades que necesitamos?
Es importante que la librería que elijamos tenga todas las funcionalidades que queramos utilizar. Muchas de las librerías de Javascript comparten el mismo espacio de nombres (como $()),
lo que hace que no sea fácil combinar sus funcionalidades. Además, al combinar librerías nos
arriesgamos a tener código redundante en nuestra aplicación.
• ¿Dispone de más funcionalidades de las que realmente necesitamos?
Cuantas más funcionalidades tenga una librería,
es de esperar que ésta contenga más líneas de
código y por tanto sea más costosa de descargar. Muchas de las librerías actuales disponen
de versiones “ligeras” e incluso algunas están
divididas en módulos, lo que nos permite incluir
en nuestra aplicación solo aquellas funcionalidades en las que estemos realmente interesados.
• ¿Es compatible con todos los navegadores?
Las librerías Javascript suelen funcionar independientemente del navegador en que se ejecuten. Esto es una gran ventaja, ya que permi-
<< dnm.plataforma.net
• ¿Dispone de un buen soporte por parte de la comunidad?
Una comunidad de usuarios y desarrolladores activa se traduce en
menos errores y un código de mayor
calidad, además de constituir un
valioso recurso al que recurrir en
caso de necesitar ayuda.
• ¿Dispone de documentación?
Una buena documentación y un
lugar donde encontrar ejemplos de
uso resulta fundamental para entender, y por tanto utilizar correctamente, cualquier librería.
Selección de elementos de una
página
Una de las características más útiles de las librerías multipropósito son
los selectores de elementos. Estos selectores son una potente herramienta que
nos permite seleccionar elementos de
una página de forma simple y eficiente. Realizar selecciones complejas utilizando las funciones nativas que Javascript nos ofrece puede ser una tarea
realmente tediosa. Por ejemplo, si quisiéramos obtener todos los controles
RadioButton seleccionados en una página, primero tendríamos que obtener
todos los elementos de tipo <input> utilizando la función getElementByTagName,
y luego iterar sobre estos elementos,
seleccionando los que sean de tipo radio
y que tengan el atributo checked.
En cambio, utilizando una librería
como jQuery (http://www.jquery.com), el
código necesario se simplificaría a:
• ¿Tiene la licencia que nos interesa?
$(“input[@type=radio][@checked]”)
Que dispongamos del código fuente de una librería no significa que
podamos hacer con él lo que nosotros queramos. La mayoría de estas
librerías son libres, pero otras exigen que cualquier código que se
escriba usando alguna de sus funcionalidades se libere bajo la misma
licencia que ellas.
¿Qué tipo de funcionalidades
me pueden aportar?
Existen muchas librerías, y cada una de
ellas tiene sus propias particularidades.
Existen librerías con funcionalidades
muy específicas, y otras que pretenden
proporcionar herramientas para facilitar las tareas más cotidianas de un programador Web. A las librerías de este
último tipo se les llama librerías multipropósito, y en el siguiente apartado
vamos a ver cuáles son las funcionalidades más comunes que éstas suelen
incorporar.
Otro ejemplo: supongamos que
queremos seleccionar todos elementos
de tipo párrafo que tengan en su atributo class el valor class1 y que además
contengan un elemento de tipo hiperenlace. En Javascript tradicional,
tendríamos que seleccionar todos los
elementos de tipo <p> mediante la función getElementByTagName, iterar sobre
éstos para seleccionar los que tengan el
valor class1 en su atributo class, y por
último recorrer todos los nodos hijos
de estos últimos para comprobar que
tengan un hijo de tipo <a>. Utilizando
jQuery, nos bastaría con escribir la
siguiente línea:
La combinación del lenguaje XPath y
los selectores CSS nos permiten realizar selecciones mediante prácticamente cualquier criterio que se nos ocurra,
y esto es algo que también nos ofrecen
librerías como jQuery.
Comunicación asíncrona
entre cliente-servidor (AJAX)
Resulta casi imposible hablar sobre
tecnología Web sin hablar sobre AJAX.
AJAX es una combinación de tecnologías ya existentes, y éstas, como no,
tienen los mismos problemas de incompatibilidad entre navegadores que siempre tuvieron. Las librerías Javascript nos
ayudan a solucionar estos problemas,
además de simplificar nuestras llamadas desde cliente hacia el servidor.
Probablemente el objeto AJAX más
popular de entre todas las librerías
Javascript es el que nos ofrece Prototype (http://www.prototypejs.org). Prototype.Ajax funciona en todos los navegadores modernos y nos ofrece métodos que nos permiten trabajar con
AJAX de una forma simple:
• El método Ajax.Request realiza una
petición simple al servidor:
new Ajax.Request(‘/some_url’,
{ method:’get’,
onSuccess: function(){
alert(“It works!”);
}
});
• Con el método Ajax.Updater podemos
incrustar directamente el contenido
devuelto por la petición AJAX a un
nodo de nuestra página. La siguiente llamada insertaría el resultado de
la petición en el nodo “node”:
$(“p.class1[a]”);
Estos dos ejemplos son una pequeña
muestra de lo que los selectores de las
librerías multipropósito pueden simplificar nuestro código, pero éstas
todavía pueden ofrecernos mucho más.
new Ajax.Updater(‘node’, ‘/some_url’,
{
method: ‘get’,
insertion: Insertion.Top
});
<<dotNetManía
te al programador olvidarse de las
diferencias entre navegadores y
escribir el código con la certeza de
que funcionará correctamente en
todos los navegadores que la librería
soporte.
37
<< dnm.plataforma.net
• Ajax.PeriodicalUpdater realiza peticiones periódicas al servidor, e inserta el contenido que obtiene
en el nodo de nuestra página que indiquemos:
new Ajax.PeriodicalUpdater(node, ‘/some_url’,
{
method: ‘get’,
insertion: Insertion.Top,
frequency: 1,
decay: 2
});
El parámetro frecuency hace referencia al intervalo de tiempo entre llamadas, mientras que el parámetro decay es el factor por el cual se multiplica la
frecuencia cada vez que el contenido de la respuesta sea el mismo. De esta forma evitamos sobrecargar al servidor con un número excesivo de peticiones.
Gestión de eventos
<<dotNetManía
La gestión de eventos es otra de las tareas que puede
resultar complicada debido a las incompatibilidades
entre navegadores: mientras que Internet Explorer
utiliza su propio método attachEvent para asociar
eventos a elementos de una página, el resto de navegadores utiliza el método addEventListener. Las
librerías Javascript nos ahorran este tipo de problemas y nos permiten tratar eventos de forma más intuitiva. Como ejemplo, podemos ver cómo añadir eventos a un elemento <textarea> utilizando la librería
mootools (http://www.mootools.net).
38
La selección de elementos, AJAX y
la gestión de eventos en cliente son
solo una parte de lo que las librerías
Javascript nos pueden ofrecer
En el ejemplo anterior vemos cómo asociar múltiples eventos a un elemento mediante el método addEvents, cómo crear eventos propios, y cómo lanzar
eventos mediante fireEvent. Si escribimos la palabra
“hello” en el cuadro de texto se lanzará nuestro evento propio, al que hemos llamado “burn”, mientras que
si escribimos la palabra “delayed” obtendremos el mismo resultado, con la diferencia que el evento se lanzará al cabo de 1000 ms.
Y mucho más…
La selección de elementos, AJAX y la gestión de
eventos en cliente son solo una parte de lo que las
librerías Javascript nos pueden ofrecer. Tareas tan
complejas como añadir efectos visuales a nuestra página o implementar un sistema de arrastrar y soltar se
simplifican enormemente utilizando librerías como
jQuery, Prototype o mootools. Es por esto que resulta altamente recomendable conocer las posibilidades
de estas librerías antes de embarcarse en cualquier
proyecto Web.
$(‘myTextarea’).addEvents({
‘focus’: function() {
if ($(‘myTextarea’).value.contains(‘Type here’))
$(‘myTextarea’).value = ‘’;
},
‘keyup’: function() {
if ($(‘myTextarea’).value.contains(‘hello’))
$(‘myTextarea’).fireEvent(‘burn’, ‘hello world!’);
else if ($(‘myTextarea’).value.contains(‘delayed’))
$(‘myTextarea’).fireEvent(‘burn’, “I’m a bit late!”, 1000);
},
‘burn’: function(text) {
$(‘myTextarea’).value = ‘burn! ‘ + text;
}
});
plataforma.net
Daniel Seara
Componentes de uso general
Otro mes, y otro componente. Veamos cómo nuestros otros componentes pueden relacionarse con el sistema operativo, la seguridad, los
usuarios… el Directorio activo.
De la identificación de usuarios
y su relación con la red
Imports AD = System.DirectoryServices
En muchas ocasiones, identificar al usuario que
realiza una acción es requerido en las aplicaciones.
Y más que eso, reflejar en la interfaz del usuario
dicha información, e inclusive las opciones y comportamiento de la misma pueden depender de esta
información. Es por ello que nos resultará útil contar con un componente que encapsule métodos que
nos faciliten la relación con el Directorio activo.
¿Qué necesitamos?
Listado 1 Dominio actual.
Para una funcionalidad básica, es suficiente con utilizar los tipos que se encuentran en System.DirectoryServices. Dentro de ese espacio de nombres,
la clase DirectorySearcher nos permite encontrar
información en el Directorio activo.
Pero antes de esto, necesitamos saber en qué
entorno nos encontramos.
Daniel Seara es mentor
de Solid Quality Mentors
y director del área de
desarrollo .NET. Es MVP
desde 2003 y ponente
habitual de
y Microsoft.
[
NOTA
Nueva idea, nuevo ensamblado: en mi
caso Solid.Servers.ActiveDirectory
Public Class Environment
Private Shared mDomainName As String
Public Shared Function DomainName() As String
If mDomainName = “” Then
mDomainName = _
AD.ActiveDirectory.Domain.GetCurrentDomain.Name
End If
Return mDomainName
End Function
]
Agreguemos a este ensamblado una clase que
nos defina el entorno: Environment. En ella, podremos agregar un método que nos retorne cuál es el
nombre del dominio actual, como se ve en el lis-
Private Shared mActualUser As String
Public Shared Function ActualUserName() As String
If mActualUser = “” Then
mActualUser = _
System.Threading.Thread.CurrentPrincipal.Identity.Name
End If
Return mActualUser
End Function
Listado 2. Usuario actual.
tado 1. Además, podríamos agregar otro método
para obtener la identificación del usuario actual,
como muestra el listado 2.
Internamente, esa clase siempre podría necesitar acceder al dominio para obtener otros objetos y referencias. Entonces sería conveniente disponer de la función privada cuyo código encontramos en el listado 3.
<< dnm.plataforma.net
cosa que los servicios de directorio son
capaces de brindarnos si hacemos una
búsqueda con DirectorySearcher, como
vemos en el listado 4.
Ahora bien, una vez se obtiene un
SearchResult (uno de los miembros de
la variable r), éste contiene un DirectoryEntry y a través de él podemos obtener qué propiedades expone. Pero, por
otra parte, hay algunos grupos que expo-
Private Shared mDomainEntry As AD.DirectoryEntry
Private Shared ReadOnly Property DomainEntry() As AD.DirectoryEntry
Get
If mDomainEntry Is Nothing Then
mDomainEntry = _
AD.ActiveDirectory.Domain.GetCurrentDomain.GetDirectoryEntry
End If
Return mDomainEntry
End Get
End Property
Listado 3. Información del dominio.
En muchas ocasiones, solo necesitaremos información específica de cualquiera
de los elementos del directorio. Y eso
significa que resulta inútil, además de
algo más inseguro, requerir referencias
al espacio de nombres de servicios de
directorio. Siempre que sea posible, es
preferible enmascarar los objetos y usar
los propios para tener un control adecuado de la información brindada.
Por otra parte, los servicios de directorio de Windows tienen un inconveniente, y es que a ellos se accede a través
de ADSI (Active Directory Service
Interfaces), un conjunto de interfaces
COM modeladas a partir del protocolo LDAP (Lightweight Directory
Access Protocol), diseñado en la era
pre-nética ☺. Por lo tanto, no son precisamente la perfección respecto al
diseño orientado a objetos. Cada objeto expone propiedades diferentes, hay
propiedades que retornan más de un
valor, no están adecuadamente identificados los tipos de datos. Conceptualmente, además, se trata de implementaciones genéricas que se expresan usando identificadores de clase (que no son
clases específicas).
Podemos, sin embargo, convertirlas en algo más “nético”, aprovechando cosas como la generación de código
y los mecanismos de reflexión.
[
Dim ms As DirectoryServices.DirectorySearcher = Searcher()
With ms
.Filter = “(objectClass=group)”
.SearchScope = DirectoryServices.SearchScope.Subtree
Dim r As DirectoryServices.SearchResultCollection = .FindAll
End With
Listado 4. Búsqueda de grupos.
Dim colElements As New Dictionary(Of String, MyPropertyInfo)
Sub GetPropertyInfo(ByVal en As DirectoryServices.DirectoryEntry)
For Each v As PropertyValueCollection In en.Properties
If v.Value.GetType.ToString <> “System.__ComObject” Then
With colElements
If .ContainsKey(v.PropertyName) Then
If .Item(v.PropertyName).Count < v.Count Then
.Item(v.PropertyName).Count = v.Count
End If
Else
Dim p As New MyPropertyInfo With _
{.Count = v.Count, _
.DataTypeName = v.Value.GetType.ToString, _
.Name = v.PropertyName}
.Add(v.PropertyName, p)
End If
End With
End If
Next
End Sub
Listado 5. Diccionario de propiedades.
Consideremos, para comenzar, que
posiblemente nos interese administrar
permisos de nuestras aplicaciones en
base a grupos de seguridad. Para ello
necesitaríamos la lista de los grupos,
NOTA
La sintaxis de filtrado en LDAP puede ser compleja. Por ejemplo, los
argumentos encerrados entre paréntesis, los nombres y valores de los filtrados, etc. suelen no ser simples. El primer punto de referencia está en
http://msdn.microsoft.com/es-es/library/system.directoryservices.aspx
]
nen distintas propiedades (o distinta
cantidad de valores para una propiedad).
Por ello, recorreremos todas ellas y
obtendremos una lista de las propiedades existentes. Una vez las tengamos,
podremos generar el código correspondiente a cada una.
En el listado 5 se ve cómo se van
coleccionando las propiedades en un
diccionario donde guardamos una clase MyPropertyInfo (listado 6), que nos
permite almacenar:
<<dotNetManía
Enmascarando los objetos
de directorio
41
<< dnm.plataforma.net
Friend Class MyPropertyInfo
Private mName As String
Public Property Name() As String
Get
Return mName
End Get
Set(ByVal value As String)
mName = value
End Set
End Property
Private mCount As Integer
Public Property Count() As Integer
Get
Return mCount
End Get
Set(ByVal value As Integer)
mCount = value
End Set
End Property
Private mDataTypeName As String
Public Property DataTypeName() _
As String
Get
Return mDataTypeName
End Get
Set(ByVal value As String)
mDataTypeName = value
End Set
End Property
End Class
Listado 6. Clase MyPropertyInfo.
<<dotNetManía
• El nombre de la propiedad.
• La cantidad de valores que contiene.
• El tipo de dato que almacena.
42
En la búsqueda de propiedades, se
excluyen aquellas que son de tipo ComObject, que son las que internamente
ADSI utiliza para la manipulación de
información.
Una vez que se obtiene la colección
completa de propiedades (y asumiendo
que la muestra es lo suficientemente
significativa), podemos generar código
para exponer dichas propiedades un la
clase Group que pretendemos definir.
Utilizamos una plantilla para generar el código correspondiente a cada propiedad. Considerando que habrá propiedades con más de un valor, tendremos
en realidad dos plantillas: una para propiedades simples (listado 7) y otra para
propiedades indexadas (listado 8).
Private m{0} As {1}
Public Property {0}() As {1}
Get
Return m{0}
End Get
Set(ByVal value As {1})
m{0} = value
End Set
End Property
Listado 7. Plantilla para propiedad simple.
Private m{0} As New Dictionary(Of
String, {1})
Public Property {0}(ByVal key As
String) As {1}
Get
Return m{0}(key)
End Get
Set(ByVal value As {1})
If m{0}.ContainsKey(key) Then
m{0}(key) = value
Else
m{0}.Add(key, value)
End If
m{0}(key) = value
End Set
End Property
Listado 8. Plantilla para propiedad Indexada.
Como se ve, en ambos casos se utiliza una sintaxis acorde a los marcadores posicionales de formatos. Ambos
códigos se agregan como recursos en el
Sub GenProperties()
Dim st As New StringBuilder
For Each p As MyPropertyInfo _
In colElements.Values
Dim sFormat As String = “”
If p.Count = 1 Then
sFormat = My.Resources.PropertyDef
Else
sFormat = My.Resources.IndexedPropDef
End If
Dim sDT As String=p.DataTypeName.Replace(_
“[“, “(“).Replace(“]”, “)”)
If p.Count > 1 Then
sDT = sDT.Replace(“(“, “”).Replace(“)”,“”)
End If
st.AppendFormat(sFormat, p.Name, sDT)
Next
Using fi As New IO.StreamWriter(“c:\Props.vb”)
fi.Write(st.ToString)
fi.Close()
End Using
End Sub
Listado 9. Generador de propiedades.
Friend Sub New(ByVal de As DirectoryServices.DirectoryEntry)
For Each v As PropertyValueCollection In de.Properties
If v.Value.GetType.ToString <> “System.__ComObject” Then
Dim sFormat As String = “”
If Me.GetType.GetProperty(v.PropertyName)
.GetIndexParameters.Count = 0 Then
CallByName(Me, v.PropertyName, CallType.Let, v.Value)
Else
For i As Integer = 0 To v.Count - 1
Dim arr As New ArrayList
If v.Value.GetType.IsArray Then
arr.AddRange( _
CType(v.Value, Collections.ICollection))
Else
arr.Add(v.Value)
End If
CallByName(Me, v.PropertyName, _
CallType.Let, i, arr(i))
Next
End If
End If
Next
End Sub
Listado 10. Constructor de la clase Group.
<< dnm.plataforma.net
proyecto de generación (una aplicación
auxiliar que solo sirve para generar este
código).
Entonces, el procedimiento del listado 9 crea una cadena de caracteres
que contiene las definiciones de todas
las propiedades y genera con ellas un
archivo en disco.
Los métodos Replace en el listado 9
permiten quitar la declaración de valor
de tipo array en las propiedades múltiples, ya que eso lo re-implementamos
usando nuestra propia metodología
para la manipulación de dichas propiedades multi-valor.
Usando el código generado en el
archivo props.vb, podemos definir ahora la clase Group, que exponga y sea
capaz de cargar las propiedades a partir de una entrada de directorio. Para
ello, implementamos un constructor
(listado 10) que recibe una variable de
tipo DirectoryEntry para obtener los
valores y utiliza reflexión para cargar
los valores de las propiedades.
Siguiendo procedimientos similares, pero utilizando otros filtros LDAP,
podemos definir clases de usuario, unidad organizativa, servidor, etc.
For Each el In Solid.Servers.ActiveDirectory.Environment.Groups
‘agrega un ítem en la lista
With ListView1.Items.Add(el.name)
‘recorre las propiedades de la clase Group
For Each p As System.Reflection.PropertyInfo In _
el.GetType.GetProperties
‘si aún no está definida la columna, la agrega
If Not ListView1.Columns.ContainsKey(p.Name) Then
ListView1.Columns.Add(p.Name, p.Name)
End If
‘pos es el índice de la columna
Dim pos As Integer = ListView1.Columns(p.Name).Index
‘si el ítem agregado tiene menos subítems,
‘agrega hasta alcanzar la posición
While pos > .SubItems.Count - 1
.SubItems.Add(“”)
End While
‘intenta agregar el valor
Try
.SubItems(pos).Text = _
CallByName(el, p.Name, CallType.Get).ToString
Catch ex As Exception
‘(puede haber errores, que se ignoran)
End Try
Next
End With
Next
Listado 12. Cargando una lista con todos los grupos.
Mejorando los resultados
obtenidos
Figura 1. Lista parcial de grupos.
que nos permita discriminar dichos elementos accesibles por los usuarios.
Public Shared Function Groups() As List(Of Group)
Dim ms As DirectoryServices.DirectorySearcher = Searcher()
Dim g As New List(Of Group)
With ms
.Filter = “(objectClass=group)”
.SearchScope = DirectoryServices.SearchScope.Subtree
Dim r As DirectoryServices.SearchResultCollection = .FindAll
For Each rr As DirectoryServices.SearchResult In r
g.Add(New Group(rr.GetDirectoryEntry))
Next
End With
Return g
End Function
Listado 11. Lista de grupos.
Comencemos por mostrar en un
ListView de una aplicación Windows
Forms todas y cada una de las propiedades de los objetos de tipo Group que
obtenemos de nuestro directorio. Definamos en nuestra clase de entorno un
método que nos devuelva una lista de
los grupos presentes en el directorio
(listado 11).
Podemos obtener el conjunto de
grupos del Directorio activo y presentar en columnas cada una de sus
propiedades como se muestra en el
listado 12.
Ejecutando esta operación, se cargará una lista con todos los grupos, como
muestra la figura 1. En ella podemos ver
<<dotNetManía
El Directorio activo define muchos
elementos que normalmente no se
encuentran disponibles para los usuarios
y que, sin embargo, aparecen cuando consultamos por código utilizando LDAP.
Por ello, siempre es bueno tener claro (y
utilizar adecuadamente) la información
43
<< dnm.plataforma.net
Los servicios de directorio de Windows tienen un inconveniente, y es que a
ellos se accede a través de ADSI (Active Directory Service Interfaces), un
conjunto de interfaces COM modeladas a partir del protocolo LDAP
(Lightweight Directory Access Protocol), diseñado en la era pre-nética
que la propiedad groupType nos permite agruparlos,
para así entender cuáles son los más útiles a nuestros
fines.
Si creamos grupos (aprovechando las características propias del control ListView), veremos claramente a qué me refiero. El listado 13 modifica el anterior, agregando grupos.
Podemos ver entonces en la figura 2 cómo
groupType nos muestra los grupos de administración,
los que permiten reunir y dar permisos a usuarios, los
que contienen servidores, etc.
For Each el In _
Solid.Servers.ActiveDirectory.Environment.Groups
‘agrega un ítem en la lista
Dim it As String = “G” & el.groupType
With ListView1.Items.Add(el.name)
Dim g As ListViewGroup = ListView1.Groups(it)
If g Is Nothing Then
g = ListView1.Groups.Add(it, it)
End If
.Group = g
‘recorre las propiedades de la clase Group
‘...
Listado 13. Agrupando por groupType.
<<dotNetManía
Conclusión
44
Este es otro caso donde establecer algo de código nos
permite personalizar funcionalidades y acceder a información de bajo nivel o de alta seguridad, en un marco controlado. A partir de aquí, se podrá agregar
Figura 2. Tipos de grupos.
métodos que permitan saber si un usuario pertenece
o no a un grupo, quiénes son miembros de un grupo, de una unidad organizativa, etc.
Un comentario final: es necesario ser muy detallista en esto de las pertenencias. Una unidad organizativa puede contener usuarios y grupos… grupos que a su vez pueden incluir usuarios de ésa o
de otra unidad organizativa. Y un grupo puede contener subgrupos, que a su vez pueden contener subgrupos…
Saludos cordiales.
Isla VB
Guillermo «Guille» Som
Extender o no extender...
Los métodos extensores en Visual Basic 2008
La versión de Visual Basic que se incluye con .NET Framework 3.5 o Visual
Studio 2008 permite que creemos métodos que extiendan la funcionalidad
de clases existentes sin necesidad de tener acceso al código fuente de éstas.
En este artículo veremos cómo crear este tipo de métodos y qué consideraciones debemos tener en cuenta para usarlos de la forma más adecuada.
¿Qué es un método extensor?
Guillermo Som ingresará los derechos de autor de
este artículo en la cuenta de Ayuda a Juanma a vivir.
Desde aquí, invita a los lectores de esta isla solidaria para que hagan sus aportaciones o participen en
las subastas que se realizan desde la Web de Juanma para recaudar fondos con el fin de ayudar en la
investigación de una cura para la enfermedad de
Alexander. http://www.ayudajuanma.es.
Los métodos extensores son métodos que podemos definir en nuestro proyecto para extender la funcionalidad de cualquier clase, ya sea de las que tenemos definidas en el propio proyecto o bien clases
definidas en el propio .NET o por cualquier otro
desarrollador. La ventaja es que esa definición de
métodos no requiere que tengamos acceso al código fuente de la clase original, y es especialmente útil
para aquellas clases que están “selladas”, es decir, de
las que no podemos crear nuevas clases derivadas.
Los métodos extensores
La mayoría de las novedades incluidas en los lenguajes de .NET Framework 3.5, y particularmente todo lo relacionado con las expresiones de consulta (LINQ), es posible gracias a los métodos
extensores (o métodos de extensión, que es como
se traduce esta característica en la documentación
de Visual Studio 2008), ya que mucha de esa nueva funcionalidad se implementa mediante métodos agregados a las clases e interfaces del propio
.NET Framework, particularmente en extensiones agregadas a la interfaz IEnumerable(Of T). Pero,
lo primero es lo primero, y es explicar qué es un
método extensor.
¿Cómo definir un método extensor en Visual
Basic?
Para crear métodos de extensión en Visual Basic,
tenemos que definir esos métodos en un módulo, ya
que los métodos extensores deben estar siempre disponibles y no depender de ninguna instancia en particular. Además, tenemos que indicarle al compilador que el método en cuestión está extendiendo la
funcionalidad de una clase; para ello tendremos que
indicar qué clase queremos extender, y esa clase la
indicaremos como el primer parámetro del método.
Pero además también tenemos que indicarle al compilador que estamos ampliando la funcionalidad de
dicha clase, y para ello tenemos que aplicar el atri-
<<dotNetManía
Guillermo “Guille”
Som
Es Microsoft MVP de
Visual Basic desde
1997. Es redactor de
dotNetManía, mentor
de Solid Quality Mentors, orador de INETA
Latam, y autor de los
libros “Manual Imprescindible de Visual Basic
.NET” y “Visual Basic
2005” y “Novedades
de Visual Basic 9.0” y
próximamente “Aprenda C# 3.0 desde 0.0 –
Parte 3: Lo nuevo” .
http://www.elguille.info.
Isla solidaria
45
<< dnm.isla.vb
buto Extension a la definición del método extensor.
El atributo Extension está definido en el espacio de
nombres System.Runtime.CompilerServices. Para comprender mejor cómo definir un método extensor, veámoslo con un ejemplo. En el listado 1 tenemos un
método llamado TrimMid que extiende la funcionalidad de la clase String añadiendo un nuevo método,
que en este caso concreto elimina todos los caracteres “blancos” que tenga una cadena (incluso los que
no están al principio o al final).
Module Extensiones
<Extension()> _
Public Function TrimMid(ByVal str As String) _
As String
Dim q = From c In str _
Where Not Char.IsWhiteSpace(c) _
Select c
Return q.ToArray
End Function
End Module
Listado 1. Un método que extiende la funcionalidad de la
clase String
En el código del listado 1 estamos usando expresiones de consulta para hacer el trabajo de recuperar
solo los caracteres que no son considerados como
caracteres blancos, para lo que he usado la función
IsWhiteSpace de la clase Char, y debido a que el resultado de esta expresión de consulta es una colección del
tipo IEnumerable(Of Char), he tenido que usar el método extensor ToArray (definido por el propio .NET Framework) para convertir esa colección en un array de
tipo Char, que como sabemos, el propio Visual Basic
convierte en una cadena de caracteres, que es lo que
finalmente se devuelve en esta función.
<<dotNetManía
Utilizar los métodos extensores
46
Para usar los métodos extensores no tenemos que
hacer nada en especial, salvo indicar el método que
queremos usar, pero esto es algo que siempre tendremos que hacer para usar un método cualquiera,
sea propio de la clase o porque se haya definido como
método extensor.
Si estamos usando Visual Studio 2008 para escribir nuestro código, al mostrar los métodos extensores por medio de Intellisense, éstos se mostrarán con
una flechita azul; de esta forma nos resultará fácil identificar cuáles son los métodos propios y cuáles los
métodos de extensión. En la figura 1 vemos cómo se
muestra el método extensor que tenemos definido en
el listado 1.
Figura 1. Intellisense muestra los métodos
extensores con una flecha
Como vemos en la figura 1, para usar el método
extensor que hemos definido en el listado 1 solo tenemos que indicarlo en una variable (o constante) del
tipo que estamos extendiendo, y la forma de usarlo es
la habitual, es decir, que no tenemos que hacerlo de
ninguna forma extraña. Otra cosa es que queramos
usar ese método extensor como un método compartido normal y corriente. Si es esa la forma en que queremos usarlo, lo haremos indicando el nombre de la
clase (módulo en nuestro caso) en el que está definido, pero como lo estamos usando como un método
“normal”, y no de extensión, tal como vemos en el listado 2, tendremos que pasarle como argumento la
cadena a la que queremos quitarle todos los espacios
que tenga.
res = Extensiones.TrimMid(s)
Listado 2. Los métodos extensores se pueden usar como
métodos de clase
Pero debido a que ese método está definido en
un módulo, Visual Basic también nos permitiría usarlo sin necesidad de indicar el nombre del módulo en
el que está definido, ya que Visual Basic siempre hace
una importación del módulo (tipo); por tanto, todos
los métodos y demás elementos que contenga estarán
siempre accesibles. Pero esto es un tema diferente,
y si el lector quiere saber más sobre los módulos y
cómo se comportan, puede leer el artículo publicado en esta misma sección del número 46 de dotNetManía.
¿Qué podemos extender?
Ya hemos visto cómo crear y usar un método
extensor, y también sabemos que podemos agregar
nueva funcionalidad a cualquier tipo de datos, incluso los tipos por valor. Por ejemplo, podemos agregar
<< dnm.isla.vb
nueva funcionalidad a los tipos enteros, etc. Pero para
un buen uso de todos los métodos extensores que decidamos crear, debemos saber qué podemos extender
y qué problemas (o inconvenientes) nos podemos
encontrar al crear esas extensiones.
Lo primero que debemos saber es que solo podemos crear extensiones en métodos, es decir, elementos Function o Sub. Lo habitual es que los métodos
extensores siempre devuelvan algo, pero tal como Visual
Basic funciona, es muy fácil crear un método extensor
de tipo Sub (no devuelve un valor); pero en ese caso,
tendremos que definir el parámetro del tipo a extender por referencia. Por ejemplo, si tenemos el código
del listado 3, estamos agregando un método a la clase
String que permite modificar la cadena a la que se aplica ese método de extensión, y por tanto, lo podremos
usar tal como vemos en el listado 4. Pero esto no es lo
habitual, además de que es mejor no modificar directamente el objeto al que se aplica el método por la sencilla razón de que si usamos una constante no hará nada,
y por tanto, no tendrá ninguna utilidad.
[
NOTA
Comentar que solo Visual Basic permite crear métodos
extensores que reciban el primer parámetro por referencia (el tipo a extender), ya que en C# no podemos definir ese primer parámetro usando los modificadores out
o ref. Y aunque ese método lo podamos usar desde C#
(agregando una referencia al proyecto de VB), esa extensión no aparecerá en la lista de métodos disponibles.
]
Sobrecargas en los métodos extensores
Ya sabemos que solo podemos extender métodos,
la siguiente pregunta es: ¿podemos crear sobrecargas
de métodos existentes? La respuesta es: sí. Incluso
podemos definir métodos que tengan la misma firma
que uno existente en la clase que queremos extender,
aunque en este caso, simplemente se ignorará esa definición, ya que las sobrecargas definidas en las clases
<Extension()> _
Public Sub TrimAll(ByRef str As String)
str = str.TrimMid
End Sub
s.TrimAll()
Listado 4. Los métodos extensores de tipo Sub deben
aplicarse a la variable
En cualquier caso, si es ésta la funcionalidad que
queremos en nuestro método extensor, lo recomendable es definirlo como una función, que además de
modificarla, devuelva el contenido de esa variable. De
esa forma, el método servirá tanto para variables como
para constantes. En el listado 5 vemos una modificación del método TrimAll para que haga lo que acabo
de comentar.
<Extension()> _
Public Function TrimAll(ByRef str As String) _
As String
str = str.TrimMid
Return str
End Function
Listado 5. Los métodos extensores admiten parámetros por
referencia
Las definiciones “nativas” del tipo
de datos siempre tienen preferencia,
y los métodos extensores solo se
usarán si no existe una sobrecarga
adecuada en el tipo que estamos
extendiendo
tienen preferencia sobre las extensiones que definamos. Y si eso ocurre, el compilador no nos avisará de
ninguna forma de que esa extensión no se usará. Esto
no hay que tomarlo con un error, ya que es conveniente saber que siempre tienen preferencia las definiciones “nativas” del tipo de datos, y que los métodos extensores solo se usarán si no existe una sobrecarga en el tipo que estamos extendiendo. Dicho esto,
queda claro que podemos crear sobrecargas de métodos existentes en el tipo que queremos extender e
incluso entre diferentes métodos extensores.
<<dotNetManía
Listado 3. Un método extensor que no devuelve un valor
47
<< dnm.isla.vb
Métodos extensores en tipos genéricos
Los tipos de datos que indiquemos para un método extensor también pueden ser genéricos. Por ejemplo, si quisiéramos crear un método extensor para los
tipos por valor (estructuras), podemos hacer algo
como vemos en el listado 6.
<Extension()> _
Public Function ToHex(Of T As Structure)( _
ByVal num As T) As String
Return Microsoft.VisualBasic.Hex(num)
End Function
Listado 6. Los métodos extensores los podemos aplicar
también a tipos genéricos
Aunque no es necesario que hagamos ninguna restricción, en este ejemplo la he hecho para que quede
claro que podemos restringir los tipos a los que queramos aplicar ese método.
<<dotNetManía
Distribuir un proyecto con métodos extensores
48
Los métodos extensores los podemos usar en proyectos en los que hayamos definido el módulo con los
métodos; pero si queremos que otros programadores
también puedan usarlos, debemos compilarlo en un
ensamblado y distribuirlo. De esta forma pondremos
a disposición de otros programadores los métodos de
extensión que hemos definido, pero si queremos hacer
esto, tenemos que tener en cuenta que los módulos
tienen un ámbito predeterminado del tipo Friend, y
por tanto solo estarán disponibles dentro del mismo
proyecto en el que estén definidos. Sabiendo esto,
debemos tener la precaución de definir el módulo con
el modificador Public. Y para evitar conflictos de nombres, ese módulo debería estar en un espacio de nombres, aunque esto último no es necesario hacerlo expresamente, ya que al crear un proyecto de Visual Basic
siempre se crea un espacio de nombres para dicho proyecto, pero es conveniente tenerlo en cuenta.
Si nuestro ensamblado con los métodos extensores se usará desde C#, debemos tener en cuenta que
ese lenguaje no permite usar métodos en los que haya
parámetros opcionales. Por tanto, la forma de usar
un método extensor que defina parámetros opcionales será diferente en Visual Basic que en C#. Por ejemplo, si tenemos el método definido en el listado 7,
desde Visual Basic lo podemos usar de las dos formas
mostradas en el listado 8. Sin embargo, en C# siempre tendremos que indicar el valor de ese parámetro
opcional, y por tanto tendremos que usarlo tal como
vemos en el listado 9.
<Extension()> _
Public Function TrimOpcional( _
ByVal str As String, _
Optional ByVal quitarCifras As _
Boolean = False) As String
Dim sb As New StringBuilder
For Each c In str
If Char.IsWhiteSpace(c) _
Then Continue For
If quitarCifras AndAlso Char.IsNumber(c) _
Then Continue For
sb.Append(c)
Next
Return sb.ToString
End Function
Listado 7. Definición de un método extensor con
parámetros opcionales
s = “ Prueba con cifras 123 y letras”
res = s.TrimOpcional
Console.WriteLine(res)
Console.WriteLine()
res = s.TrimOpcional(True)
Console.WriteLine(res)
Listado 8. En Visual Basic podemos usar los métodos
extensores con parámetros opcionales de la forma habitual
s = “ Prueba con cifras 123 y letras”;
// En C# hay que indicar siempre los parámetros
opcionales
res = s.TrimOpcional(false);
Console.WriteLine(res);
Console.WriteLine();
res = s.TrimOpcional(true);
Console.WriteLine(res);
Listado 9. En C# siempre tenemos que indicar el parámetro
opcional
¿Cuándo usar los métodos extensores?
Ésta seguramente es una pregunta que algunos se
harán para decidir si tienen que usar los métodos
<< dnm.isla.vb
extensores o buscar otra forma de conseguir lo misgo fuente de esos tipos que queremos extender. En la
mo. La respuesta, como siempre, dependerá de lo que
Web de la revista puede descargar el código de ejemnosotros decidamos.
plo, en el que también se incluye un proyecto de C#
Si somos los autores del tipo de datos que queremos
para que pueda probar todo lo comentado. Y como
extender, nos tendremos que plantear si ese método es
en esta isla también llega el verano, nos tomamos un
necesario en la mayoría de ocasiones que se utilice el tipo
pequeño descanso, pero en septiembre volveremos
de datos. Si la respuesta es no, ya que es posible que sólo
con más cosas interesantes sobre Visual Basic.
lo necesitemos en contadas ocasiones, es obvio que la
¡Feliz verano!
mejor opción es crear un método
extensor.
Otra razón para que nos decantemos por el método extensor en
TODAS LAS
lugar de modificar la definición del
NOVEDADES DE
tipo de datos es para mantener la
VISUAL BASIC 9.0
misma versión de ese tipo, ya que si
AL DETALLE
modificamos los métodos expuestos,
lo lógico es pensar que estamos creando una nueva versión, y si ese tipo
INCLUSO LO QUE
de datos lo tenemos compilado en
OTROS NO TE
un ensamblado, deberíamos increHAN CONTADO
mentar la versión de dicho ensamNUNCA
blado, y esto es posible que no nos
interese hacerlo.
También debemos tener en cuenY TODO DE UNA
ta que los métodos definidos en el
FORMA “CASI”
propio tipo siempre tienen prefeFÁCIL DE
rencia sobre los definidos como
ENTENDER
métodos extensores. Por tanto, si
queremos ofrecer esa funcionalidad
y no dejar que sea el usuario que utiliza nuestro tipo el que defina un
método con ese nombre, la opción
más efectiva es definirlo en el propio
tipo.
Por otro lado, si no tenemos
acceso al código de ese tipo que queremos extender, solo nos queda la
opción de definirlo como un método extensor.
Pero como he comentado antes,
la decisión final la tenemos que
tomar nosotros, y esa decisión la tenEL PRIMER E-BOOK EN CASTELLANO EDITADO POR
dremos que tomar evaluando cual
será la opción que mejor se adapta a
SOLID QUALITYTM PRESS SOBRE VISUAL BASIC 9.0
nuestras necesidades.
Conclusiones
En este artículo hemos visto cómo
utilizar los métodos extensores, de
forma que podamos agregar nueva
funcionalidad a las clases existentes
sin necesidad de tener acceso al códi-
COMO EL MISMO TÍTULO SUGIERE: "NOVEDADES
DE VISUAL BASIC 9.0", ESTE LIBRO CONTIENE TODA
LA INFORMACIÓN SOBRE LA NUEVA VERSIÓN DEL
COMPILADOR DE VISUAL BASIC QUE SE INCLUYE
EN VISUAL STUDIO 2008.
http://www.solidq.com/ib/eBookDetail.aspx?Id=1
todonet@qa
[email protected]
Dino Esposito
Arquitecto en IDesign,
Dino Esposito es una de
las autoridades mundiales
reconocidas en tecnologías
Web y arquitectura de
software. Sus libros más
recientes son "Programming ASP.NET 3.5-Core
Reference" e "Introducing
Microsoft ASP.NET AJAX"
(Microsoft Press). Es
ponente regular en eventos de la industria de
ámbito mundial, como
TechEd o DevConnections, y europeos, como
DevWeek y Basta.
Plantillas ASP.NET
e inferencia de tipos en C#
Abordamos este mes un examen en profundidad de las propiedades de las plantillas
de ASP.NET, y concluiremos explicando los pros y contras del uso de la nueva palabra
reservada var en C#.
Soy suscriptor de dotNetManía a través de la empresa, y he leído en el número 44, pág. 52, un artículo tuyo sobre cómo cargar en tiempo de ejecución el ContentTemplate de un control UpdatePanel.
Es muy interesante, y quiero hacer algo similar con los ItemTemplate de un ListView, pero me
encuentro con una duda. En tiempo de ejecución, voy creando los literales y los controles que necesito para hacer container.Controls.Add, y funciona bien, pero mi intención es poder tener plantillas
guardadas en la base de datos para que el usuario en tiempo de ejecución seleccione la que quiera.
Entonces no sé cómo puedo "traducir" un trozo de código que contenga etiquetas HTML del tipo
<TR>, pero que también incluya etiquetas ASP.NET del tipo <asp:label> para poderlo cargar de una
forma directa en una plantilla. ¿Tienes algún ejemplo al respecto?
Me da la impresión de que te has propuesto construir la perfecta interfaz Web que resulte totalmente configurable. Si es así, resulta un reto más
que interesante. Tu pregunta puede desdoblarse
en dos. Una es cómo añadir en la práctica algún
contenido a la propiedad ItemTemplate de un control ListView. La otra es cómo transformar un
texto leído de una base de datos en una colección
de controles ASP.NET que puedan ser añadidos
a la plantilla de un control enlazado a datos. Vamos
a responder primero a estos dos aspectos, y después añadiré alguna recomendación sobre cómo
construir la aplicación ASP.NET “perfecta”, o sea,
totalmente contenida en una base de datos.
La propiedad ItemTemplate, al igual que cualquier otra propiedad de las plantillas de los controles ASP.NET, se define mediante la interfaz
ITemplate. ITemplate se define de la siguiente
forma:
public interface ITemplate
{
void InstantiateIn(Control container);
}
Casi siempre, las propiedades de las plantillas
se establecen de forma declarativa mediante
código de marcas ASP.NET, ya sea de forma
manual, o mediante código autogenerado por
algún diseñador de Visual Studio. Y establecer
una propiedad de plantilla de forma manual es
interesante muchas veces y más sencillo de lo
que se piensa. Basta con disponer de un objeto que exponga la interfaz ITemplate. También
se puede crear un tipo personalizado y hacer
que implemente dicha interfaz. Supongamos
que ese tipo se llamase MyTemplateFromDb . El
siguiente código muestra cómo usarlo en código real:
Lo que sucede cuando el control
ListView utiliza la plantilla depende
esencialmente de la implementación
del método InstantiateIn
Internamente, cuando el control ListView necesita el contenido de la plantilla, llama al método InstantiateIn del objeto ITemplate que le hemos suministrado. Y este comportamiento no es específico del
control ListView ; es típico de cualquier control
ASP.NET que soporte plantillas con propiedades. De
forma que lo que sucede cuando el control ListView
utiliza la plantilla depende esencialmente de la implementación del método InstantiateIn.
¿Cómo podría ser una implementación de este
tipo en una clase MyTemplateFromDb? Si deseamos cargar código de marcas a partir de una base de datos,
ejecutamos una consulta sobre la tabla apropiada y
asociamos la información leída a una variable de tipo
string, como en el código siguiente:
public class MyTemplateFromDb : ITemplate
{
public void InstantiateIn(Control container)
{
string markup = ReadFromDb(...);
// ...
}
// ...
}
El siguiente problema es encontrar una forma de
transformar el código de marcas en una colección de
controles. Sin más rodeos, necesitas un parser de
ASPX. ¿Construimos uno o nos compramos alguno
de la oferta existente?
Afortunadamente, ASP.NET viene con un método no muy conocido que invoca al parser propio de
ASPX. En particular, se puede sustituir el parser estándar por uno personalizado modificando una zona del
fichero Web.config. El método al que me refiero averigua cuál es el parser adecuado para la página activa
y le utiliza para llevar a cabo la operación. El método es ParseControl y se define en la clase TemplateControl, que a su vez hereda de Control y es el antecesor jerárquico de la clase Page. Este método se define de la siguiente forma:
public Control ParseControl(string content);
Este método acepta una cadena de marcado
ASP.NET y la analiza para producir un objeto Control. Este objeto puede entonces añadirse a cualquier
formulario Web, control de usuario o propiedad Template. Si el código de marcado contiene más de un
control, se devuelve un grafo cuya raíz es el objeto
padre y podemos averiguar cuántos controles individuales posee mediante el siguiente código:
Control root = this.ParseControl(markup);
int numOfControls = root.Controls.Count;
En este punto, en el cuerpo del método InstantiateIn añadimos simplemente este grafo de
controles al contenedor suministrado, tal y como
se muestra aquí:
public class MyTemplateFromDb : ITemplate
{
public void InstantiateIn(Control container)
{
string markup = ReadFromDb(...);
Control root = this.ParseControl(markup);
container.Controls.Add(root);
// ...
}
// ...
}
En ASP.NET, existen dos sobrecargas del método ParseControl. La segunda sobrecarga tiene la
siguiente definición:
public Control ParseControl(string content,
bool ignoreParserFilter);
El segundo parámetro booleano indica si el parser debe tener en cuenta cualquier filtro indicado para
establecer qué etiquetas se ignoran (este es otro aspecto de ASP.NET sutilmente configurable).
Tal y como hemos dicho, el método ParseControl
emplea el parser activo de ASP.NET. Así que, cuando el mecanismo se activa, el proceso es exactamente igual al proceso predeterminado directamente por
ASP.NET. Esto significa, por ejemplo, que bloques
consecutivos de literales HTML son asociados al mismo control.
<<dotNetManía
MyTemplateFromDb template = new
MyTemplateFromDb(...);
ListView1.ItemTemplate = template;
[email protected] [email protected]
<< dnm.todonet@qa
51
<<dotNetManía T o d o t N e t . q a @ d o t n e t m a n i a . c o m T o d o t N e t . q a @ d o t n e t m a n i a . c o m
<< dnm.todonet@qa
52
Otro enfoque, quizás más sencillo, supone el uso del método LoadTemplate del control TemplateControl.
Este método toma una URL de un
control de usuario como entrada y
devuelve un objeto creado dinámicamente que contiene el control de
usuario más la implementación de
ITemplate. El tipo real de ese objeto
devuelto es SimpleTemplate . Para
usarlo, sin embargo, se necesita tener
un UserControl, o sea que la cuestión
aquí es cómo obtener un control desde una base de datos.
Hay dos formas. Una supone que
se cree un control de usuario con toda
la interfaz de usuario necesaria y se
almacene la URL del control en la
base de datos. La otra supone leer el
código de marcado de la base de
datos y crear un fichero temporal con
la extensión ASCX para cargarlo en
la plantilla del control ListView. Sin
embargo, crear ficheros temporales
puede ser problemático en ASP.NET,
más que por problemas de codificación por aspectos relacionados con
la seguridad.
Y para concluir con la respuesta a esta pregunta, permíteme echar
un breve vistazo a una tecnología
ASP.NET que puede utilizarse para
almacenar un sitio Web entero en
una base de datos. Se basa en unos
componentes especiales conocidos
como proveedores virtuales de
rutas (virtual path providers), y se
trata de una clase que suministra un
conjunto de métodos para habilitar
la posibilidad de que una aplicación
Web recupere sus recursos de un
sistema virtual de ficheros. Con este
sistema se puede almacenar esos
datos donde se desee, incluyendo
por supuesto una tabla de base de
datos.
DbPathProvider provider = new
DbPathProvider();
HostingEnvironment.
RegisterVirtualPathProvider(provider);
El código precedente muestra
cómo registrar un proveedor personalizado en una aplicación ASP.NET.
Este código debería ejecutarse cuando se lanza la aplicación. ASP.NET
interroga al proveedor virtual registrado y lee el contenido indicado por
éste. Se puede crear un proveedor
personalizado que recupere el contenido de cada página lógica ASPX a
partir de cualquiera de los almacenes
de datos soportados, incluyendo la
tabla de la base de datos que nos ocupa. Para más información sobre los
proveedores virtuales de rutas, visita
la página: http://msdn.microsoft.com/
en-us/library/system.web.hosting.
virtualpathprovider.aspx.
En mi empresa estamos discutiendo acerca de la utilización de la nueva palabra reservada var, introducida
con LINQ, y nos gustaría saber si se debería usar también en otras situaciones. Esto es, ¿si se utiliza en
contextos diferentes de las consultas LINQ, afectaría a la legibilidad, el rendimiento u otros aspectos del
sistema?
La palabra reservada var en C# 3.0 tiene que ver con la
inferencia de tipos, pero no con LINQ como mecanismo de consultas. Simplemente, o el desarrollador es
demasiado perezoso para declarar el tipo exacto de objeto que está definiendo, o no lo conoce en absoluto, de
modo que le indica al compilador que utilice el tipo
determinado por la expresión de inicialización de la sentencia. Puede que el desarrollador realmente no conozca el tipo que devuelve esa expresión.
¿Cómo puede ser que no conozcamos el tipo de variable que queremos usar? Solo hay una posibilidad razonable: que se estén manejando tipos anónimos. Un tipo
anónimo es un tipo sin nombre que se define en C# al
utilizar inicializadores, como por ejemplo en:
var person = new {
FirstName="Nancy", LastName="Davolio", Age=28
};
Para el CLR, los tipos anónimos y no anónimos son
exactamente lo mismo. Los tipos anónimos se pueden
utilizar en escenarios muy variados, pero se introduje-
ron principalmente para el soporte de las consultas
integradas de LINQ. De forma que aquí está el “quid”
de la cuestión. La palabra reservada var se usa principalmente para recoger los resultados de consultas
LINQ.
var data = from c in db.Customers
select new {c.CompanyName, c.ContactName};
En C# 3.0, var no indica una referencia tardía (late
binding), ya que se resuelve estáticamente. Debido a
esto, se requiere siempre una expresión de asignación
en la sentencia para evitar un error del compilador. De
forma que, al usarla, siempre se obtiene un elemento
fuertemente tipado. El compilador realiza todo el trabajo, así que no hay sobrecarga extra alguna en tiempo de
ejecución. En general, la utilización de la palabra reservada var con tipos no anónimos es posible, pero no recomendable. Sin dudas, permite teclear menos código.
Pero, ¿cuánto se gana en productividad al evitar escribir un nombre de tipo? A mí me parece una cuestión de
pereza más que de rapidez.
Traducido al castellano por Marino Posadas
…We make sure
your application rocks!
Advantage Database Server es un sistema de base
de datos cliente/servidor de alto rendimiento y totalmente equipada, especialmente diseñado para dar
respuesta a las necesidades de los desarrolladores de
aplicaciones
• Proporciona tanto acceso a tablas de datos ISAM como SQL
• Acceso nativo a archivos dbf a través de Cliente/Servidor,
sin necesidad de importar datos.
• Multi Plataforma (Windows, Linux, Netware)
• Bajo Coste Total de Propiedad: fácil instalación, administración
cero, Requisitos mínimos de hardware.
Mejoras y nuevas Funcionalidades en la version 9:
• Mejoras en el soporte de FoxPro: Advantage 9 soporta la versión
de Visual FoxPro 9.
• SQL Debugger ha sido añadido a la versión 9 de ADS Architect.
• Notificaciones de Eventos.
• 64-bit en servidores Windows y Linux: Advantage ha sido
adaptado para funcionar como aplicación nativa de 64-bit en
las versiones de x64 de Windows y Linux.
www.Abox.com
A que espera!
Consiga ya 2 usuarios
gratuitos (solo para
desarrollo) enviando un
Email a [email protected]
C/ Manso, 26-28, 2ª planta
08015 Barcelona
Teléfono: 93 426 22 57
E-mail: [email protected]
Laboratorio.net
Octavio Hernández
Aspose.Total for .NET
Este mes presentamos Aspose.Total for .NET, una potente suite de componentes reutilizables creados y comercializados por la empresa australiana Aspose,
que pueden ser adquiridos conjuntamente o por separado para ser incorporados a todo tipo de aplicaciones .NET.
Ficha técnica
Nombre: Aspose.Total for .NET
Versión: 1.4
Fabricante: Aspose
Sitio Web: http://www.aspose.com
Categoría: Componentes reutilizables
Precio:
• Suscripción a suite completa: 1.999 USD
• Paquetes individuales: desde 249 USD
• Descuentos por compra de múltiples productos y licencias.
• Todas las compras incluyen actualizaciones y parches, más soporte técnico
gratuito durante un año.
Octavio Hernández es
Mentoring Team
Leader de Plain
Concepts, editor
técnico de
dotNetManía y tutor
de campusMVP.
Es MVP de C# desde
2004, MCSD y MCT.
Como podrá confirmar el lector al consultar la
tabla 1, Aspose.Total for .NET va más allá de
lo que ofrecen otras suites de similares propósitos
disponibles en el mercado, incluyendo no solo los
clásicos controles destinados a potenciar la interfaz de usuarios de nuestras aplicaciones (rejilla,
gráficos comerciales, etc.), sino además toda una
serie de componentes utilitarios para llevar a cabo
de una manera cómoda y eficiente diferentes tareas que se presentan con bastante frecuencia durante el desarrollo de aplicaciones. Es de destacar que
la empresa ofrece también una versión paralela en
Java de la suite, Aspose.Total for Java, hecho que
puede ser muy interesante para las empresas que
desarrollen tanto para .NET como para la plataforma Java.
Componentes para formatos de ficheros
Uno de los principales grupos de componentes que
incorpora Aspose.Total es el de los componentes que
permiten el tratamiento de diferentes formatos de ficheros muy comunes en la informática de hoy, como son
todo tipo de documentos de la suite Office (Word, Excel,
PowerPoint) u otros como Adobe PDF o Adobe Flash.
Se trata de clases que han sido desarrolladas en C# al
100% y no dependen de manera alguna de la disponibilidad en el equipo de destino de ningún prerrequisito, lo que se traduce en una máxima economía y facilidad de despliegue, además de un excelente rendimiento. La amplitud y naturalidad de los modelos de
objetos de documento (DOM) que los diferentes componentes ofrecen los hacen ideales para todo tipo de
tareas de automatización. En lo relativo a los formatos
de Office, cabe destacar el soporte completo para Open
XML, ya un estándar internacional.
Componentes visuales
Por supuesto, no podía faltar en una suite como ésta un
conjunto de controles destinados a potenciar las interfaces de usuario de las aplicaciones Windows y
ASP.NET. Además de las tradicionales rejillas y visores de gráficos comerciales, cabe destacar en esta categoría un potente editor de textos (tanto para Windows
como para la Web) que pone a disposición de sus usuarios muchas de las posibilidades de Word, y un completo control de código de barras (figura 1).
Componentes utilitarios
Por último, la otra categoría en la que se puede agrupar a varios de los componentes que integran Aspo-
<< dnm.laboratorio.net
se.Total es la de utilitarios. Se trata de componentes no
visuales que facilitan la implementación de diversas tareas, que van desde la programación de aplicaciones conectadas a la comprobación ortográfica. Puede ver un resumen de lo que hacen esos componentes en la tabla 1.
Un pequeño ejemplo
Difícilmente pueda ofrecer algo novedoso para el lector
versado en el desarrollo .NET un ejemplo básico en el
que se muestre cómo utilizar una librería de clases. Por
Librerías que componen Aspose.Total for .NET
Componentes para formatos de ficheros
Aspose.Words
Permite leer, crear y modificar documentos de Word sin utilizar Microsoft Word. Ofrece soporte
completo para todas las funcionalidades que ofrece Word, y para el tratamiento de documentos en
los formatos DOC, DOCX, RTF, HTML y TXT.
Aspose.Cells
Permite leer, crear y modificar documentos de Excel sin utilizar Microsoft Excel. Ofrece soporte
completo para todas las funcionalidades que ofrece Excel, y para el tratamiento de documentos en
los formatos XLS y XLSX, además de la importación y exportación a otros diversos formatos.
Aspose.Slides
Permite leer, crear y modificar documentos de PowerPoint sin utilizar Microsoft PowerPoint. Ofrece soporte completo para todas las funcionalidades que ofrece PowerPoint, y para el tratamiento de
documentos en los formatos PPT y PPTX.
Aspose.Pdf
Permite a las aplicaciones .NET generar documentos PDF (tanto mediante código como a partir
de plantillas XML y ficheros XSL-FO) sin utilizar Adobe Acrobat. Incluye soporte para numerosas
características avanzadas, como compresión, utilización de tablas, gráficos, imágenes, hiperenlaces
o fuentes personalizadas.
Aspose.Pdf.Kit
Permite a las aplicaciones .NET gestionar y actualizar documentos PDF sin utilizar Adobe Acrobat. Orientado a tareas de tratamiento de información, como la combinación de datos en documentos
existentes y la gestión de formularios embebidos en ficheros PDF.
Aspose.Tasks
Permite a las aplicaciones .NET generar y manipular documentos de Project (MPX, MPP, MPD y
XML) sin utilizar Microsoft Project, ofreciendo un amplio conjunto de funcionalidades relacionadas con la gestión de especificaciones de proyectos.
Aspose.Flash
Permite generar y manipular dinámicamente documentos de Flash desde las aplicaciones .NET, una
posibilidad muy interesante de cara al desarrollo de sitios Web impactantes.
Aspose.Form
Ofrece un conjunto de controles Web que permiten trabajar con plantillas de InfoPath a través de
un navegador.
Aspose.Grid
Conjunto de dos potentes componentes de rejilla, uno para aplicaciones Windows y otro para aplicaciones Web, que ofrecen potentes API para dar un control total sobre la apariencia y comportamiento de las rejillas.
Aspose.Chart
Componente que hace posible la incorporación de gráficos comerciales y científicos a las aplicaciones
.NET. Soporta 21 tipos de gráficos diferentes, así como innumerables variaciones de los mismos, y diversos efectos avanzados como la renderización 3D, transparencias, gradientes o dibujo personalizado.
Aspose.Editor
Control que posibilita la edición de documentos DOC, RTF y HTML dentro de las aplicaciones
Windows Forms y ASP.NET. Nos permite visualizar, editar e imprimir documentos de manera muy
similar a Microsoft Word, pero desde nuestra aplicación y de manera autónoma.
Aspose.BarCode
Componentes robustos y fiables que permiten de una manera sencilla la incorporación de funcionalidad relacionada con códigos de barras en las aplicaciones .NET. Soporta las principales especificaciones y estándares del mercado, y permite la exportación a múltiples formatos de imagen.
Aspose.AdHoc
Generador de consultas SQL para aplicaciones ASP.NET, potente y fácil de usar, que puede ser utilizado para la generación directa de informes o la implementación de pantallas de búsqueda.
<<dotNetManía
Componentes visuales
55
<< dnm.laboratorio.net
Librerías que componen Aspose.Total for .NET
Componentes utilitarios
Aspose.Network
Paquete de componentes .NET flexibles y fáciles de usar para la programación de todo tipo de aplicaciones de comunicaciones. Incluye no solo implementaciones de la gran mayoría de los protocolos de red actualmente en uso, sino también numerosas clases auxiliares que simplifican el desarrollo. Excelente complemento para las clases del espacio System.Net de .NET.
Aspose.Workflow
Componente que ofrece un potente motor de flujos de trabajo, conjuntamente con un conjunto de
objetos de flujo construidos alrededor del estándar WFMC.
Aspose.iCalendar
Librería .NET que ofrece numerosas clases y algoritmos para la generación de patrones temporales de recurrencia y la planificación de tareas, cuya implementación es coherente con la especificación iCalendar (RFC 2445).
Aspose.Spell
Componente para la verificación ortográfica en más de 20 idiomas. Soporta diccionarios personalizados y puede ser utilizado tanto en aplicaciones para Windows como para la Web.
Aspose.ASPXpand
Componente para aplicaciones ASP.NET que ofrece implementaciones de más de 60 funciones
comunes en las aplicaciones Windows pero difíciles de programar en aplicaciones Web: asignación
de foco, edición con máscaras, completamiento automático, gestión de la tecla Intro, entre otras.
Tabla 1
lo tanto, nos limitaremos aquí a presentar la apariencia del Cuadro de herramientas de Visual Studio 2008 después
de instalar uno de los paquetes integrantes de la suite, en este caso el de códigos
de barras (figura 1), y a mostrar un fragmento de código que utiliza el componente Aspose.Words (listado 1), para que
el lector pueda hacerse una idea del modelo de programación que la librería ofrece. El sitio Web del fabricante ofrece
mucha más información general, ejemplos de código y de documentación,
además de la posibilidad de descargar versiones de evaluación de 30 días.
using System;
using System.Windows.Forms;
using Aspose.Words; // espacio de nombres de la librería
namespace WinApp4
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
// abrir documento
Document doc = new Document(@”D:\DNM\49\Lab49_Aspose.docx”);
// clase que encapsula los métodos
// de manipulación de documentos Word
DocumentBuilder builder = new DocumentBuilder(doc);
// establecer formato
builder.Font.Color = System.Drawing.Color.Blue;
builder.Font.Underline = Underline.Single;
// localizar un marcador
builder.MoveToBookmark(“FIRMA”);
// insertar hiperenlace
builder.InsertHyperlink(“Octavio Hernandez”,
“http://geeks.ms/blogs/ohernandez/”, false);
// guardar documento modificado
doc.Save(@”D:\DNM\49\Lab49_Aspose_Firmado.docx”);
}
}
}
Listado 1. Programa que procesa el documento de Word que contiene este artículo mediante
Aspose.Words
<<dotNetManía
Conclusiones
56
Figura 1. Componentes
de Aspose.BarCode
Aspose.Total for .NET ofrece un amplio
surtido de excelentes componentes, que
permiten potenciar de múltiples formas
el desarrollo de nuestras aplicaciones. Su
adquisición, conjuntamente o de manera independiente, constituye indudablemente una buena inversión, que se amortizará casi inmediatamente.
biblioteca.net
Programming Microsoft LINQ
Paolo Pialorsi y Marco Russo
Editorial: Microsoft Press
Páginas: 660
Publicado: mayo de 2008
ISBN: 978-0735624009
Idioma: inglés
Aunque no muy conocidos previamente como autores, Pialorsi y Russo (consultores, formadores y fundadores de www.DevLeap.com) han obtenido con esta obra el reconocimiento internacional y una excelente aceptación por parte de los lectores, si bien ya
se habían estrenado con alguna obra anterior centrada en XML y los servicios Web.
La atención al material incluido, el esmero en el detalle, la selección cuidadosa de los
contenidos y su interés para los lectores, los ejemplos (que funcionan, y realmente sirven de explicación a los contenidos del texto), hacen del texto un material más que recomendable. A eso hay que unir los nuevos diseños editoriales de Microsoft Press: más concisos y organizados, con mejor tipografía y organización del material expuesto y una edición más moderna. Todo ello se enmarca en la fase de renovación que se ha impuesto la
editorial y que, probablemente, el lector asiduo de MSDN Magazine ya ha podido comprobar en el último número de esa revista.
Professional C# 2008
Christian Nagel, Bill Evjen, Jay Glynn, Morgan Skinner y Karli Watson
Editorial: Wrox Press
Páginas: 1.782
Publicado: marzo de 2008
ISBN: 978-0470191378
Idioma: inglés
Nagel y compañía atacan de nuevo. Y muy bien. Se trata de una obra de consulta y
referencia. Todos los autores, excepto Evjen, ya son viejos conocidos, y varios de ellos lo
han sido de las primeras ediciones de esta obra para versiones previas del lenguaje, así
como habituales de la editorial. Y esa experiencia en el lenguaje y en el oficio de escribir
se aprecia en esta obra que cubre “todo lo que uno pueda necesitar hacer con el lenguaje C#”, según uno de sus lectores. No afirmaré yo tanto, pero lo cierto es que una larguísima parte de lo que es posible se encuentra aquí, debidamente organizado por contextos de trabajo y utilización.
novedades
Es una obra un tanto monumental, sin duda exhaustiva (la más extensa publicada hasta ahora sobre el tema; baste indicar que el número de capítulos es de 48, y todos con
bastante contenido), y que consigue lo que pretende: que el lector la vea inmediatamente como la referencia a consultar cuando se plantea la pregunta típica: ¿y cómo se haría
en C# 3.0…? Se trata, probablemente, de lo más completo publicado hasta la fecha sobre
este lenguaje (y nada cara, considerando lo anterior).
Silverlight 2.0 Bible
Brad Dayley. Editorial: Wiley. Páginas: 552. ISBN: 978-0470375006. Fecha de publicación:
octubre de 2008. Idioma: inglés.
Pro Silverlight 2 in C# 2008
Matthew MacDonald. Editorial: APress. Páginas: 400. ISBN: 978-1590599495. Fecha de
publicación: octubre de 2008. Idioma: inglés.
TEXTO: MARINO POSADAS
desván
Marino Posadas
<<dotNetManía
Tiempo de cambios en Microsoft
58
El verano invita a la reflexión.
Para la compañía de Redmond,
además, el inicio del verano es
también el inicio de un nuevo
período económico, que este
año está marcado inexorablemente por la retirada del CEO
y alma mater de la compañía, William Henry Gates III (el primero de la fila inferior de la foto, en 1978, el año de su fundación). Gates, atenderá un día a la semana asuntos relacionados con
la empresa, dedicando la mayor parte de su tiempo a la Fundación Bill y Melinda Gates, cuyas principales actividades son la
mejora de las condiciones de salud en zonas desfavorecidas (como
Mozambique, donde trabaja el doctor español Pedro Alonso, en
campañas de lucha contra la malaria y otras enfermedades), y la
expansión de las oportunidades educativas y el acceso a las tecnologías de la información.
Si, como dice el sabio, “se conoce el corazón del hombre por
lo que hace, y su sabiduría, por lo que dice”, Gates ha hecho mucho,
quizá más que nadie, por cambiar el panorama de lo que hoy día
es la informática y el acceso del gran público a este valioso recurso. Y ha sabido crear un imperio alrededor de esa idea. También
ha dicho y anticipado mucho sobre lo que tendría que ser la
informática del futuro, comenzando por su obra “The Road Ahead”, donde ya se refería como PC monedero a lo que hoy conocemos como PDA, subrayando la importancia de la informática
móvil, y donde se hablaba de interconectividad, librerías de software y muchas cosas que forman parte de la computación de hoy.
Sin embargo, los comienzos fueron complicados. La aparición del primer ordenador personal que
podría recibir tal nombre (el Altair
8800) dio la oportunidad a Gates y Paul
Allen de introducirse en el mercado.
Gates les llamó para decirles que tenían
una versión del lenguaje BASIC para
su nueva máquina (falso, ni siquiera
tenían la máquina). Allen consiguió crear un simulador del Altair
que funcionaba en el PDP-10 de Digital disponible en su escuela, mientras Gates se centraba en el código del intérprete de BASIC.
Ocho semanas más tarde, Allen introducía el código por primera vez en un Altair real. Si cualquier parte del código (del programa o del simulador) hubiera fallado, todo se habría ido al traste. Pero no fue así. El programa funcionó perfectamente la primera vez y se llamaba 4k BASIC en referencia a la memoria necesaria para su ejecución. A esto le siguió un contrato con los creadores de Altair (MITS). Un año más tarde, Gates se salía de la
Universidad de Harvard y fundaba Microsoft.
El reto que deja a sus sucesores es grande. Como decía Juan
Luis Cebrián (exdirector del diario “El País” y académico) en su
obra “La red”: “será necesario invertir de forma continuada, y hasta terca, en la formación de los ciudadanos acerca no solo de la
utilización de las nuevas tecnologías, sino en las consecuencias de
su implantación”.
noticias.noticias.noticias
documentos en la red
Warning: This Secret CSS Technique Will Surprise You!
Se trata de un artículo de Alex Walker
que explica cómo conseguir mediante
técnicas puras de Hojas de Estilo en
Cascada efectos de animación a partir de imágenes estáticas
secuenciales. Se trata nuevamente de una aportación interesante de este sitio (SitePoint), disponible en http://www.sitepoint.com/article/css-animation-technique.
“Regular Expressions: Now You Have Two
Problems” es una interesante reflexión para
programadores sobre las ventajas –y también
alguna desventaja– de la utilización de expresiones regulares. Jeff Atwood (Coding Horror) es el autor
de este artículo disponible en su sitio: http://www.codinghorror.com/blog/archives/001016.html, donde el lector encontrará
seguramente otros materiales de interés.
sitios del mes
Pixel Girl es un sitio dedicado a ofrecer
archivos (iconos, fondos de pantalla,
etc.), artículos y tutoriales sobre cómo
mejorar la experiencia visual del usuario de una plataforma o
programa mediante el uso de efectos atractivos e iconografía
adecuada. Todo ello original de la autora, cuya Web ha sido
incluida como una de las 50 mejores del año 2008, que premia
anualmente la revista Time. (http://www.pixelgirlpresents.com).
COLOURlovers. Otro sitio recomendado que parece más util
que nunca con las necesidades de diseño de aplicaciones que
se dan hoy en día es COLOURlovers, donde podemos encontrar todo lo que necesitemos a la hora de escoger un tema de
color para un sitio Web, una aplicación impactante o un juego. De especial utilidad para webmasters innovadores
(http://www.colourlovers.com).
utilidades del mes
Installed Codecs V 1.02 es una
pequeña utilidad gratuita que
permite comprobar cuáles son (y
de qué versión) los códec de audio y vídeo instalados
en nuestro sistema, así como los filtros DirectShow
activos. Se encuentra disponible en http://nirsoft.net/utils/
installed_codec.html.
Varun Kashyap, de www.ma
keuseof.com, propone un
conjunto de herramientas
gratuitas alternativas a la suite de creación de contenidos de Adobe. Desde programas de creación de documentos PDF o contenidos gráficos, hasta editores de
sonido. Para descargas y explicación de cada una, visitar http://www.makeuseof.com/tag/say-goodbye-to-adobecreative-suite.

Documentos relacionados