Reconocimiento de escritura manual

Transcripción

Reconocimiento de escritura manual
www.dotnetmania.com
nº 42 noviembre 2007 6,50 €
Visual Basic • C# • ASP.NET • ADO.NET • SQL Server • Windows System
dotNetManía
dedicada a los profesionales de la plataforma .NET
Tablet PC SDK (I)
Reconocimiento de escritura manual
Visualización de grandes conjuntos de datos en ASP.NET •
Aplicación de formato y selección manual de columnas sobre el
control DataGridView • Microsoft Operations Framework (y III)
Laboratorio
VistaDB v3.2
TodotNet@QA
Enlazándonos a LINQ
Comunidad .NET
MVP Open Day y TTT 2007
October .NET
Opinión
El viejo mito de la reusabilidad
editorial
dotNetManía
Dedicada a los profesionales de la plataforma .NET
Vol. III •Número 42 • Noviembre 2007
Precio: 6,50 €
[OT] ¡Fuerza y Honor!
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, José
Manuel Alarcón, Luis Miguel Blanco y Miguel
Katrib (Grupo Weboo)
Empresas Colaboradoras
Alhambra-Eidos
Krasis
Plain Concepts
Raona
Solid Quality Learning
Además colaboran en este número
Antonio Quirós, Daniel Seara, Eduardo
Quintás, Javier Roldán, Joan Llopart y José
Luis Montes.
Corresponsal para América Latina
Pablo Tilotta
Ilustraciones
Mascota (Clico): Yamil Hernández
Portada: Javier Roldán
Fotografía
Roberto Mariscal
Atención al suscriptor
Pilar Pérez
([email protected])
Edición, suscripciones y publicidad
.netalia
c/ Thomas Edison, 4, Bloque 1, P4-6
Parque empresarial Rivas Futura
28521 - Rivas Vaciamadrid (Madrid)
www.dotnetmania.com
Tf. (34) 91 666 74 77
Fax (34) 91 499 13 64
Bienvenido al número 42, de noviembre de
2007, de dotNetManía.
Espero que me permita este off topic, amable lector. Quisiera dedicar nuestro modesto
trabajo de este mes (con el permiso de mis
compañeros) a Juan Antonio Cebrián, que
lamentablemente nos dejó para siempre en un
maldito día de octubre. Juan Antonio, creador y director de “La rosa de los vientos”, un
programa de culto de la radio española, nos
acompañó en innumerables noches en los cierres de esta revista. Su manera de divulgar,
con rigurosidad pero con imaginación y amenidad, tanto en sus pasajes de la historia en la
radio como en sus libros, ha sido una inspiración para nosotros durante años. Lejos de las
grandes audiencias, sin saber por qué, consiguió miles y miles de fieles seguidores. Quien
le escuchó, le siguió.
Él, a quien tanto le gustaba contar historias
de héroes y batallas, tuvo siempre como lema su
famoso ¡fuerza y honor! Y cada noche se presentaba alegremente con un: “...éste que os acompaña como siempre, encantado y feliz como una
lombriz, vuestro amigo y compañero: Juan Antonio Cebrián...”. Hasta siempre “Cebri”.
Este mes tenemos muchas firmas nuevas y
les doy a todos la bienvenida; desde aquí les
invito a repetir cuando gusten. ¡Mira que tenemos buenos autores en nuestro país! A ver si
nuestros estudiantes demuestran el nivelazo
que tenemos en la nueva Imagine Cup, cuya
versión española ya ha comenzado y se ha anunciado la versión internacional, que este año será
en París. Hay mucha competencia, es cierto,
pero a ver si ganamos algo este año y estamos
aquí para contarlo.
En este número publicamos en portada
la primera parte, de un total de dos, del artículo “Tablet PC. Reconocimiento de escritura manual”, en el que Javier Roldán nos
muestra cómo integrar la funcionalidad de
reconocimiento de escritura manual con
nuestras aplicaciones .NET para Tablet PC,
que cada día adquiere mayor protagonismo
en el mercado de los dispositivos móviles,
precisamente por su capacidad para interactuar con el usuario mediante el uso de la
escritura manual. Por cierto, la portada también es obra de Javier.
Lleva razón Eduardo Quintás: en cualquier aplicación Web debemos tener en cuenta la escalabilidad a la hora de visualizar datos.
En su artículo “Visualización de grandes conjuntos de datos en ASP.NET", Eduardo nos
explica cómo un tráiler cargado de patatas puede tener el mismo rendimiento que un Ferrari descargado.
Luis Miguel Blanco vuelve con un práctico artículo dedicado al DataGridView, concretamente “Aplicación de formato y selección
manual de columnas sobre el control DataGridView”. El título lo dice todo.
Daniel Seara, Dani a partir de ahora,
amenaza con múltiples entregas sobre cómo
montarse un marco de trabajo y empieza con
el artículo “Herramientas genéricas para los
componentes”.
Por último, Joan Llopart y José Luis
Montes terminan su serie dedicada a MSF y
MOF con “Microsoft Operations Framework.
La vida del software después del desarrollo
según Microsoft.”
Pero aún hay más. Espero que le guste.
Imprime
Gráficas MARTE
Depósito Legal
M-3.075-2004
Paco Marín
<< dotNetManía
ISSN
1698-5451
3
sumario 42
El viejo mito de la reusabilidad
7-8
Siguiendo con los signos que nos indican si una empresa dedicada al desarrollo de software
presenta el suficiente grado de madurez, hoy nos toca analizar lo que este artículo denomina el
viejo mito de la reusabilidad. La denominación de viejo mito viene determinada por el hecho de
que construir software que no sólo sirva para un propósito sino también para otros posibles
futuros no deja de ser una de las aspiraciones de los equipos que desarrollan software desde que
lo hacían en lenguaje binario.
Visualización de grandes conjuntos de datos en ASP.NET
10-17
Estamos acostumbrados a leer ejemplos de ASP.NET con ADO.NET en los que nos enseñan
las características más espectaculares de .NET Framework con sencillez y elegancia, pero en
pocas ocasiones éstos hacen referencia al escalado o al uso real en una aplicación empresarial.
Aplicación de formato y selección manual de columnas sobre el control DataGridView
18-25
Cuando obtenemos información de un origen de datos para visualizarla en un control de
nuestro formulario, en gran número de ocasiones precisamos de un retoque o formateo de
dichos datos –fecha y numéricos habitualmente–, que nos permita pulirlos y adecentarlos un
poco antes de presentarlos a nuestros usuarios. En este artículo mostraremos las técnicas que a
tal efecto pone a nuestra disposición el control DataGridView, así como la posibilidad de
implementar manualmente la selección y ordenación de columnas, además de su
posicionamiento.
Tablet PC SDK (I). Reconocimiento de escritura manual
26-32
Los dispositivos conocidos como Tablet PC van adquiriendo día a día un mayor peso específico
en el mercado de los dispositivos móviles. Estos híbridos, a medio camino entre los clásicos
portátiles y las PDA, deben su éxito en gran medida a su capacidad para interactuar con el
usuario mediante el uso de la escritura manual, permitiendo una interacción hombre-máquina
mucho más intuitiva y natural.
Herramientas genéricas para los componentes
34-38
En este artículo describimos cómo pueden implementarse funcionalidades genéricas que sean
útiles a cualquier tipo de aplicación que debamos programar, considerando elementos de diseño
como su pertenencia a un componente específico o como simples rutinas. Dentro de estas
definiciones, aclararemos cómo es posible definir configuraciones específicas para cada
componente que diseñemos, así como una estructura genérica que permita llevar una bitácora
histórica de los errores dentro de las aplicaciones.
Microsoft Operations Framework (y III)
La vida del software después del desarrollo según Microsoft
40-44
Para cerrar esta serie de artículos de metodología, presentamos Microsoft Operations
Framework, la propuesta de Microsoft para planificar, desplegar y mantener soluciones de
servicio. Esta metodología complementa a Microsoft Solutions Framework, que hemos
presentado en los artículos anteriores, y juntas forman la propuesta de Microsoft para la
gestión del ciclo de vida de los proyectos de TI.
dnm.todotnet.qa
46-48
Enlazándonos a LINQ
dnm.laboratorio.net
50-53
VistaDB v3.2
dnm.comunidad.net
54-56
MVP Open Day y TTT 2007
October .NET Conference
dnm.biblioteca.net
57
3D Programming for Windows
Modelando procesos de negocio con Workflow Foundation
dnm.desvan
58
<<dotNetManía
noticias noticias noticias noticias
noticias
6
Arranca en España la 5ª edición de la competición universitaria “Imagine Cup” de Microsoft
Bajo el lema “Imagina un mundo donde la tecnología facilite un medioambiente sostenible”, más de 100
países se darán cita en la final internacional del concurso en París.
Microsoft presenta por quinto año consecutivo en nuestro país la competición
internacional para estudiantes universitarios Imagine Cup. Una iniciativa promovida desde el área de desarrollo de la
compañía para fomentar la creatividad e
innovación entre los estudiantes que
sienten un especial interés por las nuevas tecnologías.
En esta ocasión, y bajo el lema Imagina un mundo donde la tecnología facilite un medioambiente sostenible, los estudiantes deberán presentar proyectos tecnológicos aplicables a la vida real y que
cumplan unos requisitos técnicos básicos
como el diseño con .NET Framework.
El concurso se presenta en dos fases,
con materias y premios diferenciados
por la superación de cada una de ellas.
La última etapa requiere de la presentación del proyecto completo para su
evaluación ante un jurado formado por
profesionales del mundo académico,
representantes de empresas tecnológicas y personal de Microsoft, en la gran
final española del concurso que tendrá
lugar en el mes de abril.
Este año, Imagine Cup se compone de
9 categorías en total, de diferentes disciplinas tecnológicas: desarrollo de aplicaciones, desarrollo de aplicaciones embebidas, desarrollo de juegos, proyecto Hoshimi, desafío IT, algoritmos, cortometraje,
diseño de interfaces y fotografía.
Los alumnos interesados en formar
parte de este proyecto tienen hasta el 21
de marzo para inscribirse en la competición en http://www.desarrollaelfuturo.com.
Final internacional en París
El equipo ganador de Imagine Cup España
tendrá una plaza en la final internacional
Imagine Cup que tendrá lugar en París
(Francia) en el mes de julio de 2008. Los
estudiantes españoles tendrán que demostrar sus conocimientos y la originalidad y
consistencia de su proyecto frente al resto
de finalistas, para optar a un premio de
15.000 dólares.
Apoyo al ámbito universitario
Junto con Imagine Cup, Microsoft inauguró
una nueva edición del Microsoft University
Tour, las sesiones técnicas sobre tecnología
de desarrollo para estudiantes y profesores
universitarios que la compañía viene celebrando en nuestro país desde el año 2002. A
través de distintas ponencias, los asistentes
reciben práctica información de la mano de
profesionales de la compañía para mejorar
los proyectos que presenten a Imagine Cup.
Los interesados en conocer más sobre las distintas paradas del tour a lo largo de la geografía española pueden visitar la dirección
http://www.microsoft.es/estudiantes, donde
podrán encontrar información de productos,
formación, libros y material didáctico centrado en tecnología Microsoft.
El código fuente de las librerías de .NET Framework será liberado
Recientemente, Scott Guthrie, director general de la División de Desarrolladores de Microsoft, anunció
que el código fuente de la mayor parte de la librería de clases de .NET será puesto a disposición de los
desarrolladores con la próxima aparición de .NET Framework 3.5 y Visual Studio 2008.
Entre los espacios de nombres cuyo código podremos inspeccionar en un futuro ya
muy cercano están todos los que conforman la BCL (System, System.IO, System.Collections, System.Configuration, System.Threading, System.Net,
System.Security, System.Runtime, System.Text, entre otros), ASP.NET (System.Web), Windows Forms (System.Windows.Forms), ADO.NET (System.Data),
XML (System.Xml) y WPF (System.Windows). Más adelante se añadirán a esta
iniciativa nuevas librerías, como WCF,
Workflow Foundation y LINQ. Todo
ese código será liberado bajo la Licencia
de Referencia de Microsoft (MS-RL en
http://www.microsoft.com/resources/sharedsource/licensingbasics/referencelicense.mspx).
El código fuente podrá ser descargado a través de un instalador independiente, lo que hará posible utilizar cualquier
editor de texto para visualizarlo. Adicionalmente, Visual Studio 2008 ofrecerá
soporte de depuración integrado para ese
código fuente, de modo que será perfectamente factible saltar directamente desde el
código de nuestras aplicaciones al código
fuente de las librerías de .NET Framework.
Indudablemente, disponer del acceso
al código fuente de las librerías y de la integración de éste en el proceso de depuración será una gran ventaja para los desarrolladores .NET, que alcanzarán un grado de conocimiento mucho mayor sobre
la implementación de las librerías, lo que
les permitirá hacer un mejor uso de ellas y
así desarrollar mejores aplicaciones.
Más información en http://weblogs.
asp.net/scottgu (en castellano, en http://thinkingindotnet.wordpress.com. También puede
ver más información en http://www.hanselminutes.com/default.aspx?showid=101.
opinión
Antonio Quirós
El viejo mito de la reusabilidad
<< Pero hemos de plantearnos si realmente hoy estamos más
cerca que hace veinte años (y digo veinte porque yo soy tan viejo que
entonces ya estaba en esta profesión) de conseguir acercarnos al objetivo de la reusabilidad del código. Cuando comenzamos a trabajar
bajo el paradigma de la orientación a objetos pensábamos que ahí
estaba la clave, que la fabricación de componentes reutilizables sería
el factor que realmente impulsaría la reusabilidad. Han pasado entre
quince y veinte años desde que comenzaron a popularizarse las herramientas que trabajan bajo esta orientación; hoy tenemos además
SOA, como un paso más allá en ese mismo camino. Sin embargo,
no observo en general un gran salto cualitativo. Las piezas de nuestras soluciones siguen siendo tan poco operables en otras soluciones
como lo eran entonces.
Hoy tendemos a construir frameworks, tecnológicos o de negocio, que nos ayuden a empaquetar mejor la lógica de nuestros proyectos para que pueda ser más fácilmente reusable. Pero ¿no hacíamos ya esto hace veinte años en librerías de funciones o clases? Lo
hacemos ahora con tecnologías diferentes, pero en el fondo estamos
ante un proceso similar y donde no termino de encontrar el salto
cualitativo necesario.
Hoy hablamos del proceso de industrialización al que intentamos llevar el desarrollo de software. En esto se encuentra la posibilidad de contratar piezas de nuestro sistema a proveedores que se
encuentren en India o en otros lugares. Las factorías de software son
una pieza clave en este entramado, sean propias o subcontratadas
con terceros. Pero ¿hemos conseguido de verdad hacer del proceso
de fabricación de software algo industrializable? La administración
pública española, por ejemplo, sigue contratando la mayor parte de
sus servicios como asistencias técnicas in situ por parte de personal
cedido por los proveedores y que desarrolla sus proyectos en las oficinas públicas bajo el control de los coordinadores de las distintas
entidades. Si hubiéramos logrado de una forma correcta habilitar
procesos industriales para la fabricación de software, hoy habríamos
vencido a dicha tendencia y los ministerios y las comunidades autó-
<<dotNetManía
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.
Siguiendo con los signos
que nos indican si una
empresa dedicada al
desarrollo de software
presenta el suficiente
grado de madurez, hoy
nos toca analizar lo que
este artículo denomina
el viejo mito de la
reusabilidad. La
denominación de viejo
mito viene determinada
por el hecho de que
construir software que
no sólo sirva para un
propósito sino también
para otros posibles
futuros no deja de ser
una de las aspiraciones
de los equipos que
desarrollan software
desde que lo hacían en
lenguaje binario.
7
<< dnm.opinión
<<dotNetManía
…si queremos hacer una política activa dirigida a crear
código reusable, lo primero que hemos de hacer es
convencer a nuestros clientes de que la fabricación
industrial de software es posible…
8
nomas nos pedirían proyectos y productos en lugar de personas.
En la reutilización no solo tiene importancia el factor de componentización del
que estamos hablando, sino que también
lo tiene, y mucha, el relativo a la gestión
del conocimiento y a su difusión. Me refiero a que no solo podemos reutilizar piezas de software, sino también conocimientos. Hoy tenemos Internet con
potentes buscadores, blogs llenos de información, foros donde compartimos nuestros conocimientos, sistemas de comunicación de todo tipo que realmente han
conseguido acelerar esa faceta de la reutilización de la información. Hace veinte
años teníamos elementos parecidos, pero
aquí el salto cualitativo sí que ha sido enorme. Es cierto que existían BBS como
Compuserve y Fidonet, pero la universalización del mundo de las comunicaciones
ha hecho que hoy dispongamos de tal cantidad de material, con tantas facilidades
para su acceso y consulta que las situaciones realmente no son comparables, máxime si contrastamos los precios, las velocidades de conexión que logramos, las posibilidades inalámbricas, etc. Como casi
siempre, los grandes avances en el mundo de las soluciones de software vienen
más de la mano de lo que los sistemas y las
comunicaciones nos ofrecen que de aquello que los desarrolladores inventamos para
mejor hacer nuestro trabajo.
Ante todo este panorama, no queda
otra que ponerse a hacer un serio planteamiento empresarial donde hagamos de
la reusabilidad un concepto a perseguir de
forma constante. Las empresas de desarrollo de software nos quejamos permanentemente de que nuestros clientes cada
vez nos aprietan más en los precios mientras que los recursos técnicos que necesitamos para hacer los proyectos son cada
vez más caros y más difíciles de conseguir
y fidelizar. Es por ello que sólo podemos
perseguir la mejora a través de la eficiencia, y en este ámbito la posibilidad real de
la reusabilidad ensombrece cualquier otro
elemento digno de tenerse en cuenta.
Pero si queremos hacer una política
activa dirigida a crear código reusable, lo
primero que hemos de hacer es convencer a nuestros clientes de que la fabricación industrial de software es posible, de
que si dejan de contratarnos personas y
pasan a contratarnos proyectos o productos tendrán muchas ventajas operativas que
de otro modo se perderán. Podrán, por
ejemplo, exigirnos cumplimiento de plazos, estándares de calidad, niveles de servicio, etc. Pero en esto podría estar la base
para permitirnos a las empresas que desarrollamos software mejorar nuestra sistemática de producción, industrializar más
nuestro sistema, avanzar auténticamente
en el concepto de reusabilidad. Y todo ello
revertirá, sin duda, en mejoras importantes relativas a los plazos, la fiabilidad de lo
entregado y los precios.
No quiero terminar estas consideraciones de forma que parezcan contener sólo
un tono pesimista sobre los posibles avances en el ámbito de la reusabilidad. Creo
que realmente estamos en el camino
correcto, sólo que los pasos son lentos. Hoy
las empresas comenzamos a hablar de activos del ciclo de vida, comienzan a abundar
las herramientas para crear, gestionar y, por
tanto, reutilizar todos estos activos. Y el
avance respecto a la concepción clásica de
la reusabilidad viene determinado por el
hecho de que hoy consideramos reusable
no sólo un pedazo de código sino también
un requisito, un caso de uso o cualquier
otro componente, sea de código o de puro
reflejo documental del conocimiento. Pero,
además, no sólo tenemos herramientas,
sino que también existe una fuerte convicción por parte del sector empresarial del
software de que el único resquicio que tenemos para manejar mejor la eficiencia es la
industrialización del desarrollo a través de
la reutilización. A este impulso se unen los
hechos de tener herramientas adecuadas,
al modelo SOA hoy dominante en el mercado y al auge de las factorías de software,
no como un capricho sino como la plasmación de una realidad, la de que tenemos
que deslocalizar la fabricación de software, ya que donde surge la necesidad del
cliente no tenemos suficiente personal técnico que la satisfaga. Estoy convencido de
que todo esto hará imparable el proceso
hacia la reusabilidad y que las empresas
maduras serán aquellas que mejor sacarán
partido de dicho proceso.
En conclusión, y quizá dado lo disperso de la argumentación que he empleado, me gustaría resumir el hilo conductor de la misma, que en resumen es:
• En los últimos veinte años construyendo software, sólo hemos hecho
pequeños avances en el ámbito de la
reusabilidad y éstos están más centrados en cuanto a cómo gestionamos el
conocimiento necesario para construir
soluciones.
• La posibilidad de hacer código reusable es la base del avance hacia un proceso más industrializable en lo que a
la construcción de software se refiere.
• Un freno importante a este avance lo
ponen las empresas (lideradas en España
por la administración pública) y los sistemas de contratación con los que trabajan, más centrados en las personas que
se contratan que en los productos que
se pretenden conseguir y/o en la gestión de los proyectos necesarios para el
desarrollo de dichos productos.
• El avance en la noción de reusabilidad
impulsaría fuertemente la tendencia
hacia la construcción industrial de software y de ello se derivarían mejoras de
eficiencia, calidad y precio en los sistemas que se construyen.
asp.net
Eduardo Quintás
Visualización de grandes conjuntos
de datos en ASP.NET
Estamos acostumbrados a leer ejemplos de ASP.NET con ADO.NET en
los que nos enseñan las características más espectaculares de .NET Framework con sencillez y elegancia, pero en pocas ocasiones éstos hacen
referencia al escalado o al uso real en una aplicación empresarial.
Eduardo Quintás
es ingeniero en Informática y trabaja como técnico
superior en el área de
innovación tecnológica de
la Universidad de La
Coruña. Tiene amplia
experiencia en el desarrollo de soluciones Web,
especialmente aplicaciones ASP.NET y bases de
datos. Realiza labores de
development advisor para
Plain Concepts. Colabora
habitualmente en
geeks.ms.
Un ejemplo típico de lo dicho sucede cuando
usamos un GridView paginado y/o ordenado que
obtiene sus datos de un DataSet proveniente de
una base de datos. En los ejemplos de la documentación nos encontramos con soluciones que
solo funcionarían aceptablemente con un número pequeño de conjunto de datos; cuando el
número de tuplas crece, el tiempo de respuesta
crece exponencialmente.
Haciendo un símil, es como si nos diesen un
Ferrari rojo, una bala de plata, para transportar un
solo kilogramo de patatas a gran velocidad. Sin
embargo, en el mundo real hay que transportar
más patatas. Con 4 toneladas de tubérculo no nos
vale el Ferrari; necesitamos un tráiler, que suele
ser más lento aunque efectivo.
Este artículo enseña cómo podemos transportar 4, 8 ó 16 toneladas de patatas a casi la misma
velocidad que si fuera un solo saco en un Ferrari
ligeramente modificado. También sirve de introducción a las principales características disponibles para ASP.NET 2.0, como AJAX y la internacionalización de aplicaciones, y por otra parte hace
uso de las características de Visual Studio 2005 for
DB Professionals para la generación de conjuntos
de datos de prueba.
Supongamos el escenario en el que una aplicación web ASP.NET debe mostrar en un GridView un origen de datos extenso. Por ejemplo, una
lista de mensajes a un usuario, unas entradas de
un log, etc. Queremos que el usuario pueda acceder a los datos paginados y a la vez ordenarlos y
filtrarlos por algún criterio. El requisito principal es que el usuario tenga acceso a todo el conjunto de datos desde la rejilla del modo más rápido posible.
La solución más simple y la que encontramos
en la documentación y tutoriales de .NET pasa
por enlazar un origen de datos (ObjectDataSource,
SqlDataSource) a un GridView. Las características
de paginado y ordenación se pueden aplicar
automáticamente. Incluso el SqlDataSource genera la consulta SQL por ti. Mientras tengamos un
número reducido de usuarios concurrentes y pocos
datos todo irá como la seda, pero esta solución
fácil y rápida escala muy mal a poco que aumente la carga de peticiones o el número de datos a
devolver.
Con el enfoque anterior, el servidor tiene que
descargarse el DataSet completo desde el origen
de datos para, al final, mostrar solo una parte de
él. Os podéis imaginar que es algo tremendamente
ineficiente en cuanto crezca el número de datos o
de peticiones. Por otra parte, una solución basada en caché ASP.NET de servidor no parece apropiada por dos motivos:
1. DataSet extensos implican un gran uso de la
memoria física del servidor, la afinidad suele
ser por sesión de cliente.
2. Que los datos recuperados dependan de algún
parámetro intrínseco al cliente y su sesión
(por ejemplo, una colección de emails de un
usuario) y la caché, a nivel de aplicación, no
pueda aplicarse.
<< dnm.asp.net
En este artículo voy a proponeros
una solución elegante a la visualización
eficiente de grandes conjuntos de datos
que escala razonablemente bien. Vamos
a conseguir que el acceso a los datos
paginados a través de la rejilla dependa
lo mínimo posible del número de tuplas
de la tabla, intentando mantener los
tiempos de respuesta estables dentro de
la regla de los 7 segundos, que estipula que una página Web nunca debería
de tardar más de ese tiempo en dar una
respuesta al usuario.
Figura 1. Aspecto de la interfaz de la aplicación de ejemplo
Para nuestros propósitos, definiremos un
escenario bastante común. Supongamos
que necesitamos mostrar una bandeja de
entrada de mensajes, mostrando un icono de estado por mensaje así como la
fecha de envío, el remitente y el asunto.
Los datos mostrados podrán ordenarse
ascendente o descendentemente por cualquier columna. Por defecto, y por comodidad, los mensajes se mostrarán ordenados descendentemente por fecha de
envío. A la bandeja se le podrá aplicar un
filtro temporal sencillo, pudiendo ver solo
los mensajes recibidos en el día, en la
semana, en el mes o todos los mensajes.
También ha de ser posible la selección del
número de mensajes por página.
Diseño e implementación
Siguiendo los requisitos del escenario, la
aplicación ASP.NET “de juguete” tiene
el aspecto de la figura 1. Podéis descargar el proyecto VS2005 original desde
www.dotnetmania.com o desde mi blog [1].
En la capa de vista se han empleado
las tecnologías de Microsoft AJAX y las
características de Temas e Internacionalización (i18n) de ASP.NET 2.0. También se ha utilizado el control ObjectDataSource. Respecto a AJAX, se empleó
un UpdatePanel, que contiene los controles principales del ejemplo, incluido
el GridView. También se usa un ProgressPanel para aquellos postbacks que
duren más de medio segundo.
En cuanto los temas, se incluye el
tema WhiteCushion, que define todo los
atributos de visualización de la página
en el archivo de estilo WhiteCushion.css,
y el archivo de pieles WhiteCushion.Skin.
Destacamos el estilo asociado con el
UpdatePanel para presentar un mensaje
de espera, que se muestra centrado en
la página, mientras se espera la respuesta del servidor.
#UpdateProgressPanel
{
padding: 1em;
border: solid 1px gray;
position: Absolute;
top: 45%; left: 40%; width: 20%;
height: 2.5em;
background-color : White;
vertical-align : middle;
}
#UpdateProgressPanel
#ProgressPanelContainer
{
padding: 0.5em 1em 0.5em 4em;
background: white
url(‘../../images/progress_mini.gif’)
no-repeat left;
}
La página está disponible para la
cultura es-ES y, por defecto, para enUS. La cultura se elige en función de
las preferencias del navegador. En
IE7, las preferencias de idioma pueden editarse en “Herramientas” |
“Opciones de Internet” | “General”
| “Idiomas”. Se utiliza la localización
implícita, por lo que en los archivos
de recursos (Default.aspx.es.resx y
Default.aspx.resx) hay que incluir el atributo a localizar del control junto con la
clave de localización. También se hace
uso de controles <asp:Localized> para los
literales de la página. Por ejemplo:
Default.aspx
<%@ Page Language=”C#”
UICulture=”Auto” Culture=”Auto”
AutoEventWireup=”true”
Codebehind=”Default.aspx.cs”
Inherits=”GridViewSample._Default”
meta:resourcekey=”PageResource” %>
…
<asp:Localize ID=”l3” runat=”server”
meta:resourcekey=”LiteralNoData” />
Default.aspx.cs
Figura 2. Mensaje de espera
para el control AJAX UpdateProgress
GetLocalResourceObject(
“StatusInfoTimeElapsed”).ToString();
<<dotNetManía
Escenario
11
<< dnm.asp.net
Default.aspx.es.resx
<data name=”LiteralNoData.Text”
xml:space=”preserve”>
<value>No hay datos disponibles</value>
</data>
<data name=”StatusInfoTimeElapsed”
xml:space=”preserve”>
<value>Tiempo:
&lt;b&gt;{0}&lt;/b&gt;&lt;br/&gt;Filas
totales: &lt;b&gt;{1}&lt;/b&gt;</value>
</data>
Para mostrar los datos se utilizó un
control GridView; pueden observarse los
atributos de internacionalización implícita y el uso de DataImageUrlFormatString, en la primera columna, para mostrar un pequeño icono.
<asp:ObjectDataSource ID=”MessageDataSource” runat=”server”
TypeName=”GridViewSample.MessageBusinessLogic”
EnablePaging=”true” SelectMethod=”Select”
SortParameterName=”sortExpression”
MaximumRowsParameterName=”pageSize”
SelectCountMethod=”GetRowsCount”
StartRowIndexParameterName=”startRow”
OnSelected=”MessageDataSource_Selected”>
<SelectParameters>
<asp:ControlParameter PropertyName=”SelectedIndex” Name=”preFilter”
ControlID=”DDLFilter”/>
<asp:Parameter Name=”timeElapsed” />
</SelectParameters>
</asp:ObjectDataSource>
define una suscripción al evento OnSelected, que servirá para actualizar el control con las estadísticas de la consulta.
Los parámetros definidos en la sección SelectParameters permiten establecer una configuración extra para rea-
<asp:GridView ID=”MessageGridView” DataSourceID=”MessageDataSource” RunAt=”Server”
PageSize=”20” AutoGenerateColumns=”False” AllowPaging=”True”
AllowSorting=”True” BorderWidth=”0”>
<PagerSettings Mode=”NumericFirstLast”/>
<Columns>
<asp:ImageField DataImageUrlField=”Flag” AlternateText=”Flag”
DataImageUrlFormatString=”images/MessageStatus{0}.jpg” />
<asp:BoundField DataFormatString=”{0:d}” DataField=”Sent” ItemStyle-Wrap=”false”
SortExpression=”Sent” meta:resourcekey=”BoundFieldSent”/>
<asp:BoundField DataField=”FromAddress” SortExpression=”FromAddress”
meta:resourcekey=”BoundFieldFrom”/>
<asp:BoundField HtmlEncode=”False” DataField=”Subject” SortExpression=”Subject”
meta:resourcekey=”BoundFieldSubject” />
</Columns>
<EmptyDataTemplate>
<asp:Localize ID=”l3” runat=”server” meta:resourcekey=”LiteralNoData” />
</EmptyDataTemplate>
</asp:GridView>
<<dotNetManía
El GridView está enlazado a un
ObjectDataSource llamado MessageDataSource. Esta clase de DataSource, intro-
12
ducido en ASP.NET 2.0, permite crear fuentes de datos de objetos empresariales, incluidos DataSet, sin necesidad
de implementar una clase controladora
en la interfaz que ponga de acuerdo los
controles visuales con las fachadas de
nuestra lógica de negocio.
En el control ObjectDataSource se
puede indicar el método y el tipo que
resolverá la obtención de datos y el número de columnas devueltas, así como si
acepta paginado y ordenación. También
lizar la selección de datos; en nuestro
caso, un filtro temporal sobre los datos.
El parámetro timeElapsed es actualiza-
Figura 3. Mensaje de tiempo empleado y
filas sobre las que se ha paginado
do por el método que resuelve la sentencia SELECT y contiene información
estadística sobre la consulta.
Todos los parámetros definidos bajo
SelectParameters son de entrada y salida. Aunque existe un atributo (Direction) para indicar la dirección de los
parámetros, ASP.NET no lo usa y está
reservado para futuros usos, como indica la documentación.
El tipo GridViewSample.MessageBusinessLogic (figura 4) es la fachada de
nuestra lógica de negocio, y gracias a
ObjectDataSource podemos invocarla sin
necesidad de escribir una clase intermedia, controladora de interfaz.
En el control ObjectDataSource se
hace referencia a los métodos públicos
Select y GetRowsCount de MessageBusinessLogic. El primero realiza la selección
de los datos, mientras que el segundo
protected void MessageDataSource_Selected(object sender,
ObjectDataSourceStatusEventArgs e)
{
if (e.OutputParameters!=null && e.ReturnValue!=null)
StatusInfo.Text =
String.Format(GetLocalResourceObject(“StatusInfoTimeElapsed”).ToString(),
e.OutputParameters[“timeElapsed”],
e.ReturnValue.ToString());
}
<< dnm.asp.net
El método privado ConfigureDateFilter establece los valores de los pará-
devuelve el número de filas totales de la
consulta para poder calcular el número
de páginas. En este ejemplo, por motivos
de rendimiento, se aprovecha la conexión
en el método Select para obtener el
número de tuplas devueltas. También se
retrasa la apertura de la conexión lo máximo posible y se cierra en cuanto es posible, siguiendo así las buenas prácticas asociadas a los esquemas de acceso a datos
con concurrencia optimista.
El control ObjectDataSource ejecuta primero Select y luego GetRowsCount; para que timeElapsed tenga un
valor válido, es necesario devolverlo
en GetRowsCount y no en el método
Select . En caso de tener métodos
sobrecargados, ObjectDataSource siempre elegirá aquellos cuya firma coincide con los parámetros por defecto y
los definidos por el desarrollador en
SelectParameters.
public DataSet Select(string sortExpression, FilterByDate preFilter,
int startRow, int pageSize, string timeElapsed)
{
DateTime start = DateTime.Now;
DataSet ds = new DataSet();
using (SqlConnection conn = new SqlConnection(
ConfigurationManager.ConnectionStrings[“SampleDB”].ConnectionString))
{
string query = String.Format( SQL_GETMESSAGES,
(sortExpression == string.Empty) ?
SQL_DEFAULTORDER : sortExpression);
SqlCommand cmd = new SqlCommand(query, conn);
cmd.CommandTimeout = COMMAND_TIMEOUT;
cmd.Parameters.AddWithValue(“@FirstRow”, startRow);
cmd.Parameters.AddWithValue(“@LastRow”, startRow + pageSize);
ConfigureDateFilter(cmd, preFilter);
// GridView data query
conn.Open();
(new SqlDataAdapter(cmd)).Fill(ds);
// Counts total rows
cmd.CommandText = SQL_ROWCOUNT;
rowsCount = (int)cmd.ExecuteScalar();
conn.Close();
}
this.timeElapsed = (DateTime.Now - start).ToString();
return ds;
}
public int GetRowsCount(FilterByDate preFilter, out string timeElapsed)
{
timeElapsed = this.timeElapsed;
return rowsCount;
}
cmd.Parameters.Add(“@param_string”,
SqlDbType.NVarChar, 64);
cmd.Parameters[0].Value = “abc”;
Una explicación más detallada del
problema puede consultarse en el blog [3].
Pasemos a hablar de la consulta
SQL. Puesto que nuestro GridView debe
ser capaz de ordenar los datos por
columnas de forma ascendente y descendente, necesitamos pasarle ese parámetro a la consulta SQL. El problema
es que el orden no puede parametrizarse como se hace con los valores en una
cláusula WHERE, por lo que es necesario
construir una nueva consulta cuando se
cambia el orden. Este hecho genera un
plan de ejecución distinto para cada
orden. En nuestro caso, siguiendo un
escenario real, los datos son ordenados
por el criterio más relevante, concretamente por el atributo Sent (fecha de
envío) de forma descendente.
Primero se realiza un SELECT utilizando ROW_NUMBER() , una columna
especial de Transact SQL que devuelve el número secuencial de una fila de
una partición de un conjunto de resultados, pudiendo definir el orden de los
resultados. Sobre este conjunto de
resultados se aplica una restricción,
usando @FirstRow y @LastRow , para
devolver solo las tuplas que toca ver en
la página actual.
<<dotNetManía
Figura 4. Diagrama de la clase MessageBusinessLogic
metros de la consulta SQL relacionados con la fecha para obtener los mensajes de: hoy, esta semana, este mes y
todos los mensajes.
Hay que prestar especial atención
al uso del método para definir parámetros SQL AddWithValue. Un mal uso
de este método puede provocar que se
creen y se almacenen en la caché de
SQL Server varios planes de ejecución.
Podemos usar AddWithValue siempre
que el tipo de dato tenga un tamaño
preestablecido, como ocurre en los
casos de int, DateTime, etc. Pero nunca si es un string; en ese caso es necesario definir previamente el tamaño
con el búfer máximo esperado, por
ejemplo:
13
<< dnm.asp.net
private const string SQL_DEFAULTORDER = “Sent DESC”;
private const string SQL_ROWCOUNT =
@”SELECT COUNT(*) FROM dbo.Message WHERE Sent ” +
“BETWEEN @minDate AND @maxDate”;
private const string SQL_GETMESSAGES=
@“ WITH PagedMessage AS ( ” +
“
SELECT Id, Flag, Sent, FromAddress, Subject, ” +
“
ROW_NUMBER() OVER (ORDER BY {0}) AS RowNumber ” +
“
FROM dbo.Message WHERE Sent BETWEEN @minDate AND @maxDate) ”+
“ SELECT Id, Flag, Sent, FromAddress, Subject ” +
“ FROM PagedMessage ” +
“ WHERE RowNumber BETWEEN @FirstRow AND @LastRow ” +
“ ORDER BY RowNumber ASC ”;
Para aquellos que no estén muy
familiarizados con los gestores relacionales de base de datos, les recomiendo
que ejecuten cada consulta de su desarrollo en Microsoft SQL Server Management Studio (vale la versión Express,
que es gratuita para ciertos usos) y comprueben el plan de ejecución. Las operaciones principales deberían optimizarse como Index Seek o Clustered
Index Seek y evitarse los Sequential Scan
o Table Scan.
La edición de Visual Studio 2005 for
DB Professionals permite entre otras
cosas generar pruebas de unidad sobre
nuestro esquema y, lo más interesante
Figura 5. Plan de ejecución para la consulta principal
La consulta SQL_ROWCOUNT obtiene el
número de tuplas totales para el filtro
temporal actual. Este valor es necesario
para que el ObjectDataSource pueda indicarle al GridView cuál es el volumen de
paginado y mostrar adecuadamente los
enlaces a las páginas.
Para mostrar todos los mensajes,
abrimos el intervalo temporal entre la
fecha más pequeña y la más grande.
Nunca se han de utilizar las propiedades MaxDate y MinDate de System.DateTime con SQL Server, pues se lanzaría
una excepción ya que la fecha máxima
y mínima de SQL Server difieren de la
de las del tipo DateTime definidas en
.NET Framework. Es necesario utilizar SqlDateTime.MinDate y SqlDateTime.MaxDate.
<<dotNetManía
Esquema de datos del ejemplo
14
Para crear el esquema y generar los
datos de prueba se empleó Visual Studio 2005 Team Edition for DB Professionals. Para crear las consultas y revisar los planes de ejecución estimados y
reales, se utilizó Microsoft SQL Server
Management Studio Express.
private static void ConfigureDateFilter(SqlCommand cmd, FilterByDate filter)
...
case FilterByDate.Today:
cmd.Parameters.AddWithValue(“@mindate”, DateTime.Today);
cmd.Parameters.AddWithValue(“@maxdate”, DateTime.Today.AddDays(1));
break;
default:
cmd.Parameters.AddWithValue(“@mindate”, SqlDateTime.MinValue);
cmd.Parameters.AddWithValue(“@maxdate”, SqlDateTime.MaxValue);
break;
...
El esquema de datos del ejemplo se
presenta a continuación. Los índices son
obligatorios. Sin ellos, SQL Server no
podría realizar una planificación óptima de la consulta.
en nuestro caso, generar conjuntos de
datos de prueba. Creando un proyecto
para SQL Server 2005 se puede crear
en la carpeta del proyecto Data Generation Plans un plan de generación de
CREATE TABLE dbo.Message
(
Id INT IDENTITY PRIMARY KEY,
Flag INT NOT NULL,
Sent DATETIME NOT NULL,
FromAddress VARCHAR(64) NOT NULL,
Subject VARCHAR(128) NOT NULL,
);
CREATE INDEX idxFromAddress ON dbo.Message ( FromAddress ASC);
CREATE INDEX idxSent ON dbo.Message ( Sent DESC);
CREATE INDEX idxSubject ON dbo.Message ( Subject ASC);
<< dnm.asp.net
Figura 6. Configuración para un plan de generación
de datos en Visual Studio 2005 for DB Professionals
SATA, Windows XP SP2, Visual Studio 2005 for DB Professionals y SQL
Server Express 2005.
Se hizo una prueba con 8.388.608
tuplas y los tiempos se incrementaron
notablemente, pero ello no puede ser
achacable a la capa de acceso de datos sino
a la configuración, sin ningún tipo de optimización de SQL Server 2005 Express, y
la carga de mi sistema junto con sus limitaciones hardware; los índices ocupaban
algo más de 1 Gb, al igual que la tabla física, y el sistema estaba paginando la memoria virtual continuamente.
Respecto a la planificación de consultas, para el primer y segundo escenario se
genera un plan para cada una de las dos
consultas. En el tercer escenario se crea
un plan para una sola consulta. Los planes para las consultas dependientes del
ObjectDataSource son del tipo Prepared,
y la del SqlDataSource es Adhoc.
datos. En él podremos especificar el
número de tuplas que deseamos y también las plantillas de generación de valores. Para cada columna se puede indicar el rango de valores, la distribución
e incluso se puede aplicar una expresión
regular para generar los atributos alfanuméricos o enlazar a un conjunto de
datos reales.
Pruebas y resultados
Gráfica 1. Resultados para el ObjectDataSource correctamente paginado
frente al acceso clásico con un SqlDataSource. En el eje X,
los números de tuplas. En el eje Y, el tiempo en segundos
Gráfica 2. Resultados para el ObjectDataSource correctamente paginado
con y sin índices adecuados en la tabla. En el eje X,
los números de tuplas. En el eje Y, el tiempo en segundos.
<<dotNetManía
Para probar la aplicación de juguete se
crearon tres escenarios; para los dos primeros se aplica la técnica descrita basada en ObjectDataSource, uno con índices
adecuados en la tabla Message y en el otro
sin índices. En el tercer escenario se utiliza un GridView con un SqlDataSource,
el ejemplo más común de visualización
de datos con ASP.NET.
Para cada uno ellos se midieron
tiempos con 65.536, 262.144, 524.288
y 1.048.576 tuplas. Se tomaron dos
medidas a partir de la media de tres
muestras. La primera medida toma el
tiempo que le lleva mostrar páginas al
GridView del principio del conjunto de
datos. La segunda medida toma el tiempo en las páginas finales del GridView.
El entorno de software y hardware
de pruebas ha sido el siguiente: Pentium
IV a 3Ghz con 2GB de RAM, disco
15
<< dnm.asp.net
Del artículo [3] se desprende que las consultas
Adhoc, o de corto periodo de vida, se comportan mal
en cuanto a rendimiento cuando el número de usuarios es alto. Este hecho no ocurre con las consultas
parametrizadas de los dos primeros escenarios.
Hay que señalar que la consulta autogenerada por
el control SqlDataSource no utiliza nombres totalmente
calificados a la hora de referenciar a las tablas. El uso
de éstos mejoraría ligeramente el rendimiento, ya que
facilitaría la resolución de la localización de la tabla
en la base de datos.
nario 3 es exponencialmente más lento que el 1 ó el
2. Si bien para un conjunto pequeño de tuplas (65.536)
las diferencias no parecen grandes (algo menos de un
segundo), en cuanto crece en tamaño la tabla (1M de
tuplas) las diferencias se disparan a más de 100 segundos, un tiempo imposible de aceptar en un entorno
Web e incluso en una aplicación de escritorio.
Se ha encontrado una diferencia de tiempo para
los escenarios 1 y 2 dependiendo de si se consultan
las páginas iniciales de la tabla o las finales. Esta
diferencia de tiempo parece que crece linealmente
Consulta
Escenarios 1 y 2 basados en
ObjectDataSource y paginado
óptimo
Escenario 3, consulta autogenerada por un SqlDataSource
Tipo
(@FirstRow int,@LastRow
Compiled Plan
int,@mindate
datetime,@maxdate
datetime)SELECT COUNT(*) FROM
dbo.Message WHERE Sent
BETWEEN @minDate AND @maxDate
Prepared
(@FirstRow int,@LastRow
int,@mindate datetime,@maxdate
datetime) WITH PagedMessage AS
( SELECT Id, Flag, Sent,
FromAddress, Subject,
ROW_NUMBER() OVER (ORDER BY
Sent DESC) AS RowNumber FROM
dbo.Message WHERE Sent BETWEEN
@minDate AND @maxDate )
SELECT Id, Flag, Sent,
FromAddress, Subject FROM
PagedMessage WHERE RowNumber
BETWEEN @FirstRow AND @LastRow
ORDER BY RowNumber ASC
Compiled Plan
Prepared
SELECT * FROM [Message] ORDER
BY [Sent] DESC
Compiled Plan
Adhoc
Tabla 1. Planes de consulta generados para los escenarios 1 y 2 (las dos primeras filas) y para el escenario 3 (última fila)
Para todos aquellos interesados en consultar las
vistas del sistema relacionadas con el plan de ejecución de consultas y procedimientos almacenados, os
remito de nuevo a los post en el blog [2].
Para un vistazo rápido a la caché de planes de ejecución podéis ejecutar la siguiente consulta:
<<dotNetManía
select text, cacheobjtype, objtype, usecounts
from sys.dm_exec_cached_plans cp cross apply
sys.dm_exec_sql_text(plan_handle)
16
Discusión
Las gráficas del apartado anterior no dejan lugar a
dudas. En cuanto el conjunto de datos crece, el esce-
con el número de tuplas. Sin poder encontrar una
explicación definitiva, intuyo que probablemente
se debe a que SQL Server tarda en recorrer índices tan extensos (cerca de 512 Mb). Este retraso es
aceptable en el sentido de que es extraño que el
usuario acceda a las páginas finales. Porque podría
invertir el orden, que es una operación muy rápida, o filtrar más los datos. Otra opción es suprimir
el botón de acceso a la última página, algo bastante común cuando se necesita devolver gran cantidad de datos paginados, tal como hace el buscador
Google.
El motivo de incluir dos escenarios con ObjectDataSource, uno con índices y otro sin ellos, nos sirve
para demostrar la importancia del uso de índices en
un gestor de base de datos relacional. Solo hay que
<< dnm.asp.net
ver cómo se incrementa exponencialmente la diferencia de tiempos a medida que crece el número de
tuplas (gráfica 2) que, sin ser tan exagerada como la
del escenario 3, es un tiempo considerable (unos 12
segundos de diferencia para un 1M de tuplas).
Han de crearse los índices justos y necesarios,
dependiendo de la frecuencia y el tipo de consulta
que se hagan con los datos. Un número excesivo de
índices implica un mayor mantenimiento de los mismos por parte de SQL Server, ralentizando las operaciones de inserción, modificación y borrado de
tuplas.
Respecto al diseño de los escenarios 1 y 2, el GridView es alimentado por un ObjectDataSource que tiene un modo elegante de comunicarse con una fachada de la lógica de negocio. La interfaz de usuario que
controla el paginado y el orden de los datos mostrados está ya implementada y podemos centrarnos en
lo importante, en la lógica de negocio. Siendo una
funcionalidad de las más productivas y útiles de
ASP.NET 2.0.
He utilizado objetos DataSet sin tipar. Los que me
conocen saben que soy amigo del modelo de lógica
de negocio basado en objetos empresariales. Sin
embargo, en este escenario, es necesario renunciar a
ellos y buscar la estructura de datos más equilibrada
y flexible que tenemos en .NET para que la lógica de
negocio pueda enviar rápidamente datos a la capa de
vista. Esta idea está reflejada en el patrón de diseño
FastLane Reader [4]. Los DataSet de ADO.NET son
los contenedores ideales para implementar este patrón
de forma óptima y sin necesidad de instanciar una
gran cantidad de objetos, que si bien es una acción
rápida en .NET Framework en comparación con Java,
no es un tiempo desdeñable que evitamos en gran
medida utilizando DataSet. Tened presente que el uso
de FastLane Reader no excluye, sino más bien complementa, el tener un modelo conceptual de objetos
con sus correspondientes DAO y fachadas como forma normal de hacer operaciones CRUD con nuestros objetos empresariales.
Una lección aprendida, que debemos recordar y
aplicar del mundo real, es que si nuestro cliente tiene una operación común y recurrente con un tiempo
excesivo de respuesta, poco le va a importar que le
razonemos que tiene el código y diseño lógico más
puro que una gota de rocío; su insatisfacción será enor-
me. Generalmente en un software en producción el
requisito principal es el rendimiento por encima de
una arquitectura rígida de objetos empresariales. Equilibrar la arquitectura y el rendimiento no es fácil y tiene más arte que ingeniería. Con la debida disciplina
y a través del patrón FastLane Reader conseguimos
rendimiento en componentes de visualización críticos con alta tasa de accesos, aunque sintamos que sacrificamos una arquitectura homogénea. Está totalmente
justificado.
En cuanto a la base de datos, deciros que también
se planteó la posibilidad de utilizar procedimientos
almacenados, aprovechando que se compilan y su plan
de ejecución nunca se descarta de una caché. Pero en
nuestro caso tendríamos que construir uno por criterio de ordenación o bien crear una consulta dinámica con EXEC ‘query’ dentro de un solo procedimiento, perdiendo las ventajas de la compilación. Se creó
uno con orden por fecha descendente y arrojó los mismos tiempos que la consulta.
Una vez consultada la documentación en el blog
[2] de cómo SQL Server trata la caché de planes de
consulta y preguntando a MVP de SQL Server llegamos a la conclusión de que en este escenario, de
alto número de accesos, es muy probable que las consultas estén siempre en la caché de planes de ejecución y no compensa crear los procedimientos.
Una crítica a lo expuesto es que ha sido probado
con un alto número de tuplas pero con solo un usuario concurrente. Para tener unas conclusiones más
amplias se debería probar una ejecución masiva con
varios clientes lanzando peticiones concurrentes y
estudiar los tiempos de respuesta. También sería adecuado probarlo con un SQL Server 2005 Standard,
con el servidor de aplicaciones y de base de datos
separados.
Concluimos que, atendiendo a la regla que dice
que una página Web debería cargar en menos de 7
segundos para que el usuario no la abandone, solo el
escenario 1 serviría. De los presentados es el único
escalable según aumenta el número de tuplas. Con
pequeñas variantes, debería ser el enfoque utilizado
por desarrolladores que implementen aplicaciones que
necesiten visualizar grandes conjuntos de datos, especialmente en aplicaciones ASP.NET.
Ya tenéis un tráiler a velocidad de Ferrari. A disfrutarlo con muchas patatas.
Referencias
[ 1 ] Blog de Eduardo Quintás en geeks.ms: http://geeks.ms/blogs/quintas/.
programming practices”. SQL Programmability & API Development Team Blog, en
http://blogs.msdn.com/sqlprogrammability/archive/2007/01/13/6-0-best-programming-practices.aspx.
[ 3 ] Documento sobre el rendimiento de las consultas Adhoc, en http://support.microsoft.com/kb/243588.
[ 4 ] Patrón de diseño FastLane Reader [Java Blue Prints], en
FastLaneReader-detailed.html.
http://java.sun.com/blueprints/patterns/
<<dotNetManía
[ 2 ] “Best
17
plataforma.net
Luis Miguel Blanco
Aplicación de formato y selección
manual de columnas sobre el control
DataGridView
Cuando obtenemos información de un origen de datos para visualizarla en
un control de nuestro formulario, en gran número de ocasiones precisamos
de un retoque o formateo de dichos datos –fecha y numéricos habitualmente–, que nos permita pulirlos y adecentarlos un poco antes de presentarlos a nuestros usuarios. En este artículo mostraremos las técnicas que a
tal efecto pone a nuestra disposición el control DataGridView, así como la
posibilidad de implementar manualmente la selección y ordenación de columnas, además de su posicionamiento.
Preparando el escenario de trabajo
Supongamos que nos encontramos desarrollando
un formulario en el que necesitamos visualizar
dentro de un DataGridView la consulta SQL del listado 1.
SELECT ProductKey, SpanishProductName,
ListPrice, EndDate, Color
FROM DimProduct
WHERE ListPrice IS NOT NULL
Listado 1
Luis Miguel Blanco es
redactor de dotNetManía. Es consultor en
Alhambra-Eidos.Ha
escrito varios libros y
decenas de artículos
sobre la plataforma
.NET
(lalibreriadigital.com)
Pero como siempre ocurre en estos casos, los
requerimientos de la aplicación dictan que ciertos
campos de la tabla deberán mostrarse y formatearse de un modo distinto al estándar, con el agravante de que estas operaciones no podrán ser realizadas para todos los casos, sino en función de
ciertas condiciones, determinadas por los valores
de algunos campos de la tabla.
Es por ello que no podremos recurrir directamente a la solución que a priori sería la más rápida: la creación de estilos para las columnas nece-
sarias, configurando en dichos estilos las propiedades oportunas para alterar el aspecto que los
datos tienen por defecto.
Sí que emplearemos estilos para conseguir
nuestros propósitos, pero será dando un rodeo que
nos llevará por CellFormatting, un utilísimo y versátil evento perteneciente a DataGridView.
CellFormatting. Estilo y formato condicionales
Se trata de un evento que se produce para cada celda del control DataGridView que precise ser dibujada,
por lo que en el método manipulador de evento que
escribamos añadiremos el código encargado de realizar las comprobaciones oportunas, para de esta
manera aplicar cuando sea necesario los cambios de
estilo sobre aquellas columnas que lo requieran.
La información complementaria que necesitemos manejar, como puede ser el índice de columna o fila sobre la que se está produciendo el evento, la recibiremos en un parámetro de tipo DataGridViewCellFormattingEventArgs.
Una vez descrito brevemente este evento, pasemos a continuación a exponer las diferentes técnicas de formato que utilizaremos, en función del
<< dnm.plataforma.net
valor con el que debamos trabajar en
cada ocasión.
Aplicar formato en función
del valor de la celda actual
Figura 1. Formato aplicado a números y fechas
Formato indirecto
La siguiente técnica que mostraremos
se basa en aplicar un formato de forma
indirecta, o lo que es lo mismo, cuan-
private void dgvGrid_CellFormatting(object sender,
DataGridViewCellFormattingEventArgs e)
{
// si la celda a dibujar es ListPrice...
if (this.dgvGrid.Columns[e.ColumnIndex].Name == “ListPrice”)
{
// ...y se cumple esta condición
if ((decimal)e.Value > 1000)
{
// modificar propiedades del estilo de la celda
e.CellStyle.Alignment = DataGridViewContentAlignment.MiddleCenter;
e.CellStyle.BackColor = Color.DarkTurquoise;
e.CellStyle.ForeColor = Color.WhiteSmoke;
e.CellStyle.Font = new Font(“Comic Sans MS”, 12);
e.CellStyle.Format = “#,#.#0”;
}
}
// si la celda a dibujar es EndDate...
if (this.dgvGrid.Columns[e.ColumnIndex].Name == “EndDate”)
{
// ...y se cumple esta condición
if (e.Value != System.DBNull.Value && ((DateTime)e.Value).Year == 2002)
{
e.CellStyle.Font = new Font(“Comic Sans MS”, 8 ,
FontStyle.Italic | FontStyle.Bold););
e.CellStyle.Format = “dddd, dd-MMMM-yyyy”;
}
}
//....
}
Listado 2
do se vaya a dibujar la celda de la columna ProductKey, comprobamos el valor
de otra celda —la que se halla en la
columna EndDate—, y según el año que
tenga esa fecha, cambiamos o no el estilo de la celda de ProductKey. En este
caso, además, no accedemos directamente a los miembros de CellStyle,
sino que creamos previamente un objeto de estilo que finalmente asignamos
a esta propiedad, como vemos en el listado 3. Podemos comprobar el efecto
en la figura 2.
Emplearemos estilos,
pero será dando un rodeo
que nos llevará por el
utilísimo y versátil
evento CellFormatting
<<dotNetManía
Esta técnica consiste en comprobar la
columna a la cual pertenece la celda que
se va a dibujar, así como su valor, que
nos facilita la propiedad Value del objeto DataGridViewCellFormattingEventArgs. En el caso de que se cumpla una
determinada condición sobre el valor,
utilizaremos la propiedad CellStyle del
mencionado objeto para modificar el
aspecto habitual de la celda, resaltando
ciertas características de su estilo.
Este caso queda ilustrado en el listado 2, donde aplicamos esta operación
sobre los campos ListPrice y EndDate del
origen de datos. La figura 1 muestra la
apariencia resultante de esta operación.
19
<< dnm.plataforma.net
private void dgvGrid_CellFormatting(object sender,
DataGridViewCellFormattingEventArgs e)
{
//....
// si la celda a dibujar es ProductKey...
if (this.dgvGrid.Columns[e.ColumnIndex].Name == “ProductKey”)
{
DateTime dtEndDate;
// comprobar el valor del campo EndDate,
if (DateTime.TryParse(
this.dgvGrid.Rows[e.RowIndex].Cells[“EndDate”].Value.ToString(),
out dtEndDate))
{
// si se cumple esta condición
if (dtEndDate.Year == 2003)
{
// crear un estilo...
DataGridViewCellStyle styCelda = new DataGridViewCellStyle();
styCelda.BackColor = Color.PaleVioletRed;
styCelda.ForeColor = Color.LightYellow;
styCelda.Font = new Font(“Castellar”, 12, FontStyle.Bold);
// ...y asignarlo a la celda
e.CellStyle = styCelda;
}
}
}
//....
}
private void
dgvGrid_CellFormatting(
object sender,
DataGridViewCellFormattingEventArgs e)
{
//....
//si la celda a dibujar es Color...
if (this.dgvGrid.Columns[
e.ColumnIndex].Name==“Color”)
{
// cambiar el texto de la celda
switch (e.Value.ToString())
{
case “Black”:
e.Value = “Negro”;
break;
case “Blue”:
e.Value = “Azul”;
break;
case “Grey”:
e.Value = “Gris”;
break;
//....
}
}
//....
}
Listado 3
fuente de datos. Dado que la
propiedad DataGridViewCellFormattingEventArgs.Value es de
lectura y escritura, vamos a
tomar su valor original, volviéndolo a asignar ya traducido, como vemos en el listado 4.
La figura 3 muestra el resultado de esta traducción.
Figura 2. Modificar el aspecto de una celda en
base al valor de otra
<<dotNetManía
Cambiando el valor original
de la celda
20
Seguidamente ilustramos la capacidad
de cambiar el valor que originalmente debería mostrar la celda por uno
propio; en este caso sin alterar para
nada los estilos de la columna —aunque perfectamente podríamos haberlo hecho—. La columna a manipular
corresponderá al campo Color de la
Figura 3. Cambiando (traduciendo)
el valor de las celdas
Listado 4
Observando el resultado al ejecutar el formulario, nos percataremos de
que las cabeceras muestran el texto “a
sus anchas”. Quiero con esto decir que
no ocurre como en otras ocasiones,
donde el espacio dedicado a la cabecera de columna era extremadamente
justo, mostrándose incluso algunos
títulos recortados. ¿Cómo logramos
esta característica? Pues mediante la
utilización de dos líneas de código en
el evento Load del formulario: la primera para indicarle al control que ajuste automáticamente el tamaño de las
celdas con la propiedad AutoSizeColumnsMode; y la segunda en la propiedad Padding del estilo de la cabecera,
donde usando un objeto de dicho tipo,
conseguimos establecer un espacio
alrededor del título y los límites de la
celda de cabecera. Veámoslo en el listado 5.
<< dnm.plataforma.net
// establecer el tamaño automático para las celdas
this.dgvGrid.AutoSizeColumnsMode =
DataGridViewAutoSizeColumnsMode.DisplayedCells;
//....
// estilo para la cabecera
DataGridViewCellStyle styCabecera = new DataGridViewCellStyle();
//....
styCabecera.Padding = new Padding(10);
this.dgvGrid.ColumnHeadersDefaultCellStyle = styCabecera;
Listado 5
Listado 6
Cuando utilizamos un estilo aplicado a una columna que contiene valores
nulos, podemos conseguir, a través de la
propiedad NullValue del estilo, que estos
se muestren con un texto informativo.
Sin embargo, en la consulta SQL que
estamos usando para estos ejemplos, el
campo SpanishProductName contiene
registros con valores que no son nulos,
sino vacíos, por lo cual no tendría efecto el uso de la propiedad NullValue para
el estilo de dicha columna.
Nos encontramos pues ante un candidato ideal para tratar mediante el
evento CellFormatting. Todo lo que
tenemos que hacer es añadir el código
del listado 6 al manipulador de este
evento, para que muestre un literal en
el caso de que la celda de esta columna
no vaya a mostrar valores, y de paso
cambiamos su color de fondo.
Si queremos otorgar mayor vistosidad a
la información mostrada, podría interesarnos visualizar diferentes imágenes en
private void Form1_Load(object sender, EventArgs e)
{
//....
// columna para mostrar imágenes
DataGridViewImageColumn colDinero = new DataGridViewImageColumn();
colDinero.Name = “colDinero”;
this.dgvGrid.Columns.Insert(3, colDinero);
}
private void dgvGrid_CellFormatting(object sender,
DataGridViewCellFormattingEventArgs e)
{
//....
//....
// si la celda a dibujar corresponde a la columna de imágenes
// añadir a la celda la imagen en función del valor del campo ListPrice
if (this.dgvGrid.Columns[e.ColumnIndex].Name == “colDinero”)
{
string sImagen;
if ((decimal)this.dgvGrid.Rows[e.RowIndex].Cells[“ListPrice”].Value>1000)
{
sImagen = @”\euro.jpg”;
}
else
{
sImagen = @”\dolar.jpg”;
}
e.Value = new Bitmap(new Bitmap(Application.StartupPath + sImagen),
this.dgvGrid.Rows[e.RowIndex].Cells[“colDinero”].Size);
}
}
Listado 7
<<dotNetManía
// si la celda a dibujar es
// SpanishProductName...
if (this.dgvGrid.Columns[
e.ColumnIndex].Name ==
“SpanishProductName”)
{
if (e.Value.ToString() ==
string.Empty)
{
e.CellStyle.BackColor =
Color.Aqua;
e.Value =
“Producto sin descripción”;
}
}
Asignar una imagen
a la celda
las celdas de una columna, en función de
una condición dada por el valor de uno
de los campos, por ejemplo ListPrice.
Para manipular imágenes en un
DataGridView disponemos de la clase
DataGridViewImageColumn, que como su
nombre indica, permite crear columnas cuyo contenido sea dicho tipo de
dato. Por lo que en el evento Load procederemos a instanciar un objeto de esta
clase, insertándolo en la colección de
columnas de la cuadrícula, al lado de
ListPrice. Posteriormente, en el evento CellFormatting, cuando detectemos
que la celda de esta nueva columna va
a ser dibujada, le asignaremos una imagen creando un objeto Bitmap a partir
de un archivo gráfico, como vemos en
el listado 7. Podemos ver la apariencia
del control con esta nueva columna en
la figura 4.
21
<< dnm.plataforma.net
Figura 4. DataGridView con
columna de imagen
<<dotNetManía
Selección de celdas
por columna
22
A poco que hayamos utilizado el control DataGridView, nos habremos percatado de que, por defecto, permite
realizar una selección múltiple de
todas las celdas de una fila con un solo
clic en la cabecera de dicha fila. Sin
embargo, en algunas situaciones sería
deseable disponer de la capacidad de
seleccionar todas las celdas de una
columna al hacer clic en la cabecera
de las mismas.
El comportamiento de este control,
en lo que a selección múltiple de celdas
se refiere, se encuentra gobernado por
su propiedad SelectionMode, que corresponde al tipo enumerado DataGridViewSelectionMode, cuyos valores determinarán el modo en que las celdas son seleccionadas al hacer clic el usuario sobre
ellas. La tabla 1 muestra una descripción
de los miembros de esta enumeración.
Como reza el título de este apartado,
si queremos seleccionar todas las celdas
de una columna al hacer clic en su cabecera, asignaremos el valor DataGridViewSelectionMode.ColumnHeaderSelect a la
propiedad SelectionMode del control.
El efecto más inmediato que probablemente obtendremos será un tremendo error al intentar ejecutar la aplicación. Esto es debido al hecho de que
los objetos columna del control tienen
por defecto en su propiedad SortMode el
valor Automatic, lo cual es incompatible
con la capacidad de selección por
columna.
Propiedad
Descripción
CellSelect
Selección por celdas independientes. Permite seleccionar una o varias.
ColumnHeaderSelect
Se seleccionará la columna al completo al hacer clic
en su cabecera. También podemos seguir seleccionando
celdas independientes.
FullColumnSelect
Se seleccionará la columna al completo al hacer clic en
su cabecera, o bien en cualquier celda de esa columna.
FullRowSelect
Se seleccionará la fila al completo al hacer clic en la
cabecera de fila, o bien en cualquier celda de esa fila.
RowHeaderSelect
Se seleccionará la fila al completo al hacer clic en su
cabecera de fila. También podemos seguir seleccionando celdas independientes.
Tabla 1.Valores de la enumeración DataGridViewSelectionMode.
// cambiar el modo de ordenación de cada
// columna a manual
foreach (DataGridViewColumn oColumna in this.dgvGrid.Columns)
{
oColumna.SortMode=DataGridViewColumnSortMode.Programmatic;
}
// ahora ya se puede establecer la selección por columna
this.dgvGrid.SelectionMode = DataGridViewSelectionMode.ColumnHeaderSelect;
Listado 8
Figura 5. Selección por columna en el control
<< dnm.plataforma.net
Para solucionar este entuerto, bastará con recorrer la colección de columnas de la cuadrícula y cambiar el valor
de esta propiedad a Programmatic, lo
cual implicará que sea el código de la
aplicación quien se encargue de las operaciones de ordenación. Ver el listado
8. De este modo, como vemos en la
figura 5, ya será posible la selección por
columna.
Hemos conseguido pues, que al
hacer clic en la celda de cabecera de una
columna, esta quede seleccionada. Pero
hemos ganado una funcionalidad y perdido otra: la ordenación automática de
columnas, lo que quiere decir que nos
veremos obligados a implementar este
aspecto por código.
El punto más idóneo para realizar
estas operaciones será el evento ColumnHeaderMouseClick, producido al hacer
clic sobre la cabecera de una columna;
donde en primer lugar obtendremos,
gracias al parámetro DataGridViewMouseCellEventArgs y su propiedad ColumnIndex, la columna seleccionada para
ordenar.
// establecer un orden por defecto
ListSortDirection xDireccionOrden = ListSortDirection.Ascending;
//
//
//
if
{
calcular qué orden vamos a aplicar:
—————————————————si no hay columna ordenada actualmente se ordenará en ascendente
(colOrdenActual == null)
xDireccionOrden = ListSortDirection.Ascending;
}
else
{
// si hay columna ordenada actualmente:
// ——————————————————
// si se ha pulsado la misma columna que ya estaba ordenada
// se invierte el orden actual
if (colPulsada == colOrdenActual &&
this.dgvGrid.SortOrder == SortOrder.Ascending)
{
xDireccionOrden = ListSortDirection.Descending;
}
else
{
// en cualquier otro caso se establece orden ascendente
xDireccionOrden = ListSortDirection.Ascending;
}
}
// ordenar la columna
this.dgvGrid.Sort(colPulsada, xDireccionOrden);
// si había una columna previamente seleccionada
// y es distinta de la que acabamos de ordenar
if (colOrdenActual != null && colOrdenActual.Name != colPulsada.Name)
{
try
{
// quitar el estado de selección
// de la columna anterior
colOrdenActual.Selected = false;
}
catch
{ }
}
}
Listado 9
<<dotNetManía
Mediante el valor
Programmatic indicamos
que será el código de la
aplicación quien se
encargue de las
operaciones de
ordenación
private void dgvGrid_ColumnHeaderMouseClick(object sender,
DataGridViewCellMouseEventArgs e)
{
// obtener las columnas pulsada y actualmente ordenada
DataGridViewColumn colPulsada = this.dgvGrid.Columns[e.ColumnIndex];
DataGridViewColumn colOrdenActual = this.dgvGrid.SortedColumn;
23
<< dnm.plataforma.net
<<dotNetManía
Figura 6. Seleccionar una columna y ordenarla
24
La columna que estaba previamente ordenada, si es que había alguna, se
encontrará en la propiedad SortedColumn del control. Como siguiente paso,
determinaremos la dirección en la que
se van a ordenar los datos, pero sin
ordenarlos realmente aún. Lo que haremos será guardar en una variable de tipo
ListSortDirection esta dirección para el
orden.
A continuación ordenaremos la
columna correspondiente a la cabecera pulsada utilizando el método DataGridView.Sort, al que pasaremos como
parámetro la columna a ordenar y su
dirección.
Por último, en el caso de que existiese una columna anteriormente
ordenada, y por lo tanto seleccionada, quitaremos dicho estado de selección asignando false a su propiedad
Selected. Curiosamente, esta operación provocará una excepción, pero
controlándola mediante un bloque
try…catch, el estado de selección de
la columna en cuestión finalmente
quedará quitado. Podemos ver estas
operaciones en el listado 9, y en la
figura 6 volvemos a observar el control en ejecución, permitiéndonos ya
tanto seleccionar la columna como
ordenarla.
Posicionamiento y bloqueo de
columnas
Las clases DataGridView y DataGridViewColumn ofrecen al desarrollador un conjunto de propiedades destinadas a la
manipulación de las posiciones de las
columnas dentro del control.
Comenzaremos por la propiedad
DataGridView.AllowUserToOrderColumns, de tipo lógico. Asignándole el
valor true , conseguiremos que el
usuario al hacer clic y arrastrar en la
cabecera de una columna cambie la
posición de la misma con respecto a
las demás. Cuando añadimos un nuevo control de este tipo al formulario,
por defecto no podremos cambiar las
posiciones de sus columnas, ya que el
valor de esta propiedad es false.
Puede, sin embargo, que en lugar
de dejar al usuario el movimiento de
las posiciones de columnas, queramos
establecerlo nosotros por código. Para
ello contamos con la propiedad DataGridViewColumn.DisplayIndex, que permite asignar a la columna la posición
de visualización, independientemente
de la posición real que dicha columna
tenga en la colección de columnas del
control.
Por otro lado, también disponemos
de la capacidad de bloquear o congelar
una columna mediante su propiedad
Frozen, consiguiendo, al asignarle el
valor true, que dicha columna y las que
se encuentren a su izquierda se mantengan fijas cuando el usuario utilice la
barra de desplazamiento horizontal del
DataGridView.
Como ejemplo ilustrativo de estas
características, vamos a crear un formulario que contenga un TextBox por
cada una de las columnas de la rejilla,
y un botón que al ser pulsado asignará
nuevas posiciones a las columnas a
través de su propiedad DisplayIndex,
Figura 7. Cambio de posición visual y bloqueo de columnas.
<< dnm.plataforma.net
// cambiar las posiciones de visualización de las columnas
// utilizando su propiedad DisplayIndex
private void btnPosicionar_Click(object sender, EventArgs e)
{
// si hay una columna bloqueada, no se puede
// aplicar el cambio de posición con DisplayIndex
if (this.dgvGrid.Columns[1].Frozen)
{
MessageBox.Show(“Desbloquear columnas”);
}
else
{
// recorrer los controles, obtener el valor de los textbox
// y asignarlo como nueva posición de columna en el grid
foreach (Control oControl in this.Controls)
{
if (oControl.GetType() == typeof(TextBox))
{
TextBox txtTexto = oControl as TextBox;
this.dgvGrid.Columns[txtTexto.Name.Substring(3)].DisplayIndex =
int.Parse(txtTexto.Text) - 1;
}
}
}
}
// bloquear-desbloquear las dos primeras columnas,
// establecer un grosor para el borde de una columna
// y color para las líneas del control
private void chkBloquear_CheckedChanged(object sender, EventArgs e)
{
if (this.chkBloquear.Checked)
{
this.dgvGrid.Columns[1].Frozen = true;
this.dgvGrid.Columns[1].DividerWidth = 10;
this.dgvGrid.GridColor = Color.DarkOrchid;
}
else
{
this.dgvGrid.Columns[0].Frozen = false;
this.dgvGrid.Columns[1].Frozen = false;
this.dgvGrid.Columns[1].DividerWidth = 0;
this.dgvGrid.GridColor = SystemColors.ControlDark;
}
}
en base a los números que hayamos
introducido en los mencionados controles de edición.
También añadiremos un control
CheckBox al formulario, que al ser marcado, bloqueará las dos primeras
columnas del DataGridView. Para hacer
este efecto más palpable visualmente,
ampliaremos la anchura de la segunda columna utilizando su propiedad
DividerWidth. Finalmente, como mero
efecto estético, cambiaremos el color
de las líneas de la cuadrícula usando
la propiedad GridColor del control.
Todo ello lo vemos en el listado 10,
donde debemos aclarar que los nombres que hemos dado a los controles
TextBox del formulario están compuestos por el prefijo “ txt ” más el
nombre del campo; de ahí que al recorrer la colección de controles, cada vez
que encontremos un TextBox, extraigamos el nombre del campo aplicando el método Substring a la propiedad Name del TextBox.
Resulta interesante, una vez que
hemos bloqueado las columnas, hacer
un desplazamiento de las mismas, observando cómo la primera de ellas que no
está bloqueada se va ocultando al ser
desplazada a la izquierda; efecto que
vemos en la figura 7.
Listado 10
En el presente artículo hemos abordado diversas características del control
DataGridView relacionadas con su capacidad de presentación de datos. Estas
características nos permiten realizar
operaciones tales como la aplicación de
formato sobre los valores de las celdas,
selección de columnas y cambio en el
orden predeterminado en que las mismas se muestran al usuario, tanto por
código como debido a la acción del propio usuario sobre los elementos del control en tiempo de ejecución. Confiamos
en que todas estas posibilidades para
obtener un mayor beneficio visual en
nuestra cuadrícula de datos resulten
interesantes a los lectores que necesiten
implementar este tipo de mejoras en sus
aplicaciones.
<<dotNetManía
Conclusiones
25
plataforma.net
Javier Roldán
Tablet PC SDK (I)
Reconocimiento de escritura manual
Los dispositivos conocidos como Tablet PC van adquiriendo día a día un
mayor peso específico en el mercado de los dispositivos móviles. Estos híbridos, a medio camino entre los clásicos portátiles y las PDA, deben su éxito
en gran medida a su capacidad para interactuar con el usuario mediante el
uso de la escritura manual, permitiendo una interacción hombre-máquina
mucho más intuitiva y natural.
Fco. Javier Roldán
Huecas
es ingeniero superior
informático. Con una
experiencia laboral de
ocho años como profesional del sector de
las TI, en la actualidad
trabaja como ingeniero de sistemas y responsable de proyectos
en una importante
empresa industrial de
ámbito nacional.
Puede contactar con
él en:
[email protected]
Desde que el ser humano diseñó y construyó su primera herramienta, se vio en la inevitable necesidad
de tomar en consideración la mejor manera de interactuar con ella, y por tanto de sacarle el máximo
provecho. De este modo, observó que uniendo una
empuñadura de madera a la afilada piedra usada en
sus tareas diarias no sólo mejoraba la efectividad de
la herramienta, sino que además podía utilizarla en
nuevos campos de aplicación.
Poco a poco la complejidad de las herramientas aumentó, y con ella aparecieron de nuevas y
mucho más elaboradas formas de interacción. Así
los medios de transporte de tracción animal
requerían de un sencillo sistema para la puesta en
marcha, parada y control de la dirección del vehículo basada en la domesticación del animal. Sin
embargo, los sofisticados medios de transporte
actuales, como por ejemplo los aviones, requieren
de complejos sistemas de información y control.
Y es a partir del excesivo aumento de la complejidad de las herramientas utilizadas cuando la
necesidad de interactuar con las máquinas de
manera intuitiva y natural se convierte en una meta
para la comunidad científica. De este modo, la aparición de las computadoras como herramientas de
una gran complejidad, pero a su vez de una gran
capacidad de cálculo, alimentó desde sus comienzos las expectativas de una fluida interacción hombre-máquina, basada principalmente en la comunicación oral y escrita.
Es precisamente esta última, la comunicación
escrita, la que trataremos de abordar en el presente artículo. En concreto, veremos la manera de
integrar en nuestras aplicaciones .NET la funcionalidad de reconocimiento de escritura.
El entorno de desarrollo
Para empezar a programar aplicaciones con capacidades de reconocimiento de escritura manual,
deberemos tener instalados en nuestra máquina
de desarrollo los siguientes componentes:
1. Microsoft Windows XP Tablet PC Edition
Software Development Kit 1.7.
2. Microsoft Windows XP Tablet PC Edition
2005 Recognizer Pack.
Microsoft Windows XP Tablet PC
Edition Software Development Kit 1.7
Microsoft Windows XP Tablet PC Edition
Software Development Kit 1.7 es la última versión disponible del kit de desarrollo para la plataforma Tablet PC.
Este kit de desarrollo se compone de bibliotecas
administradas y de automatización que permiten el
desarrollo de aplicaciones con reconocimiento de
escritura manual y de voz, así como de desplazamiento mediante el uso del lápiz óptico.
<< dnm.plataforma.net
Recursos Web para la preparación del entorno de desarrollo
Descarga de los entornos de desarrollo Visual Studio 2005 Express Edition:
http://msdn.microsoft.com/vstudio/express/downloads
Preguntas frecuentes relacionadas con Visual Studio 2005 Express Edition:
http://msdn.microsoft.com/vstudio/express/support/faq
un mensaje de error aparecerá casi al
instante indicando que no existen reconocedores de texto manuscrito instalados y que necesitamos al menos uno de
ellos para ejecutar el ejemplo (figura 1).
Precisamente esto es lo que realizaremos a continuación.
Microsoft Windows XP Tablet PC Edition Software Development Kit 1.7:
Microsoft Windows XP Tablet PC Edition 2005 Recognizer Pack:
http://www.microsoft.com/downloads/details.aspx?FamilyID=080184dd-5e924464-b907-10762e9f918b&DisplayLang=en
Para instalar el kit de desarrollo,
procederemos en primer lugar a descargarlo desde la página Web habilitada al efecto (ver cuadro de recursos
Web). Una vez descargado, procederemos a su instalación en nuestro equipo, para lo que ejecutamos el fichero
Setup.exe . Siempre es interesante
seleccionar la instalación personalizada, ya que pinchando sobre cada una
de las opciones disponibles podremos
ver una pequeña descripción de cada
una de ellas.
Antes de continuar, y con el objeto
de entender un poco mejor el siguiente paso a realizar, accederemos a “Inicio” | “Programas” | “Microsoft Tablet
PC Plattform SDK” y ejecutaremos
“Samples and Source Code”, tras lo cual
lanzaremos el ejemplo “Ink Recognition” de la sección “Recognition”. Una
sencilla aplicación de ejemplo nos invita a escribir cualquier texto sobre un
panel titulado “Ink Here”, para luego
pulsar sobre el botón “Recognize Ink”.
Sin embargo, si ejecutamos tal acción,
Figura 1. Error al no haber reconocedores instalados
Microsoft Windows XP Tablet PC
Edition 2005 Recognizer Pack
Microsoft Windows XP Tablet PC
Edition 2005 Recognizer Pack es lo que
permitirá a nuestras aplicaciones reconocer el texto escrito de manera manual
mediante el uso de la tinta digital y convertirlo a texto estándar (entendiendo
como texto estándar aquel que el ordenador almacena como una cadena de
caracteres). Actualmente el reconocedor de escritura manual soporta los
siguientes idiomas: chino (tradicional y
simplificado), inglés (US y UK),
francés, alemán, italiano, japonés, coreano y por supuesto español.
Para instalarlo, procederemos una
vez más a descargarlo desde el sitio Web
de Microsoft (ver cuadro de recursos
Web). En esta ocasión, la instalación
personalizada nos dará la opción de elegir que idiomas queremos instalar. Para
seguir los ejemplos de este artículo con
instalar el idioma español es suficiente.
Si ahora ejecutamos de nuevo el
ejemplo “Ink Recognition”, veremos
cómo al pulsar sobre el botón “Recognize Ink” la aplicación ya no solo no nos
mostrará el error, sino que entenderá a
la perfección el texto introducido (figura 2). Aquí podemos probar a escribir
diferentes tipos de texto manuscrito y
comprobar la eficacia del reconocedor
incluso con textos cuyas letras se enlazan unas con otras en un solo trazo. Sin
embargo, este ejemplo no es de los
mejores en cuanto a eficacia de reconocimiento de escritura, que se puede
aumentar exponencialmente si de antemano sabemos el tipo de información
que va a ser escrita, mediante el uso de
diferentes técnicas que serán abordadas
en detalle en el próximo artículo.
<<dotNetManía
http://www.microsoft.com/downloads/details.aspx?FamilyId=B46D4B83-A82140BC-AA85-C9EE3D6E9699&displaylang=en
27
<< dnm.plataforma.net
<<dotNetManía
Antes de poder utilizar los
controles InkEdit e InkPicture,
debemos hacerlos disponibles
en el entorno de desarrollo.
Para ello, es conveniente crear
inicialmente una nueva categoría (que denominaremos
“Microsoft Ink”) en el Cuadro
de herramientas, y en ella colocaremos los componentes InkEdit e InkPicture (figura 3).
Figura 2. Reconocedor funcionando
El control InkEdit es el más
sencillo de todos los controles
existentes dentro del Tablet PC SDK.
El control InkEdit
Tan sencillo que permite capturar trazos de tinta digital y hacer su reconocimiento de manera automática sin que
Ha llegado el momento de empezar a
tengamos que escribir ni una sola línea
desarrollar nuestras propias aplicaciones
de código. Como era de esperar, es con
de reconocimiento de escritura manual.
diferencia el menos flexible de todos
Para ello, abrimos Microsoft Visual C#
ellos. Pero veamos cómo se utiliza.
2005 Express Edition y creamos un nueDesde el Cuadro de herramientas
vo proyecto mediante la opción de menú
pincharemos y arrastraremos el control
“File” | “New Project”. Al aparecer el
asistente de creación de nuevo proyecto,
InkEdit hasta situarlo sobre el formuseleccionamos “Windows Application” y
lario de la aplicación, como haríamos
le ponemos un nombre al proyecto (por
con cualquier control que quisiéramos
ejemplo, InkEditTest). Por defecto, Visual
situar sobre nuestro formulario. ObserStudio nos mostrará el formulario Form1
vamos que su aspecto es muy similar al
en su vista de diseño.
del clásico control RichTextBox, y de
28
Figura 3. Seleccionamos los controles InkEdit e InkPicture
para añadirlos al Cuadro de herramientas
hecho comparte muchas de sus propiedades. No podía ser de otro modo, ya
que la clase InkEdit deriva directamente de este último. Pero si observamos
las propiedades del control, veremos
que dispone de una categoría específica, denominada Ink; esta categoría es
la que precisamente nos va a permitir
determinar la manera en que va a comportarse el control InkEdit en cuanto a
lo que tinta digital y reconocimiento de
escritura manual se refiere. Las propiedades son las siguientes:
• Cursor. Determina la forma del cursor al pasar sobre el control.
• Factoid. Esta propiedad acepta una
cadena de caracteres que determina
el tipo de información que va a recoger el control y que aumenta considerablemente el ratio de aciertos de
reconocimiento.
• InkInsertMode. Existen dos modos:
• InsertAsText. En este modo el control InkEdit reconoce los trazos
escritos y los inserta como texto.
Es el modo por defecto.
• InsertAsInk. Utilizado en los casos
en los que deseemos que los trazos
escritos no sean reconocidos y sustituidos por su equivalente en texto (por ejemplo, si queremos capturar una firma). Suele usarse en
combinación con el método SaveFile. Este modo sólo funciona en
las versiones Tablet PC del sistema operativo Windows XP.
• InkMode. Ofrece tres valores:
• Disabled. No permite la introducción de tinta digital.
• Ink. Permite la introducción de tinta digital, únicamente para ser
reconocida como texto.
• InkAndGesture. Permite la introducción de tinta digital, tanto para ser
reconocida como texto, como para
ser reconocida como gestos. Los gestos son unos trazos especiales que
permiten entre otras cosas borrar un
carácter (backspace), borrar todo el
texto (scratch), introducir un salto de
línea (intro), etc.
<< dnm.plataforma.net
Inicialmente pondremos la propiedad UseMouseForInput a true y dejaremos el resto de las propiedades con su valor por defecto. Ejecutamos la
aplicación y probamos a escribir algún texto
mediante el uso del ratón o tableta digitalizadora,
si disponemos de ella (figura 4). Al cabo de dos
segundos, el texto será reconocido e insertado como
texto en el control InkEdit (figura 5). Y todo ello
sin una sólo línea de código. Puede probar a cambiar las diferentes propiedades del control para estudiar cómo se comporta.
Figura 4. Probando el control InkEdit
Figura 5. Reconocimiento automático
El control InkPicture
A diferencia del control InkEdit, el control InkPicture deriva de la clase Picture y está pensado
principalmente para mostrar imágenes sobre las que
poder escribir con tinta digital, pero en las que no
necesitemos reconocimiento de escritura manual.
Sus aplicaciones prácticas pasarían desde la captura de firmas de clientes hasta la señalización por parte de un perito de las zonas dañadas tras un siniestro directamente sobre la fotografía del vehículo.
El control InkPicture está pensado
principalmente para mostrar
imágenes sobre las que poder
escribir con tinta digital
¿Significa esto que no es posible reconocer los trazos introducidos sobre el control InkPicture y convertirlos en texto? No, simplemente significa que
no lo hará de manera automática, aunque ello es
perfectamente posible mediante programación. A
continuación veremos cómo, pero antes exploremos
las propiedades que diferencian al control InkPicture de su clase padre:
• AutoRedraw. Indica si la tinta digital trazada sobre
el control será repintada cuando el control sea
invalidado. Un control se invalida cuando deja de
ser visible; por ejemplo, al posicionar temporalmente una ventana sobre él. Su valor por defecto
es true.
• CollectionMode. Equivalente a la propiedad InkMode del control InkEdit, determina si se detectarán
los trazos como tinta, gestos o ambos.
• DynamicRendering. Valor lógico que indica si los trazos se muestran o no conforme son dibujados. Si
ponemos esta propiedad a false, ello equivaldría a
dibujar con “tinta invisible”.
• EditingMode. Especifica si el puntero creará trazos,
los borrará o seleccionará para su escalado y posicionamiento. Esta propiedad es de gran utilidad si
en vez de ser establecida en tiempo de diseño se
hace mediante programación.
• EraserMode. Esta propiedad determina si al borrar
se eliminarán trazos enteros o solamente la sección
del trazo por donde pase el puntero.
• EraserWidth. Grosor del puntero de borrado. Esta
propiedad es relevante únicamente en el caso de que
el valor de EraserMode sea PointErase.
• InkEnabled. Indica si se encuentra activa la recolección de trazos. En caso de estar desactivada, no
podremos pintar sobre el control.
<<dotNetManía
• RecoTimeout. Es el tiempo que nuestro control tarda desde que ha recibido el último trazo de tinta digital hasta que intenta realizar el reconocimiento.
• UseMouseForInput. Si queremos usar nuestro ratón
como si de un puntero de Tablet PC se tratase y
por tanto probar nuestra aplicación, deberemos
poner esta propiedad a true.
29
<< dnm.plataforma.net
using
using
using
using
using
using
using
using
System;
System.Collections.Generic;
System.ComponentModel;
System.Data;
System.Drawing;
System.Text;
System.Windows.Forms;
Microsoft.Ink;
namespace InkPictureTest
{
public partial class FormInkPicture : Form
{
public FormInkPicture()
{
InitializeComponent();
}
Figura 6. Potencia del modo Select
private void buttonEscribir_Click(object sender, EventArgs e)
{
this.inkPictureTest.EditingMode = InkOverlayEditingMode.Ink;
}
private void buttonBorrarTrazo_Click(object sender, EventArgs e)
{
this.inkPictureTest.EraserMode = InkOverlayEraserMode.StrokeErase;
this.inkPictureTest.EditingMode = InkOverlayEditingMode.Delete;
}
private void buttonBorrarPunto_Click(object sender, EventArgs e)
{
this.inkPictureTest.EraserMode = InkOverlayEraserMode.PointErase;
this.inkPictureTest.EditingMode = InkOverlayEditingMode.Delete;
}
private void buttonSeleccionar_Click(object sender, EventArgs e)
{
this.inkPictureTest.EditingMode = InkOverlayEditingMode.Select;
}
}
}
<<dotNetManía
Listado 1. Entendiendo mejor algunas de las propiedades del control InkPicture
30
• MarginX. Esta propiedad establece un margen de
seguridad en el eje X sobre el que se podrá pintar
aún a pesar de estar fuera de los límites del control
InkPicture.
• MarginY. Equivalente a la propiedad MarginX pero
en el eje Y.
• SupportHighContrastInk. Especifica si la tinta será
representada en alto contraste cuando el sistema
se encuentre configurado en modo de contraste
alto, utilizado por personas con deficiencias visuales.
• SupportHighContrastSelectionUI. Especifica si el cuadro de manipulación de
trazos (EditingMode = Select) será representado en alto contraste cuando el sistema se encuentre configurado ese
modo.
Para entender mejor algunas de estas
propiedades, veremos un sencillo ejemplo en el que crearemos un simple editor
de trazos utilizando el control InkPicture. Para ello, arrastraremos dicho control
sobre un formulario en blanco y crearemos cuatro botones: uno para crear trazos, otro para seleccionarlos y modificar
su tamaño y posición, otro para borrar trazos enteros y otro para borrar segmentos
de trazo. El código correspondiente a este
ejemplo puede verse en el listado 1.
Al ejecutar la aplicación podemos
observar claramente las diferencias existentes entre los diferentes valores de EditingMode (figura 6) y EraserMode.
Reconociendo texto manuscrito con el control
InkPicture
Hasta aquí hemos visto brevemente la sencillez y
potencia del control InkPicture para crear trazos, seleccionarlos, modificarlos y borrarlos de diferentes modos.
Sin embargo, no hemos visto la manera de reconocer
los trazos introducidos como texto manuscrito, que es
al fin y al cabo el objetivo primordial de este artículo.
<< dnm.plataforma.net
Como comentaba al principio de la presente sección,
el control InkPicture no proporciona ningún método
para el reconocimiento automático de la escritura
manual, sin embargo con unas pocas líneas de código
es posible efectuar esta tarea de manera sencilla.
Para ello, añadiremos al formulario del ejemplo
anterior un botón, cuyo evento Click contendrá toda
la lógica del reconocimiento y un cuadro de texto donde mostraremos el resultado (podemos ver el resultado final en la figura 7).
private void buttonReconocimiento_Click(object sender,
EventArgs e)
{
Recognizers reconocedores = new Recognizers();
if (reconocedores.Count != 0)
{
Recognizer reconocedor =
reconocedores.GetDefaultRecognizer();
RecognizerContext contextoReconocedor =
reconocedor.CreateRecognizerContext();
RecognitionResult resultadoReconocedor;
RecognitionStatus estatusReconocimiento;
contextoReconocedor.Strokes = inkPictureTest.Ink.Strokes;
if (contextoReconocedor.Strokes.Count > 0)
{
resultadoReconocedor = contextoReconocedor.Recognize(
out estatusReconocimiento);
if (estatusReconocimiento==RecognitionStatus.NoError &&
resultadoReconocedor != null)
{
String resultado = resultadoReconocedor.TopString;
if (resultado != null && resultado.Length > 0)
{
this.textBoxTextoReconocido.Text = resultado;
}
}
Figura 7. Reconocimiento en InkPicture
A continuación analizaremos el código a añadir
asociado al nuevo botón de reconocimiento, y que
puede verse completo en el listado 2.
}
Listado 2. Código de reconocimiento de escritura
• En primer lugar, creamos una nueva instancia de
la clase Recognizers (reconocedores) que no es ni
más ni menos que el conjunto de reconocedores de
texto instalados.
• Una vez creada dicha instancia, comprobamos si
existe o no algún reconocedor instalado en el sistema; si no es así mostramos un mensaje y finalizamos la ejecución.
<<dotNetManía
El control InkPicture no reconoce
automáticamente los trazos
introducidos, pero ello es
perfectamente posible mediante
programación
}
}
else
{
MessageBox.Show(“No existen reconocedores instalados”);
}
31
<<dotNetManía
<< dnm.plataforma.net
32
• A continuación, obtenemos el reconocedor por
defecto mediante el uso del método GetDefaultRecognizer de la clase Recognizers, el cual puede aceptar un parámetro de tipo int que indica el código
identificador de idioma (LCID), o lo que es lo mismo, el idioma para el que estamos obteniendo el
reconocedor por defecto. En caso de que no se proporcione dicho parámetro, el idioma se determina
en función del Idioma predeterminado del dispositivo de entrada configurado a través del Panel de
control, Configuración regional y de idiomas (pestaña “Idiomas”, botón “Detalles”).
• Asimismo, creamos un contexto de reconocimiento (RecognizerContext), el cual permitirá en última
instancia el reconocimiento de escritura manual
que esperamos, y definimos un resultado de reconocimiento (RecognitionResult) que almacenará el
resultado del reconocimiento y un estatus de reconocimiento (RecognitionStatus) que guardará el
estado generado tras el reconocimiento.
• A través de la propiedad Ink.Strokes, obtenemos
los trazos realizados en el control InkPicture y los
asignamos a los trazos del contexto reconocedor.
Si existe por lo menos algún trazo, efectuamos el
reconocimiento mediante una llamada al método
Recognize del contexto y lo asignamos al resultado
de reconocimiento. El estatus de reconocimiento
se obtiene como parámetro por referencia.
• Por último, de todas las posibles alternativas de
reconocimiento almacenamos en una cadena de
caracteres la que el reconocedor ha clasificado como
las más probable (TopString), y la mostramos en el
cuadro de texto habilitado al efecto.
En la siguiente entrega
En este artículo hemos preparado el entorno de
desarrollo necesario para la programación de aplicaciones capaces de aceptar tinta digital para más tarde
reconocerla como escritura manuscrita. Así mismo,
hemos analizado y utilizado los principales elementos
que para este fin proporciona Microsoft Windows XP
Tablet PC Edition Software Development Kit.
Sin embargo, han quedado sin tratar muchos temas
interesantes relacionados con el reconocimiento de la
escritura manual mediante el uso de Microsoft Windows XP Tablet PC Edition SDK, como por ejemplo
la posibilidad de añadir características de tinta digital
a cualquier control que decidamos mediante el uso de
la clase InkOverlay, la mejora del reconocimiento
mediante el uso de Factoids (cadenas predefinidas que
proporcionan al reconocedor información sobre el
tipo de texto a reconocer) y WordLists (listas predefinidas de palabras), o por ejemplo el uso de Gestures
(gestos especiales no reconocidos como palabras y que
permiten la ejecución de acciones a medida).
Trataremos todos estos temas en detalle en la próxima entrega; espero no nos falléis y veros de nuevo
el mes que viene.
plataforma.net
Daniel Seara
Herramientas genéricas
para los componentes
En este artículo describimos cómo pueden implementarse funcionalidades genéricas que
sean útiles a cualquier tipo de aplicación que debamos programar, considerando elementos de diseño como su pertenencia a un componente específico o como simples
rutinas. Dentro de estas definiciones, aclararemos cómo es posible definir configuraciones específicas para cada componente que diseñemos, así como una estructura genérica que permita llevar una bitácora histórica de los errores dentro de las aplicaciones.
Quienes me conocen saben que soy casi fanático de encapsular, montar componentes, generalizar (sin exagerar). Para que esto funcione
adecuadamente, hay que diseñar antes que
codificar, y hacerse de algunas “reglas guía”
que en muchos lugares se mencionan como
patrones (cuidado, si tienen ganas, algunos
comentarios sobre patrones están en mi blog en
http://blogs.solidq.com/ES/dseara/… digo, para
que se me entienda como pienso las cosas).
Ahora bien, hay algunas cosas que casi cualquier componente podría llegar a necesitar.
En algunos casos, podrían ser otro componente
aparte… en otros no.
Comencemos pues.
Definiendo la configuración
de nuestro componente
Daniel Seara es mentor
de Solid Quality Mentors
y director del área de
desarrollo .NET. Es MVP
desde 2003 y ponente
habitual de INETA y
Microsoft.
Uno de los elementos importantes a tener en cuenta respecto de los componentes es la definición
adecuada de sus configuraciones (en el número 41
hay un excelente artículo de mi amigo Guille acerca del tema de las configuraciones).
El mejor modo, y más ordenado, de hacer esto
para componentes, es crear una clase de configuración que pueda persistir la misma en el archivo
correspondiente de la aplicación o el sitio Web.
Además, dada la estructura jerárquica de los elementos de configuración, se podrá definir la con-
figuración, por ejemplo, directamente en machine.config, y de esa forma utilizar la misma para
múltiples aplicaciones.
Definimos entonces una clase que herede de
System.Configuration.ConfigurationSection.
[
NOTA
Para poder hacer esto, es necesario agregar una referencia al ensamblado System.Configuration.dll.
]
Imports System.Configuration
Public Class Configuration
Inherits System.Configuration.ConfigurationSection
Una vez heredada, debemos crear nuestro propio conjunto de propiedades.
Las clases de configuración exponen los distintos valores a través de una colección de tipo
específico: ConfigurationPropertyCollection. Para
poder definir las nuestras propias, debemos declarar un objeto de dicho tipo en nuestra clase y expo-
<< dnm.plataforma.net
Nótese que es posible, mediante
atributos, aplicar validadores a estas
propiedades.
Private Shared mProperties As ConfigurationPropertyCollection
Protected Overrides ReadOnly Property Properties() _
As ConfigurationPropertyCollection
Get
Return mProperties
End Get
End Property
A continuación, procederemos a
definir campos compartidos para cada
una de las propiedades.
Para que sean persistibles en el
archivo de configuración, estos campos
deben declararse como ConfigurationProperty. Además, la propia declaración
define el tipo de dato, el valor predeterminado y atributos que determinan,
por ejemplo, si la propiedad es obligatoria y demás. Por ejemplo:
Otro ejemplo de validador sería el
de expresiones regulares, como vemos
en el siguiente ejemplo:
Private Shared mEmail As New _
ConfigurationProperty( _
“Email”, GetType(String), _
“[email protected]”, _
ConfigurationPropertyOptions.None)
<RegexStringValidator(_
“^[a-zA-Z\.\-_]+@([a-zA-Z\.\-”+_
“_]+\.)+[a-zA-Z]{2,4}$”)>_
Private Shared mFileNameandPath As _
New ConfigurationProperty( _
“fileName”, GetType(String), _
“c:\SolidLog.log”, _
ConfigurationPropertyOptions.None)
Y luego lo exponemos como propiedad:
<StringValidator( _
InvalidCharacters:=_
” ~!@#$%^&*()[]{}/;’””|”, _
MinLength:=1,_
MaxLength:=600)> _
Public Property _
FileNameAndPath() As String
Get
Return CStr(Me(“fileName”))
End Get
Set(ByVal value As String)
mIsModified = True
Me(“fileName”) = value
End Set
End Property
Public Property Email() As String
Get
Return CStr(Me(“Email”))
End Get
Set(ByVal value As String)
Me(“Email”) = value
mIsModified = True
End Set
End Property
La idea entonces es tener una clase
de configuración para cada componente que la necesite, de manera que queden secciones en el archivo de configuración según necesitemos.
Teniendo todas las propiedades
definidas, debemos, en el constructor
de la clase, agregar las mismas a la propertyCollection. De esa forma, si existen propiedades configuradas en el
archivo de configuración de la aplicación, o en machine.config, se cargarán
automáticamente con sus valores. En
caso contrario, retornarán los valores
definidos como predeterminados.
Sub New()
MyBase.New()
mProperties = New _
ConfigurationPropertyCollection
mProperties.Add(mFileNameandPath)
mProperties.Add(mDestination)
mProperties.Add(mEmail)
mProperties.Add(mEventLogName)
End Sub
Definiendo los valores en el
archivo de configuración
Para que nuestro componente “encuentre” los valores de configuración, se
debe cambiar el archivo config según
lo siguiente:
Primero, se debe agregar la definición del sectionGroup y el sectionName
que utilizamos:
<configuration>
<configSections>
<sectionGroup name=”Solid”
type=”System.Configuration.
ConfigurationSectionGroup,
System.Configuration,
Version=2.0.0.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a” >
<section name=”ExceptionHandler”
type=”Solid.Tools.ExceptionHandler.
Configuration,
Solid.Tools.ExceptionHandler,
Version=3.0.0.0,
Culture=neutral,
PublicKeyToken=b856d8a5d9f2c2be”/>
</sectionGroup>
</configSections>
Una vez hecho esto, agregar el grupo y la sección correspondientes:
<Solid>
<ExceptionHandler
fileName=”c:\Solid.log”
Destination=”LogFile”
Email=”[email protected]”
EventLogName=”Solid Log” />
</Solid>
<<dotNetManía
nerlo sobrescribiendo un método de la
clase Configuration, como se ve en el
código siguiente:
35
<< dnm.plataforma.net
¿Cómo asegurarnos
de que estamos haciendo las
cosas bien?
Para tener siempre la definición correcta en el archivo de configuración, basta con crear una instancia de nuestra
clase de configuración, asignarle valores a todas sus propiedades, y guardarla en la configuración de una aplicación
Windows, la cual puede persistirse en
cualquier archivo de texto:
es casi imposible definir desde el inicio
todos los errores que podrían producirse, nada mejor que un objeto de error
genérico, del cual hereden todos y cada
uno de nuestros errores personalizados.
De esa forma, cualquier otro componente definirá sus propias clases de
excepción, heredando de ésta, y así se
conseguirá individualizar excepciones
específicas, y al mismo tiempo, mantener una bitácora centralizada de los
errores.
With System.Configuration.ConfigurationManager.OpenExeConfiguration( _
Configuration.ConfigurationUserLevel.None)
Dim conf As New Solid.Tools.Configuration
With conf
.Email = “[email protected]”
.FileNameAndPath = “c:\Solid.log”
.EventLogName = “Solid Log”
End With
Dim grp As New Configuration.ConfigurationSectionGroup()
.SectionGroups.Add(“Solid”, grp)
grp.Sections.Add(“Mi configuración”, conf)
.SaveAs(“c:\Solid.config”) ‘Se graba en C:\ para ubicarla fácilmente
Un componente de registro
de errores
Requerimientos
Estamos hablando entonces de crear únicamente una clase, base para todas
las excepciones, y que fuerce a que deba
heredarse.
La idea es tener una
clase de configuración para
cada componente que
la necesite
El objeto
Nuestro objeto, BaseException,
hereda obviamente de System.Exception,
para que pueda entenderse, por parte
del CLR, que se trata de excepciones.
Expone las mismas propiedades que
Exception, pero sobrescribe Message.
Esto nos permite personalizar el mensaje de cada nueva excepción que herede de la nuestra.
<<dotNetManía
Cada error producido en la aplicación ha
de quedar registrado de alguna forma.
• La implementación de dicho registro ha de ser centralizada, de modo
de desligar al programador de
implementar mecanismos en cada
parte del proceso de la aplicación.
• El registro debe ser configurable,
de modo tal que se pueda determinar el o los mecanismos a través de
los cuales dicho registro se realiza.
• Dicha configuración puede ser
modificable por el programador o
un administrador del área de sistemas de la compañía, pero no por el
usuario, para asegurar integridad.
36
Modelo
Es obvio que en este caso, la herencia es nuestra mejor herramienta. Como
Figura1
<< dnm.plataforma.net
Creamos en este caso, una clase de
configuración que exponga las propiedades que podemos ver en el listado 1.
<Flags()> Public Enum Destination As Integer
LogFile
EventViewer
Email
End Enum
Private Shared mDestination As New ConfigurationProperty( _
“Destination”, GetType(Destination), _
Destination.EventViewer.ToString, _
ConfigurationPropertyOptions.IsRequired)
Public Property Destination() As Destination
Get
Return CType(Me(“Destination”), Destination)
End Get
Set(ByVal value As Destination)
Me(“Destination”) = value
mIsModified = True
End Set
End Property
BaseException
Public MustInherit Class _
BaseException
Inherits System.Exception
Para poder manejar mejor el mensaje (por ejemplo, podríamos exponerlo en múltiples idiomas) lo definimos
sobrescribiendo la propiedad Message:
Private Shared mFileNameandPath As New ConfigurationProperty( _
“fileName”, GetType(String), _
“c:\SolidLog.log”, _
ConfigurationPropertyOptions.None)
Dim mMessage As String = “”
Public Overrides ReadOnly _
Property Message() As String
Get
If mMessage = “” Then
mMessage = MyBase.Message
End If
Return mMessage
End Get
End Property
<StringValidator( _
InvalidCharacters:=” ~!@#$%^&*()[]{}/;’””|”, _
MinLength:=1, MaxLength:=600)> _
Public Property FileNameAndPath() As String
Get
Return CStr(Me(“fileName”))
End Get
Set(ByVal value As String)
mIsModified = True
Me(“fileName”) = value
End Set
End Property
Private Shared mEventLogName As New ConfigurationProperty( _
“EventLogName”, GetType(String), _
“Solid Components”, _
ConfigurationPropertyOptions.None)
Debemos también definir las distintas formas de los constructores, para
poder hacer el registro en la bitácora
correspondiente.
Private Shared mEmail As New ConfigurationProperty( _
“Email”, GetType(String), _
“[email protected]”, _
ConfigurationPropertyOptions.None)
<RegexStringValidator(“^[a-zA-Z\.\-_]+@([a-zA-Z\.\-_]+\.)+[a-zA-Z]{2,4}$”)>_
Public Property Email() As String
Get
Return CStr(Me(“Email”))
End Get
Set(ByVal value As String)
Me(“Email”) = value
mIsModified = True
End Set
End Property
Listado 1
Nuestro objeto,
BaseException, hereda de
System.Exception, para que
pueda entenderse por
parte del CLR que se trata
de excepciones
<<dotNetManía
<StringValidator( _
InvalidCharacters:=” ~!@#$%^&*()[]{}/;’””|”, _
MinLength:=10, MaxLength:=40)> _
Public Property EventLogName() As String
Get
Return CStr(Me(“EventLogName”))
End Get
Set(ByVal value As String)
Me(“EventLogName”) = value
mIsModified = True
End Set
End Property
37
<< dnm.plataforma.net
El constructor sin argumentos lo
haremos privado, para que no pueda
utilizarse nunca desde afuera, ni
siquiera por parte de las clases que lo
hereden.
Private Sub New()
MyBase.New()
End Sub
Y definimos los otros constructores
como protegidos, para que sólo puedan
ser utilizados por quienes heredan esta
clase base.
Protected Sub New(ByVal message_
As String)
MyBase.New(message)
LogThis()
End Sub
Protected Sub New( _
ByVal message As String, _
ByVal innerException As _
Exception)
MyBase.New(message, _
innerException)
If message = “” Then message =_
innerException.Message
mMessage = message
LogThis()
End Sub
LogThis
Finalmente, definiremos el método
que procede al registro de la excepción,
LogThis.
[ ]
<<dotNetManía
NOTA
38
Hay temas de seguridad de
acceso al código que hay que
tener en cuenta, como que no
todos los usuarios pueden
escribir en la bitácora de
eventos, o en archivos físicos
en disco…. Eso ya es otra
cuestión.
Private Sub LogThis()
‘Obtiene la configuración
Dim conf As Configuration = _
CType( _
System.Configuration.ConfigurationManager.OpenExeConfiguration( _
System.Configuration.ConfigurationUserLevel.None).GetSectionGroup( _
“Solid”).Sections( _
“ExceptionHandler”), _
Configuration)
Dim logName As String = conf.EventLogName
Dim sfilename As String = conf.FileNameAndPath
Dim sEmail As String = conf.Email
‘Obtiene el nombre de la aplicación
Dim sApp As String = _
System.Reflection.Assembly.GetEntryAssembly.FullName.Split(“,”c)(0)
‘Arma el mensaje
Dim sError As String = String.Format(“{0},{1},{2}”, Me.Message, _
Me.HResult, Me.StackTrace)
‘Registra la excepción dependiendo de los valores de configuración
If CBool( _
conf.Destination Or CInt( _
Destination.EventViewer = Destination.EventViewer) _
) Then
‘Si no existe el log, lo crea
If Not System.Diagnostics.EventLog.Exists(logName) Then
System.Diagnostics.EventLog.CreateEventSource( _
logName, logName)
‘Es necesario esperar hasta que el log esté disponible
While Not System.Diagnostics.EventLog.Exists(logName)
Threading.Thread.Sleep(1000)
End While
End If
System.Diagnostics.EventLog.WriteEntry( _
logName, sError, EventLogEntryType.Error)
End If
If CBool( _
conf.Destination Or CInt( _
Destination.LogFile = Destination.LogFile) _
) Then
Dim fs As New System.IO.StreamWriter(sfilename, True)
fs.Write(Now)
fs.Write(“,”)
fs.Write(sApp)
fs.Write(“,”)
fs.WriteLine(sError)
fs.Close()
fs.Dispose()
End If
If CBool( _
conf.Destination Or CInt( _
Destination.Email = Destination.Email) _
) Then
Try
Dim sbody As New System.Text.StringBuilder(“<Table>”)
sbody.AppendFormat(“<tr><td>Message: </td><td>{0}</td></tr>”, _
Me.Message)
sbody.AppendFormat(_
“<tr><td>Stack Trace: </td><td>{0}</td></tr>”, _
Me.StackTrace)
sbody.Append(“</table>”)
With New System.Net.Mail.SmtpClient
Dim m As New System.Net.Mail.MailMessage( _
sEmail, _
sEmail, _
“ERROR in “ & sApp, _
sbody.ToString)
m.IsBodyHtml = True
.Send(m)
End With
Catch ex As Exception
End Try
End If
End Sub
Joan Llopart
José Luis Montes
metodologías
Microsoft Operations Framework (y III)
La vida del software después del desarrollo según Microsoft
Para cerrar esta serie de artículos de metodología, presentamos Microsoft
Operations Framework, la propuesta de Microsoft para planificar, desplegar
y mantener soluciones de servicio. Esta metodología complementa a Microsoft Solutions Framework, que hemos presentado en los artículos anteriores, y juntas forman la propuesta de Microsoft para la gestión del ciclo de
vida de los proyectos de TI.
Joan Llopart es Project Manager y arquitecto de Raona.
Joan es MCSE, MCDBA, MCT,
ITPro y MSF Certified.
Joséé Luis Montes es Software
Architect de Raona. José es
MCSD.Net y MSF Certified
Frecuentemente, en los departamentos de desarrollo, existe la idea que el proceso de “creación” de un
software finaliza en el momento que se realiza el último requerimiento del cliente y las pruebas sobre la
solución tienen un resultado satisfactorio en su entorno de trabajo. Por tanto, una vez se cumplen estos
requisitos se tiene la sensación que el aplicativo debe
ser implantado inmediatamente en el entorno real
del cliente. A veces es difícil de entender por qué una
vez el trabajo ha terminado y los resultados de verificación son plenamente satisfactorios se ha de esperar, por ejemplo, cuatro días, para verlo en el entorno real, teniendo incluso que realizar las mismas pruebas en un entorno previo de preproducción. Si realizamos este razonamiento desde el punto de vista de
la persona que ha implementado el software, parece
lógico pensar que se está perdiendo el tiempo y que
se están repitiendo pasos innecesarios. Es muy habitual escuchar en los equipos de desarrollo la frase:
¿Para qué necesitan cuatro días para implantar nuestro aplicativo si yo lo hago en diez minutos?
Para dar respuesta a esta pregunta deberemos
hacer el esfuerzo de ponernos el traje del equipo de
sistemas, y entonces veremos que no es tan descabellado este tiempo de espera. La idea de un buen departamento de sistemas es aquel que es proactivo, es decir,
un departamento que se prepara para reaccionar eficazmente ante una posible emergencia. Un departamento de sistemas que se pierde en el trabajo del día
a día, y que acaba invirtiendo la mayoría de su tiem-
po en reaccionar ante emergencias imprevistas, es un
departamento mal organizado, poco productivo, y
que puede acabar en un caos, provocando grandes
pérdidas a su cliente. Para evitar esta situación, es
necesaria una buena planificación de las tareas y una
buena organización del departamento. Pensemos
entonces en qué se ha de hacer con el software que
el equipo de desarrollo les proporciona: ¿implantarlo directamente en el entorno real del cliente? Parece claro que no. Lo más habitual es que este nuevo
software conviva con otros ya existentes en la empresa; ¿qué pasaría si la implantación de este nuevo software afectara al correcto funcionamiento de los existentes? Parece lógico un periodo de pruebas en un
entorno igual al real, que nos permita ver si todo funciona correctamente sin poner en peligro el resto de
sistemas. Por otro lado, parece necesario estudiar
cómo reaccionar ante paradas o errores del software,
y cómo minimizar las pérdidas del cliente. ¿Sigue
pareciendo mucho cuatro días? Parece claro que si se
quiere hacer un buen trabajo final, no.
Hemos visto la necesidad de organizar los departamentos de TI siguiendo alguna metodología. En
este artículo se mostrará cómo Microsoft Operations
Framework (MOF) ––la solución que propone
Microsoft– puede ayudarnos a mejorar la calidad de
los servicios que ofrecemos a nuestros clientes y, consecuentemente, a reducir esa sensación de descontrol subyacente en algunos departamentos de TI.
Asimismo, espero que ayude a responder a la pre-
<< dnm.metodologías
gunta: ¿Para qué necesitan cuatro días
para implantar nuestro aplicativo si yo lo
hago en diez minutos?
MSF y MOF, el ciclo de vida
de un proyecto de TI
Microsoft, basándose en su experiencia en
consultorías, en la realización de proyectos en clientes de diferentes tamaños, y en
el gran conocimiento de su tecnología,
presenta, para dar solución a los grandes
retos que afrontan las empresas de hoy en
día, un conjunto de propuestas para
diseñar, desarrollar, implementar, operar
y dar soluciones de forma eficiente.
La propuesta de Microsoft consiste en dos metodologías complementarías: Microsoft Solutions Framework
(MSF) y MOF, que si se integran adecuadamente proporcionan un conjunto de directrices para gestionar el ciclo
de vida de los proyectos de TI.
MSF es la metodología Microsoft para
el desarrollo de software y su posterior
implantación con éxito. MSF, mediante
un conjunto de principios, modelos, disciplinas, conceptos, directrices y prácti-
cas, intenta ayudar a los equipos
y organizaciones a obtener soluciones de éxito en sus clientes.
MSF define, mediante una metodología, cómo:
• Poner al mismo nivel los
objetivos del negocio y la
tecnología.
• Establecer claramente los
objetivos del proyecto, los
roles que intervienen y las responsabilidades de cada rol.
• Desarrollar iterativamente,
basándose en un sistema de
hitos intermedios.
• Gestionar el riesgo de manera proactiva; es decir, se ha de trabajar para
intentar evitar los riesgos y en obtener una solución antes que sucedan.
• Responder eficazmente a los cambios.
MOF consiste en un conjunto de
directrices sobre la manera de planificar, desplegar y mantener procesos operativos de TI para el soporte de soluciones de servicio. MOF tiene una
estructura flexible basada en:
• La experiencia en consultoría y trabajo en clientes.
Figura 2. El ciclo de vida de un proyecto
de TI con MSF y MOF
• La IT Infrastructure Library (ITIL).
• La ISO 15504 (también denominado SPICE), que proporciona un
enfoque normalizado para evaluar la
madurez de procesos de software.
Como se puede observar en la figura 2, la combinación de MSF y MOF
en el ciclo de vida de un proyecto de IT
intenta ayudar a los equipos de desarrollo y de operaciones para dar solución a los tres puntos clave de las necesidades del cliente:
• Entender las necesidades del cliente.
• Desarrollar y desplegar la solución
eficientemente, ocasionando la
menor perturbación posible en el
cliente.
• Ofrecer un buen servicio de gestión
de la solución una vez implantada.
El ciclo de vida de IT con MSF y
MOF sigue cuatro pasos básicos:
Figura1. MSF y MOF las soluciones Microsoft
1. Planificar la solución siguiendo las
directrices de MSF y MOF.
2. Desarrollar la solución siguiendo las
directrices de MSF.
3. Desplegar la solución siguiendo las
directrices de MOF y MSF.
4. Gestionar la solución siguiendo las
directrices de MOF.
En el ciclo de vida de los proyectos
de TI, MOF entra plenamente en juego
cuando termina MSF; es decir, una vez
<<dotNetManía
Microsoft Solutions Framework (MSF) y Microsoft Operations Framework (MOF) son metodologías independientes entre sí, pero complementarias, y juntas forman la propuesta de Microsoft para ofrecer una visión completa del ciclo de vida de las soluciones en el ámbito de los
sistemas de información.
MSF es la propuesta de Microsoft para el desarrollo de proyectos, mientras que MOF es
una metodología independiente para gestionar los servicios de TI.
Aunque queda fuera del ámbito de este artículo hablar de MSF, recomiendo la lectura de
la información que Microsoft presenta en la web http://www.microsoft.com/technet/solutionaccelerators/msf/default.mspx y los enlaces relacionados. De esta forma se podrá
entender mejor la relación entre ambas metodologías.
41
<< dnm.metodologías
que se desarrolla una solución de servicio
y ésta se despliega en el entorno real. Por
tanto, MOF se activa cuando aparece la
necesidad de mantener la solución en condiciones óptimas de funcionamiento.
Relación con ITIL
La Biblioteca de Infraestructuras de
Informática (ITIL) es un conjunto completo y coherente de recomendaciones
para la administración de servicios
informáticos. MOF es una adaptación
del estándar ITIL a la plataforma
Microsoft para la gestión de servicios de
TI basados en tecnologías Microsoft.
Si se detecta alguna carencia en MOF,
siempre se puede consultar ITIL para ver
cómo se aborda el problema en un nivel
más general. Para más información acerca de ITIL, puede consultar su Web oficial: http://www.itil.co.uk.
Entrando en MOF
Una vez vistos los orígenes de MOF y
su relación con MSF, solo nos queda dar
el paso de entrar a conocer MOF por
dentro. Creo que ya es hora de responder a la pregunta que da nombre a este
artículo: ¿qué es MOF?
Como ya he comentado anteriormente, MOF es un conjunto de recomendaciones, principios y modelos. Esta
guía proporciona a los equipos de operaciones un conjunto de herramientas
para realizar su trabajo de gestión de los
entornos de producción de una manera más eficiente, con mayor seguridad,
y reduciendo riesgos de fallos inesperados y críticos. Un punto que hay que
resaltar es que MOF es un modelo genérico, lo que significa que para implantarlo en una organización deberá ser
adaptado a las necesidades concretas de
la misma.
MOF se basa en tres conceptos fundamentales: el modelo de procesos, el
modelo de equipos y la gestión del riesgo.
<<dotNetManía
Modelo de procesos
42
Tal y como se puede observar en la
figura 3, el modelo de procesos de MOF
es cíclico y se divide en cuatro cua-
Figura 3. MOF Process Model
drantes (etapas): Changing, Operating,
Supporting y Optimizing. Asimismo, al
final de cada cuadrante se produce una
revisión o hito: Operations, SLA, Release Approved y Release Readiness; siendo las dos primeras de carácter periódico y las otras dos por cada versión final
que se realice.
Como ya he comentado anteriormente, el ciclo de MOF entra en juego
en el momento que finaliza el proceso
de desarrollo de una solución y se inicia su fase de despliegue en el entorno
del cliente; este punto corresponde al
cuadrante Changing. El paso inicial de
este cuadrante ha de ser la comunicación entre el departamento de desarrollo y el de explotación para acordar los
requisitos que garanticen el correcto
funcionamiento de la solución en el
entorno real. Este conjunto de requisitos puede producir cambios en los
entornos de trabajo; este punto se conoce como Change Management. Una vez
detectados los requisitos necesarios para
desplegar la solución, se pasa a la fase
de localizar los recursos necesarios para
lograr cumplir los requisitos marcados.
Este punto, conocido como Configuration Management, incluye un punto a
veces olvidado por muchos equipos de
trabajo: la documentación de los componentes del entorno y su interrelación.
El punto final de este cuadrante es la
gestión de versiones, ya que como
hemos comentado anteriormente este
modelo es cíclico, y por tanto nos podemos encontrar con la necesidad de realizar el ciclo para varias versiones de la
solución. Al final de este cuadrante es
donde se obtiene el permiso para el
arranque real del despliegue, concretamente en el hito Release Readiness
Review, y es cuando se ejecuta el despliegue en sí, proceso llamado Release
Management, donde además se hacen
las pruebas de despliegue y la validación
del prototipo final. Es en este momento cuando la solución está en el entorno real del cliente.
Una vez la solución está operativa en
el entorno real del cliente, es necesario
realizar un conjunto de tareas que permitan garantizar el correcto funcionamiento de la solución en todo momento. Estas
tareas pertenecen al cuadrante Operating.
Este conjunto de tareas está orientado a
hacer una ejecución más eficiente de las
tareas diarias. Este cuadrante se centra en
tareas como:
• El seguimiento del estado del servicio
mediante la monitorización del mismo (Service Monitoring & Control).
• El mantenimiento de los sistemas de
mensajería y la coordinación de los
equipos de TI (System Administration).
• Tareas de mantenimiento físico de la
red (Network Administration).
• Tareas de mantenimiento de Active
Directory (Directory Services Administration).
<< dnm.metodologías
Este cuadrante finaliza al conseguir
el hito Operations Review y consiste en
revisiones periódicas para revisar el trabajo hecho y toda la documentación
generada.
Aunque en el cuadrante anterior se
haya creado un conjunto de actividades
para mantener operativa la solución, en
el día a día surgen problemas ante los que
hay que reaccionar, ya que en un entorno crítico de producción se ha de minimizar al máximo el tiempo de respuesta
ante un error del sistema. La mayoría de
empresas cuenta con un departamento
Service Desk, el cual se encarga de recibir y solucionar la petición de cambio o
solución de problema, o redirigirla a la
persona adecuada para que lo haga. Otras
dos funciones que encontramos en este
cuadrante son Incident Management y
Problem Management. La primera función se encarga de dejar constancia de
las peticiones que se reciben, mientras
que la segunda haría referencia a cómo
gestionar la solución al problema basándose en la información que se ha proporcionado. Estas tres funciones pertenecen al cuadrante Supporting. Este cuadrante finaliza al conseguirse el hito SLA
Review, que consiste en reuniones periódicas entre clientes, proveedores y los
responsables del departamento de TI
para revisar el estado del servicio y estudiar posibles vías de mejora.
Una vez tenemos desplegada la
solución, hemos sentado las bases para
intentar que esté operativa siempre, y
en caso contrario hemos creado medidas para reaccionar lo más rápido posible y hemos estructurado un mecanismo de soporte ante peticiones de
cambio; solo queda un punto a tratar:
cómo optimizar el servicio. Este punto se trata en el cuadrante Optimizing,
el cual presenta un conjunto de procesos que buscan cómo obtener más
rendimiento, capacidad y disponibilidad con menos coste. Concretamente tenemos:
• Service Level Management, que mide
la relación entre el servicio prestado
y el acordado.
• Capacity Management, que observa
las tendencias de rendimiento y estima las futuras necesidades de recursos para tenerlas en cuenta en los presupuestos y en los SLA.
• Availability Management, que mide la
relación entre la disponibilidad de la
oferta de servicios respecto a su coste.
• Security Management, que define y
comunica todos los temas relacionados con la seguridad.
• Infrastructure Engineering, que consiste en un conjunto de estándares
para mejorar la interoperabilidad y
reducir el riesgo de fracaso en los despliegues de soluciones.
• Financial Management, que gestiona el presupuesto de TI.
• Workforce Management, que trata
de gestionar la disponibilidad de
recursos para intentar permitir que
el recurso humano necesario para
cumplir con los SLA siempre esté
disponible.
• Service Continuity Management, que
trata todo lo relacionado con cómo
debe actuar un departamento de TI
frente a errores críticos imprevistos.
En este punto acaba el ciclo de vida,
pero nos encontramos con diversas posibilidades:
1. Se acepta la versión actual, con lo
cual se para momentáneamente el
ciclo.
2. Se requieren cambios de desarrollo,
con lo cual entraría en juego MSF y
cuando acaba el desarrollo se vuelve
a iniciar MOF en el cuadrante Changing con una nueva versión.
Modelo de equipo
El Modelo de equipo que plantea
MOF y los clústeres de funciones asociadas aportan un conjunto de directrices para intentar asignar a las personas más capacitadas a las funciones
operativas y de esta forma crear equipos de trabajo más eficientes. El
modelo se basa en un conjunto de
roles, concretamente seis: Release,
Operations, Support, Partner, Infrastructure y Security.
En la figura 4 podemos ver los diferentes roles que se mencionan y los dife-
Figura 4. MOF Team Model
<<dotNetManía
• La confidencialidad, integridad y disponibilidad de los datos (Security
Administration).
• Todas las tareas referentes al almacenamiento de datos (Storage Management).
• Tareas de mantenimiento programadas que que intentan minimizar el
impacto sobre el entorno (Job Scheduling).
43
<< dnm.metodologías
rentes objetivos que tienen asignados.
Asimismo, cada rol está asignado a uno
o varios de los cuadrantes del Modelo de
Procesos; concretamente, Release y Operations están claramente vinculados a los
cuadrantes Changing y Operating, respectivamente; Support y Partner están
presentes en los cuadrantes Supporting
y Optimizing; el rol Infrastructure está
en el cuadrante Optimizing; y el de Security en Optimizing y Operating.
Un punto importante de este
modelo es el hecho que existe la posibilidad de que una persona participe
en diversos roles, pero no todas las
combinaciones son posibles, ya que
ciertas combinaciones podrían desestabilizar el correcto funcionamiento
del equipo. En la figura 5 podemos ver
una matriz donde se muestran las
combinaciones Posibles, No deseadas
y No recomendadas.
Figura 6. MOF y la gestión del riesgo
La segunda etapa consiste en analizar la lista de riesgos detectada en la
etapa anterior, midiéndose la probabilidad de que ocurra y el impacto que
supondría.
Figura 5. Matriz de combinación de roles
<<dotNetManía
Gestión del riesgo
44
En este apartado MOF describe el
proceso para identificar un riesgo y
cómo actuar ante él. En la figura 6 se
puede observar que este proceso consta de cinco etapas:
La primera etapa consiste en la
detección del riesgo. Pero para detectar un riesgo, primero es necesario saber
qué es. Un riesgo es el daño potencial
que puede surgir por un proceso presente o evento futuro. El resultado de
esta etapa es una lista de riesgos.
Una vez realizado el análisis de
impacto, se realiza la planificación de
acciones para intentar mitigar el impacto de que suceda el riesgo detectado.
Todos los riesgos detectados y documentados necesitan un seguimiento que
actualice los datos que se tengan documentados (Risk Assessment Document).
En la etapa de control se evalúa
cómo actuar ante los riesgos existentes.
Conclusiones
Cada día tiene más importancia una gestión eficiente del tiempo y de los recur-
sos, así como la reducción al máximo de
los errores críticos no controlados y del
tiempo de respuesta ante una eventualidad. Acabamos de ver MOF, la propuesta de Microsoft para lograr estos
propósitos, y aunque parezca que si se
siguen los pasos que ésta marca en el
trabajo diario perderemos el tiempo, es
todo lo contrario.
Es cierto que parece más rápido
desplegar una solución directamente
en el entorno real del cliente que pasar
por un periodo de prueba en un entorno de preproducción. También parece más sencillo esperar a que ocurra
un problema que invertir horas en prepararse ante un posible error futuro
que tal vez nunca suceda. Pero estoy
seguro que todos podemos recordar
algún momento de crisis en un departamento de TI en el cual se ha detectado un error crítico que ha parado
completamente al cliente, y que para
resolverlo se han tenido que dedicar
varios días sin dormir. Esto lo único
que aporta es una mala imagen al cliente y deja al departamento de TI como
un grupo de ingenieros desorganizados y que solo se implican cuando tienen un problema.
Aunque hoy en día cada vez existen menos departamentos de TI con
“poca organización”, se ha de seguir
trabajando hacia departamentos de TI
proactivos. Es ahí donde MOF entra
en juego, conjuntamente con MSF,
como una herramienta muy útil para
conseguirlo.
Cuando tomamos decisiones,
necesitamos información; esa
información nos ayuda a gestionar mejor los procesos de
nuestras organizaciones y a ser
más competitivos, dándonos
mejores posibilidades de supervivencia en el corto, medio y
largo plazo. Aproximadamente
el noventa por ciento de la
información que necesitamos
está dentro de nuestra propia
organización, pero por desgracia la dispersión de la información (múltiples fuentes de
datos, múltiples formatos, calidad del dato mostrado, etc.)
hace que solo el treinta por
ciento esté accesible en el formato y en el tiempo adecuado;
el resto son datos por tratar o
información no estructurada...
si no lo tienes claro...
GR
d
La revista de la
Gestión del Rendimiento
La revista para la Gestión del Rendimiento
www.gestiondelrendimiento.com
todonet@qa
[email protected]
Dino Esposito
Dino Esposito
es mentor de Solid
Quality Learning. Es
ponente habitual en
los eventos de la
industria a nivel
mundial.Visite su
blog en: http://weblogs.
asp.net/despos.
(todotNet.QA@
dotnetmania.com)
Enlazándonos a LINQ
El término LINQ proviene de Language Integrated Query, tecnología creada por Microsoft para solucionar la necesidad de las aplicaciones de acceder a datos desde un alto nivel de abstracción mediante un conjunto de herramientas poderoso pero sencillo. LINQ se creó para independizar las aplicaciones del motor de acceso a datos y sus opacas cadenas de conexión y comandos, reemplazándolos por sintaxis nativa incorporada a los lenguajes C# y VB .NET. De forma que estos compiladores
soportan en .NET Framework 3.5 una nueva sintaxis extendida (las expresiones de consulta) que
hace posible consultar un almacén de datos sin recurrir a una API específica como ADO.NET.
LINQ suministra un conjunto de métodos estándar (conocidos como operadores de consulta
estándar), que se corresponden con una API subyacente de cada tipo de almacén a consultar. Por
tanto, hay muchas modalidades de LINQ: concretamente, LINQ to Objects para colecciones .NET
en memoria, LINQ to DataSets para objetos data-
set de ADO.NET, LINQ to SQL para tablas relacionales de SQL Server y LINQ to XML para
documentos XML.
Este mes responderé a varias preguntas frecuentes en relación a LINQ y LINQ to SQL,
saltándome la más popular: “He oído hablar de
LINQ, ¿cómo puedo utilizarlo?”.
¿Cuál es la diferencia entre el uso de las nuevas palabras reservadas como from, select y where
y los métodos con nombres similares que se llaman sobre el mismo objeto al que se va a consultar? ¿Cuál es preferible?
Hay dos formas de expresar operaciones
LINQ. Puedes usar expresiones de consulta a
través de un conjunto de nuevas instrucciones
específicas del lenguaje, tales como las mencionadas en la pregunta (from, select, where, y
también orderby, group y join). Como alternativa pueden utilizarse los métodos del objeto de
consulta.
Lo primero a tener en cuenta es que las
expresiones de consulta son una especie de metáfora sintáctica para hacer más fácil la consulta.
Todas las expresiones de consulta que utilizan las
palabras reservadas se harán corresponder, en
tiempo de compilación, con llamadas a métodos.
Esto significa que las dos opciones son correctas y equivalentes desde el punto de vista funcional y de rendimiento. De forma que, al final,
utilizar una u otra es más bien una cuestión de
preferencia personal. Claramente, una expresión
que utilice una sintaxis libre del tipo from…select
es, normalmente, más legible y menos prolija que
una larga letanía de llamadas a métodos en cadena. Como ejemplo, consideremos la siguiente
expresión:
// Uso de expresiones LINQ
YourDataContext db = new
YourDataContext(connString);
var data = from c in db.Customers
where c.Country == “Spain”
select c;
Esta sentencia es totalmente equivalente a la
llamada a método que se muestra a continuación.
La conversión de la primera en la segunda es totalmente una labor del compilador, y las reglas de
conversión entre las palabras reservadas y los ope-
// Uso de llamadas a operadores de consulta estándar
var data = db.Customers.Where(
c => c.Country == “Spain”)
La palabra reservada var es nueva en .NET Framework 3.5 e indica la inferencia del tipo de las variables
locales. El tipo de la variable que sigue al operador se
determina en tiempo de compilación en función del valor
que se le asigna. ¿Pero, qué es db.Customers?
Aparte del hecho de que el ejemplo utiliza LINQ to
SQL, la expresión db.Customers puede ser considerada
como un contenedor para un objeto de consulta. Un tipo
.NET es consultable si implementa IEnumerable<T> o
una interfaz derivada, tal como IQueryable<T>. Array,
List, Dictionary y cualquier otro tipo de colección en
.NET Framework, incluyendo las clases de colección
personalizadas son inherentemente consultables, porque son enumerables. No se requiere ningún tipo de
transformación para hacer estos objetos consultables.
Por otra parte, un documento XML o un DataSet no
tipado no se pueden consultar de forma inmediata, y por
ello requieren un tratamiento especial. Para consultar
un DataSet no tipado desde LINQ, hay que invocar previamente el método AsEnumerable sobre el DataSet. De
la misma forma, hay que cargar un fichero XML mediante el método XDocument.Load. Una vez que esos métodos retornan, disponemos de objetos válidos que pueden ser usados como fuente para la consulta integrada.
¿Qué sucede con el mundo de las bases de datos?
Digamos antes que LINQ to SQL solo soporta –de
momento– SQL Server. Para permitir que LINQ trabaje sobre una base de datos, debemos crear una clase
heredera de DataContext; en Visual Studio 2008, el
diseñador O/R lo hace por nosotros (ver figura 1). La
clase contiene propiedades que representan a las tablas
físicas de la base de datos. El tipo de esas propiedades es
System.Data.Linq.Table<T>, que es obviamente un tipo
consultable. En el código anterior, la expresión db.Customers hace referencia a Table<Customer>, desde el que
se van a seleccionar los clientes residentes en España.
Cualquier objeto consultable dispone de un conjunto de métodos/operadores de consulta; por ejemplo, Where. En la comparación de las dos sentencias
funcionalmente idénticas, no puedo ignorar la extraña
expresión pasada como argumento al método Where.
El operador => indica una expresión lambda, que es
una expresión en línea que puede utilizarse siempre
que se espere la presencia de un delegado. El operador => se lee como “de forma que”, o “implica que”,
igual que en la jerga del análisis matemático. La parte izquierda de la expresión indica los parámetros de
entrada. A la derecha se sitúa la expresión o bloque
de sentencias a ejecutar. Las expresiones lambda se
utilizan en LINQ como una manera conveniente de
crear delegados. Será necesario crear expresiones lambda en LINQ principalmente si trabajamos con llamadas a métodos. Es improbable, pero no imposible,
que haya que usar estas expresiones si se utiliza la sintaxis del lenguaje.
Y, por último, una expresión de consulta que utilice
la sintaxis integrada es perfectamente equivalente a una
consulta expresada mediante métodos; lo contrario, sin
embargo, no es siempre cierto. Por ejemplo, no se puede expresar una consulta de tipo SELECT DISTINCT usando la sintaxis de C#. Del mismo modo, no puede seleccionarse un objeto dado sin recurrir a los métodos. Veamos cómo hacer una consulta SELECT DISTINCT:
var data = (from c in dataContext.Customers
select c.Country).Distinct();
Mediante la inclusión de la expresión from…select
entre paréntesis, disponemos de su resultado como
una secuencia (consultable) de elementos de un tipo
anónimo, sobre la que podremos llamar a cualquiera
de los operadores de consulta soportados.seleccionar
un objeto que cumpla una condición específica,
podríamos tener que especificar el criterio usando una
función lambda:
var data=(from c in dataContext.Customers
select c).First(x => x.Orders.Count()>10);
Figura 1: Diseñador de objetos relacionales en
Visual Studio 2008
¿Qué hace la consulta anterior? Selecciona el primer cliente que tiene más de 10 pedidos. ¿Qué es
mejor entre operadores y métodos? A mí me gusta la
inmediatez de los operadores, pero –al mismo tiempo– no me preocupa utilizar lambdas donde veo que
es necesario.
<<dotNetManía
radores de consulta están establecidas en las especificaciones del lenguaje.
[email protected] [email protected]
<< dnm.todonet@qa
47
<<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
48
He visto que LINQ me permite seleccionar un elemento en una posición particular en la
secuencia. Me gustaría seleccionar, pongamos, el undécimo registro de la tabla de clientes.
Cuando lo hago, la aplicación falla, con un mensaje indicando que estoy utilizando una
característica no soportada. Pero esto funciona estupendamente para DataSet y colecciones. Estoy utilizando Visual Studio 2008 Beta 2 y .NET Framework 3.5. ¿Es un bug?
No, no es un bug. Lo que indicas tiene sentido en un
escenario LINQ to SQL. No es que no haya forma de
lograr eso, pero no es nada extraño que una sentencia
falle por el hecho de que una característica no esté soportada. Me explico.
LINQ to SQL traduce la sintaxis de expresiones de
consulta en llamadas a los operadores estándar, que a
fin de cuentas generan comandos T-SQL. Sin embargo, los operadores de LINQ se definen para secuencias ordenadas de elementos. El lenguaje T-SQL (del
mismo modo que cualquier otro lenguaje basado en
SQL) funciona devolviendo un conjunto no ordenado
de valores. ¿Qué significa “no ordenado” aquí? No me
refiero al hecho de que puedas o no usar la cláusula
ORDER BY, sino a que no existe ninguna noción de orden
en la secuencia que obtienes de SQL Server. La identidad de un elemento en un conjunto de resultados
SQL se establece por los valores de sus campos, y no
por su posición en la secuencia. No es un problema de
LINQ o SQL Server: es uno de los pilares del lenguaje SQL.
A causa de esta diferencia sustancial entre colecciones
en memoria y conjuntos de resultados, ciertos operadores
estándar de LINQ no funcionan apropiadamente o simplemente no están soportados en LINQ to SQL. Por eso
es que operadores como Take o Skip pueden funcionar, pero
a costa de ejecutar comandos T-SQL más complejos. Otros
operadores, como TakeWhile, SkipWhile, Last, Reverse y,
especialmente, ElementAt, no se traducen a T-SQL y no
están soportados por LINQ to SQL.
En LINQ to SQL, para recuperar un registro
deberías especificar una cláusula where y localizarlo por
su clave primaria o por los valores de algunas de sus
columnas. Es la forma en que funcionan las bases de
datos relacionales, no es un tema de LINQ.
Dicho esto, alguien podría argumentar que SQL Server 2005 soporta el operador ROWCOUNT para establecer
algún orden en un conjunto no ordenado de datos. Hasta donde yo sé, LINQ to SQL no aprovecha esa característica, ni siquiera cuando la base de datos de consulta es SQL Server 2005. Pero esto es algo que podría
cambiar en el futuro.
¿Puede destacar la diferencia entre los métodos Select y SelectMany en LINQ? Ambos
devuelven una selección de registros y ambos pueden devolver cero, uno o muchos registros. ¿Así pues, cuál es la diferencia?
Como has dicho “brevemente”, trataré de resumir. Imagina un escenario donde tenemos una relación entre dos
tablas, digamos, Customers y Orders. La siguiente consulta utiliza el operador estándar Select y devuelve una secuencia de objetos con el conjunto de propiedades indicado.
var data = from c in dataContext.Customers
where c.Country == “Spain”
select new { c.CompanyName, c.Orders };
Los elementos de la secuencia resultante consisten de objetos cuyo tipo tiene una propiedad de cadena CompanyName y una secuencia de objetos Order. La
propiedad Orders, por supuesto, contendrá todos los
objetos Order correspondientes a los pedidos de cada
cliente dado; pero los datos devueltos no son tabulares y la mayoría de los controles enlazados (por ejemplo, el DataGrid), no podrán mostrarlos.
La misma consulta puede expresarse en forma
tabular, con alguna inevitable redundancia de datos,
mediante el uso del método SelectMany:
var data = (from c in dataContext.Customers
where c.Country == “Spain”
select c).SelectMany(c => c.Orders);
En este caso, el resultado es un conjunto de datos
“aplanado”.
Traducido al castellano por Marino Posadas
Octavio Hernández
Laboratorio.net
VistaDB v3.2
Este mes presentamos un asombroso motor de bases de datos relacionales,VistaDB 3.2, de VistaDB Software, desarrollado completamente en código manejado
y que ofrece un excelente rendimiento y un alto nivel de compatibilidad con SQL
Server 2005 en una insignificante huella de memoria (aprox. 750 KB), lo que lo
hace ideal incluso para el desarrollo de aplicaciones para dispositivos móviles.
Ficha técnica
Nombre: VistaDB
Versión: 3.2
Fabricante: VistaDB Software
Sitio Web: http://www.vistadb.net/
Categoría: Motores de bases de datos
Precio por desarrollador:
• Sin suscripción: 199 USD/desarrollador.
• Con suscripción anual: 299
USD/desarrollador*
• Con código fuente y suscripción anual:
1.499 USD/desarrollador
* - descuentos especiales para múltiples licencias.
Octavio Hernández es
Development Advisor
de Plain Concepts,
editor técnico de
dotNetManía y tutor
de campusMVP.
Es MVP de C# desde
2004, MCSD y MCT.
VistaDB Software lleva más de una década dedicándose al desarrollo de motores de bases de datos embebibles. Con el surgimiento de .NET Framework, la
empresa decidió no portar el código no manejado de
la versión anterior (que aún sigue a la venta), sino
rediseñar y volver a programar todo el producto
(incluyendo el núcleo del motor, las clases de su
librería DDA –Direct Data Access–, el procesador de
consultas compatible con Transact SQL, el proveedor ADO.NET y las herramientas externas) utilizando única y exclusivamente código C#. El resultado de ese esfuerzo es VistaDB 3, un motor de bases
de datos relacionales basado al 100% en código manejado que garantiza una integración completa con
.NET Framework, .NET Compact Framework y
Mono. Creo sinceramente que los equipos que estén
desarrollando aplicaciones que utilicen bases de datos
de pequeño o mediano tamaño deberían echar un
vistazo a este excelente producto.
Características principales
Una simple enumeración de la impresionante lista
de características de VistaDB 3 debería ser suficiente
para despertar la curiosidad de la mayoría de los lectores de la revista:
• Ejecución nativa en .NET, Compact Framework
2 y Mono, como hemos mencionado anteriormente. De aquí se desprende el soporte para todas
las plataformas para las que estas implementaciones funcionan.
• Arquitectura 100% manejada y segura en cuanto
a tipos, como puede comprobarse ejecutando PVerify. Esto implica que el código de VistaDB funcionará sin problemas en entornos de confianza
parcial, como el hosting compartido o desde una
unidad de DVD bajo Windows Vista.
• Soporte multiusuario para el acceso a bases de
datos en una unidad local, unidad de red compartida e incluso en memoria local, gracias a la
tecnología DDA.
• Ínfima huella de memoria, con menos de 1 Mb
de redistribución total, lo que garantiza un mínimo tiempo de descarga para los programas que
utilicen VistaDB y hace posible su incorporación
a aplicaciones para Pocket PC y otros dispositivos con memoria reducida.
• Ninguna necesidad de desarrollar instaladores
complicados. Solo será necesario copiar una DLL
de menos 1 MB con su ejecutable. Tampoco será
necesario tener privilegios de administrador o instalar servicios durante la instalación de un software basado en VistaDB.
<< dnm.laboratorio.net
• Embebible al 100% si se utiliza ILMerge. En caso
de que se combine el ensamblado de VistaDB con
la aplicación que lo utiliza, ni siquiera habrá que
redistribuir por separado ese ensamblado. dotNetManía ya dedicó hace unos meses un artículo,
escrito por mi compañero Jorgito Serrano [2], a
ILMerge.
• Bases de datos implementadas en un único fichero,
para simplificar al máximo el despliegue y la ya de
por sí casi innecesaria administración posterior.
• Compatibilidad con Microsoft SQL Server en los
tipos de datos y la sintaxis de las sentencias DML
en Transact SQL. Esto simplifica enormemente el
desarrollo simultáneo para ambas bases de datos.
• Soporte para procedimientos almacenados y disparadores en código manejado (C#, VB, etc.). Una
futura versión (finales de 2007) añadirá soporte para
procedimientos almacenados y triggers en Transact
SQL.
• Modelo de programación sencillo y familiar a los
desarrolladores .NET, basado al 100% en el modelo de objetos de ADO.NET 2.0, como ejemplificaremos más adelante.
• Integración completa en Visual Studio 2005. Desde el Explorador de servidores y la ventana de Orígenes de datos de VS 2005 es posible visualizar las
bases de datos de VistaDB 3, y usar las mismas técnicas de “arrastrar y soltar” disponibles para SQL
Server y otras bases de datos.
• Conjunto completo de herramientas visuales auxiliares, incluyendo Data Builder, para la creación y
modificación interactiva de bases de datos y Data
Migration Wizard, para la migración a Vista DB de
bases de datos SQL Server, Oracle, Access, FoxPro
y otras.
• Muy avanzado el soporte para Visual Studio 2008
y .NET Framework 3.5. De hecho, el ejemplo que
se presenta a continuación ha sido desarrollado bajo
la Beta 2 de VS 2008. También se trabaja actualmente en un proveedor LINQ y el soporte para los
nuevos servicios de sincronización.
y las numerosas aplicaciones de ejemplo que lo acompañan, y que muestran la utilización de VistaDB para
el desarrollo de aplicaciones de escritorio, Web o para
dispositivos móviles.
Figura 1. Grupo de programas
de VistaDB
Migración de datos
Comenzaremos nuestro proceso de familiarización
con VistaDB por la migración de una base de datos
de SQL Server; en este caso utilizaremos la base de
datos FUTBOL2006, que almacena información sobre los
equipos que tomaron parte en la Liga española de fútbol 2006-2007 y en la que se basa una buena parte de
los ejemplos de mi libro “C# 3.0 y LINQ” [3]. Tanto
la base de datos original como la resultante de la migra-
Un recorrido rápido
La instalación de VistaDB en un equipo de desarrollo es inmediata y no presenta problema alguno. Como
resultado de la instalación, se añade al menú “Inicio”
(figura 1) un grupo de programas desde el que se puede lanzar las herramientas que forman parte al producto, así como explorar la completa documentación
Figura 2. Migración de la base de datos FUTBOL2006 (1).
<<dotNetManía
Instalación
51
<< dnm.laboratorio.net
Para el desarrollo visual arrastrando y soltando basta
con registrar la base de datos ante el Explorador de
Servidores y la ventana de Orígenes de datos; la figura 5 muestra los detalles del proceso. A partir de ese
momento, se puede seguir una metodología similar a
la que se utiliza en el caso de otras bases de datos. Por
ejemplo, la figura 6 muestra el resultado de la ejecución de la aplicación terminada; la navegación por la
tabla de futbolistas ha sido obtenida sin escribir una
sola línea de código.
Figura 3. Migración de la base de datos FUTBOL2006 (2).
ción y todo el código de ejemplo pueden descargarse
del sitio Web de dotNetManía.
Para migrar la base de datos, lanzamos la herramienta Data Migration Wizard. Como puede verse
en las figuras 2, 3 y 4, el asistente reconoce perfectamente los metadatos y datos de SQL Server y ejecuta una migración perfecta. Desde el paso final del asistente es posible lanzar Data Builder, que nos permite examinar la base de datos de VistaDB resultante
(figura 4).
Figura 5. Agregando una conexión
a la base de datos VistaDB
Figura 4.VistaDB Data Builder en acción
<<dotNetManía
Desarrollo de aplicaciones
52
La programación de aplicaciones que utilicen bases
de datos de VistaDB es, gracias a la disponibilidad del
proveedor ADO.NET y la excelente integración con
Visual Studio, muy similar a si se tratara de bases de
datos de SQL Server, Access u otra de las bases de
datos soportadas directamente por .NET Framework.
La programación de aplicaciones
que utilicen bases de datos
de VistaDB es muy similar
a si se tratara de bases de
datos de SQL Server
<< dnm.laboratorio.net
cómo a nivel de programación también el trabajo contra una base de datos de VistaDB es muy
similar a si se tratara de una de las bases de datos
“habituales”, y cómo VistaDB soporta funciones predefinidas de SQL Server como DateDiff
o GetDate.
Conclusiones
Figura 6. La aplicación de ejemplo en ejecución.
Pero como no solo de desarrollo visual vive el programador, hemos agregado a la interfaz de usuario de
la aplicación un botón cuyo gestor de eventos ejecuta, a través de una conexión diferente, una sencilla sentencia SQL; el código (listado 1) permite comprobar
A lo largo de este artículo hemos intentado mostrar
las principales ventajas que puede ofrecer a los desarrolladores la utilización en sus proyectos que necesiten bases de datos de pequeño o mediano tamaño de
un motor de base de datos ligero y embebible como
VistaDB. Recomendamos sin duda alguna al lector
que descargue del sitio Web del fabricante [1] la versión de evaluación y compruebe por sí mismo las bondades de este producto.
// using VistaDB.Provider;
private void button1_Click(object sender, EventArgs e)
{
using (VistaDBConnection con = new VistaDBConnection(
“Data Source=C:\\DNM42\\FUTBOL2006.vdb3;Password=futbol2006”))
{
con.Open();
using (VistaDBCommand cmd = new VistaDBCommand(
“SELECT AVG(DateDiff(year, FechaNacimiento, GetDate()))” +
“ FROM Futbolista”, con))
{
int media = (int) cmd.ExecuteScalar();
MessageBox.Show(“La media de edad es de “ +
media.ToString());
}
}
}
Referencias
[ 1 ] Sitio Web de VistaDB: http://www.vistadb.net.
[ 3 ] Hernández O., “C# 3.0 y LINQ”, ISBN: 978-84-935489-1-9, Krasis Press (http://www.krasispress.com).
<<dotNetManía
[ 2 ] Serrano J., “Combinando ensamblados .NET con ILMerge”, en dotNetManía nº 36, abril de 2007.
53
comunidad.net
MVP Open Day y TTT 2007
Grupos de usuarios .NET, DotNet Clubs y MVP, juntos por un día
<<dotNetManía
Ambos eventos se han celebrado conjuntamente este año, reuniendo a los
profesionales y estudiantes más involucrados en la difusión de tecnologías
Microsoft.
54
El día 5 de octubre se celebró en las oficinas de Microsoft Ibérica en Madrid el “MVP Open Day y TTT”,
un evento que este año congregó a los MVP y a los
componentes de grupos de usuarios y los clubs universitarios (dotnet clubs), reuniendo así a un buen
número de entusiastas de tecnologías Microsoft de
toda España, todos ellos con vocación de aportar
talento a la comunidad de desarrolladores y profesionales IT en nuestro país.
Las ponencias se dividieron en tres tracks: desarrollo, sistemas y Office. En el track de desarrollo
asistimos a las charlas “Meigas… haberlas haylas.
ADO.NET Synch Services” de Unai Zorrilla;
“Codigo VIVO. LIVE Services for Developers”, de David
Salgado; y “Y se hizo la luz.
Silverlight” de Isabel Gómez.
Ya por la tarde asistimos a reuniones particulares de los grupos de usuarios y dotnet clubs y
de los MVP, con la presencia
de Çigdem Akin, EMEA
MVP Team Manager de
Microsoft, Steve Alter, MVP
Program Manager de Microsoft y Cristina González,
MVP Lead de Microsoft.
Finalizó la jornada con una cena a la que Microsoft Ibérica nos invitó amablemente, y en la que todos
tuvimos la ocasión de confraternizar con compañeros a los que habitualmente solo vemos en el ciberespacio.
Cambios para MSDN
Muy interesante estuvo la presentación de
MSDN Online, que además de estrenar nueva imagen, ofrecerá próximamente la traducción inmediata de los contenidos tanto de los centros de desarrollo americanos como de la revista MSDN Magazine y MSDN Library.
Además, se han mejorado las búsquedas, con
indexación de contenidos incluidos en blogs y foros;
habrá cambios de infraestructura de los foros (con
fotos de participantes, por ejemplo) y nuevos cursos online, entre los que tendremos uno sobre Silverlight para noviembre y otro de Orcas para principios del año que viene.
Se hizo mucho hincapié en la importancia de la participación de toda la comunidad de profesionales y estudiantes en la elaboración de contenidos en castellano.
<< dnm.comunidad.net
opciones, finalmente se ha decidido crear nuestra
propia versión, que
verá la luz a finales
Fotografía reciente de Capitán Tomate de este año natural. En Capitán
Capitán Tomate
Tomate se publicarán contenidos
multimedia, vídeos con entrevistas,
series técnicas, eventos de grupos de
Además, David Salgado, evangelista
usuarios, screencasts, podcasts, además
de desarrolladores de Microsoft, presentó
de otros recursos en castellano.
la iniciativa que divertidamente han denominado Capitán Tomate, con una filosofía similar a Channel 9, pero en casteAlto o disparo…
llano. Después de reunirse con la dirección de Channel 9 (http://channel9.
Por último, el día 6 de octubre pudimsdn.com) en USA y valorando otras
mos resolver todas nuestras diferencias
David Salgado, evangelista de desarrolladores de
Microsoft, atacado por los feligreses
a balazos en un divertido paintball que
nos dejó con algún que otro moratón,
pero satisfechos al haber podido ajustar cuentas pendientes ☺.
III Aniversario Gusenet
Santa Pola, 23, 24 y 25 de noviembre
Como en ocasiones anteriores,
hemos preparado
una combinación
de actividades técnicas con actividades lúdicas.
A continuación tenéis la agenda prevista.
Tened en cuenta que podéis elegir las actividades que queráis, desde venir solos al
evento e iros al finalizar, hasta pasar un fin
de semana completo con la familia.
Agenda
Viernes
Llegada y registro en el hotel (para los
que quieran venirse con tiempo).
Sobre las 21:00, salida de cañas y cena
por Santa Pola.
Paralelamente a las actividades técnicas,
habrá una visita organizada a las salinas de Santa
Pola para los acompañantes.
Sábado
Lugar: Baluarte del castillo.
09:00 Desayuno, inscripción y registro.
10:00 Desarrollo Windows con Visual Studio 2008. El Guille (Solid Quality
Mentors).
12:15 Desarrollo Web con Visual Studio 2008.
David Salgado (Microsoft Ibérica).
14:00 Cierre de la mañana y salida al restaurante. Comida todos juntos.
16:30 Mesa redonda sobre el grupo de usuarios. Ofertas de empleo y otros.
18:30 Cierre, entrega de goodies y retirada al
hotel.
20:30 Salida de cañas y cena.
Domingo
10:00 Salida hacia la isla de Tabarca y comida ya
de vuelta en Santa Pola.
Hotel
Hotel Patilla, Santa Pola. Habitación doble,
sin desayuno: 62€ (IVA incluido)
Más información y detalles actualizados en
www.gusenet.com.
Salvador Ramos - Gusenet
eventos.eventos.eventos.eventos.eventos.eventos
<< dnm.comunidad.net
October .NET Conference
<<dotNetManía
•
56
Chad Hower también
presentó dos trabajos,
“Una introducción
seria a WPF”, sobre
los fundamentos de
esta nueva tecnología
de presentación, y
“Silverlight 1.1”, en la
que nos adelantó las
Durante los días 15 y 16 de octubre se llevó a
principales posibilidades que pondrá a
cabo en las instalaciones del Hotel Siken “Puerta
nuestra disposición la
de Málaga” de esa ciudad andaluza el evento
próxima versión de
Silverlight.
October .NET Conference, organizado por el
• Tanto las ponencias de
.NET User Group de Málaga y patrocinada por
Michael Li, “Defienda
sus aplicaciones ASP.
iMeta y MSDN, además de otras empresas.
NET de los ocho ata
ques más comunes” y
“Escalabilidad de aplicaciones
Contando un elenco de speakers realASP.NET” como las de Dino Espomente sobresaliente, que incluía tanto a
sito, “ASP.NET para valientes” y “Por
figuras de relieve internacional como
qué el renderizado parcial no es
Neal Ford (ThoughtWorks), Chad
AJAX”, y las de José Manuel Alarcón,
Hower (Woo-Hoo), Michael Li (Info“Diseño avanzado de controles con
Can), Hadi Hariri (AtoZed Software) o
ASP.NET 2.0” y “Creación de páginuestro columnista habitual Dino Esponas modulares personalizables
sito (Solid Quality Mentors), como a
mediante WebParts” estuvieron dediponentes españoles de amplio reconocicadas a temas relacionados con el desamiento como José Manuel Alarcón
rrollo de aplicaciones Web ASP.NET.
(CampusMVP), Guillermo “El Guille”
•
Otro
tema que estuvo ampliamente
Som (Solid Quality Mentors), David
representado
en la conferencia fue el
Salgado (Microsoft), Martín López
relacionado
con
las novedades que
(Adesis Netlife) y Unai Zorrilla y un serintroducirán
las
próximas
versiones de
vidor (Plain Concepts), el evento atrajo
.NET
Framework
y
Visual
Studio en
la atención de más de 150 entusiastas parlos
lenguajes
de
programación
y las
ticipantes, que disfrutaron de 20 presenposibilidades
que
ofrecerá
la
tecnotaciones relacionadas con los más diverlogía LINQ, donde “El Guille” y un
sos aspectos del desarrollo de aplicacioservidor presentamos (uno orientánnes para la plataforma .NET:
dose a Visual Basic 9.0 y el otro, a C#
• Neal Ford presentó dos ponencias,
3.0), dos ponencias cada uno: una
“Polyglot programming”, sobre la
dedicada a las novedades en el lenguaje
conveniencia y las vías para combinar
y la otra, a la utilización desde él de la
durante el desarrollo diferentes paratecnología LINQ.
digmas y lenguajes de programación
•
Hadi Hariri también presentó dos trapara aprovechar las ventajas relativas
bajos,
“Lo que todo desarrollador debe
de cada uno, y “Ruby on Rails en
conocer
sobre MS Build”, sobre las
.NET”, una introducción al desarroposibilidades
que ofrece esta potente
llo de aplicaciones utilizando este cada
herramienta,
y “Uso del CLR desde
vez más popular marco de trabajo.
•
•
•
•
SQL Server”, sobre cómo aprovechar
las ventajas del código .NET dentro
de SQL Server 2005.
Otro invitado extranjero, Pawel Glowacki de CodeGear, mostró las posibilidades que ofrece la recién estrenada versión de RAD Studio 2007
(cuyo nombre interno era Highlander), que permite desarrollar aplicaciones tanto para Win32 (incluyendo
Vista) como para .NET en diversos
lenguajes de programación.
David Salgado, de Microsoft, en su
ponencia “Depuración en entornos de
producción”, ofreció múltiples recomendaciones prácticas en relación con
un aspecto tan esencial en nuestro trabajo diario como es la depuración.
Unai Zorrilla deleitó a los asistentes
con sus profundos conocimientos
sobre Windows Workflow Foundation y mostró las ventajas que ofrece
este potente framework para modelar
procesos de negocio.
Por último, en otra ponencia muy
interesante que contó con un alto nivel
de participación, Martín López
mostró las posibilidades que se abren
ante los desarrolladores que utilicen
SharePoint gracias a la versión 3.0 de
Windows SharePoint Services.
Todas las presentaciones están disponibles para su descarga en el sitio Web
del evento, http://www.octoberconference.net/content.
Resumiendo, quiero plasmar aquí la
impresión que comparto con otros
ponentes y asistentes con los que tuve la
oportunidad de charlar de que el evento
fue un rotundo éxito en todos los sentidos y manifestar nuestro sincero agradecimiento a Málaga .NET User Group
y en particular a su coordinador, Hadi
Hariri, por todo el trabajo desarrollado
para hacerlo posible. Esperamos que el
año próximo se repita.
Octavio Hernández
biblioteca.net
3D Programming for Windows
Charles Petzold
Editorial: Microsoft Press
Páginas: 448
Publicado: julio 2007
ISBN: 978-0735623941
Idioma: inglés
Tener un libro de Petzold sobre el escritorio es, para muchos de nosotros (al menos, los
de mayor edad), algo que con los años se ha hecho tradicional. Esta vez el Maestro vuelve con un tema relacionado con la programación de interfaces de usuario, hasta cierto
punto continuación de su anterior obra, “Applications = Code + Markup”, y no nos defrauda en modo alguno. Asumiendo del lector el dominio previo de los fundamentos de programación para Windows Presentation Foundation, este libro presenta los conocimientos necesarios para trabajar con mallas geométricas (la base sobre la que se apoyan los
gráficos 3D en WPF) y muestra cómo utilizar los recursos que este componente esencial de .NET 3.0 pone a disposición de los desarrolladores para crear de una manera efectiva aplicaciones que incluyan gráficos tridimensionales.
Modelando procesos de negocio con Workflow Foundation
Unai Zorrilla Castro
Editorial: Krasis Press
Páginas: 242
Publicado: octubre 2007
ISBN: 978-8493548926
Idioma: castellano
novedades
Unai Zorrilla es ponente habitual en los eventos que realiza Microsoft por toda
España, y una de las personas con conocimientos más amplios y profundos acerca de la
plataforma .NET y todo lo que la rodea que conozco. En este, su primer libro, Unai nos
ofrece un excelente acercamiento a las posibilidades que ofrece Workflow Foundation,
otro de los nuevos pilares introducidos con .NET Framework 3.0, para modelar los más
diversos procesos de negocio.
La primera parte del libro presenta el concepto de flujo de trabajo e introduce las
piezas clave de la infraestructura asociada. A continuación se describen detalladamente,
con ayuda de ejemplos, las actividades o pasos de los flujos de trabajo que WF provee
por defecto, así como los servicios que ofrece (persistencia, planificación y otros). Varios
capítulos posteriores, de un corte más avanzado, se refieren a la creación de actividades
personalizadas, con ejemplos completos de proyectos de este tipo. Finalmente, el libro
dispone de dos apéndices, dedicados respectivamente a la implementación de máquinas
de estados dentro de WF y a las novedades que introducirán en esta tecnología .NET
Framework 3.5 y Visual Studio 2008 en un futuro ya muy próximo. Recomendado tanto a aquellos que deseen familiarizarse con las posibilidades que ofrece WF como a quienes deseen comprender las interioridades de su funcionamiento.
Programación Web 2.0
Eric Van der Vlist, Danny Ayers, Eric Bruchez y otros. Editorial: Anaya Multimedia/Wrox.
Páginas: 560. Publicado: septiembre 2007. ISBN: 978-84-415-2252-7.Idioma: castellano.
Desarrollo de aplicaciones Web
Ralph Moseley. Editorial: Anaya Multimedia/Wrox. Páginas: 400. Publicado: septiembre
2007. ISBN: 978-84-415-2265-7. Idioma: castellano.
TEXTO: OCTAVIO HERNÁNDEZ
Marino Posadas
Windows XP Service Pack 3, con muchas
novedades de seguridad
Según un análisis realizado por NeoSmart,
el nuevo Service Pack 3 para XP no solo
incluirá nuevos drivers, optimización y arreglos de bugs, sino muchas características innovadoras, en especial respecto al tema de la
seguridad, con la ventaja de un rendimiento
excelente, según los analistas. Una de ellas,
Network Access Protection (NAP), impide el
acceso a Windows Server 2008 sin pasar un mínimo de garantías
de seguridad, respecto a la presencia de otros SP y ciertos parches, deshabilitando el acceso hasta que se produzca la actualización. Otro cambio importante sería la inclusión del nuevo
Kernel Mode Cryptographic Module (KMCM), que permite a los
administradores implementar una segunda capa de políticas
para comunicaciones cifradas, asumiendo que los algoritmos
Triple-DES estén accesibles a través del kernel. Otras mejoras
se refieren a la pila de direcciones IP.
Independientemente de esto, también existirán algunas
mejoras y novedades operativas, algunas de ellas fruto de la
experiencia obtenida en la implantación de Windows Vista: no
será necesario teclear la clave del producto durante el proceso de instalación, existirán varias mejoras en el rendimiento y
fiabilidad, así como arreglos y actualizaciones de drivers.
noticias.noticias.noticias
<<dotNetManía
Google propone la creación de
Multiversos Virtuales
58
A pesar de que el furor
creado por “Second Life”
parece remitir, los mundos 3D están de moda, y
lo van a estar más aún si
tenemos en cuenta esta
propuesta de Google,
Arquitectural Wonders, que permitirá a los
usuarios crear “universos
interactivos 3D on-line”. Se podrá partir de una base geográfica
como Google Maps, e incluir elementos de un repositorio de modelos 3D. Por ejemplo, podremos partir de un terreno, como un
barrio de nuestra ciudad, incluir modelos de edificios en 3D, e
incluso crear los propios del usuario para generar un auténtico
mundo virtual por donde pasear e interactuar, pudiendo añadir
toda clase de contenidos personalizados. Para más información,
ver el artículo de Daniel Terdiman en CNet News “Google tools
to power virtual worlds” (http://www.news.com/Google-tools-to-powervirtual-worlds/2100-1043_3-6212325.html?tag=nefd.lede).
noticias.noticias.noticias
desván
documentos en la red
Introduction to ASP.NET 2.0 SqlDataSource Control de
Michael Youssef. En el sitio Web http://www.aspfree.com/c/a/
ASP.NET/Introduction-to-ASPNET-20-SqlDataSource-Control
encontramos este interesante artículo sobre el control SqlDataSource y su forma de utilizarlo en ASP.NET. Pero no
es el único artículo que recomendamos de este sitio. También es notable el de Jagadish Chaterjee, “Developing
Business Logic using the WCF Service Library with
VS2K8 and ASP.NET 3.5”, disponible en http://www.aspfree.com/c/a/ASP.NET/Developing-Business-Logic-using-theWCF-Service-Library-with-VS2K8-and-ASPNET-35/.
Petzold Book Blog. Charles Petzold, uno de nuestros
más admirados autores de literatura técnica, opina (¡y
cómo!) sobre el oficio de escribir y las penurias, dificultades y escasa remuneración y recompensas de este noble
arte. Ya hemos recomendado su blog en esta sección, pero
quienes se planteen algo así deberían echar un vistazo a
sus consejos en “Hard Work, No Pay: What's the Point?”,
disponible en http://www.charlespetzold.com/blog/2007/
10/081247.html
Humor (informático) en la red
Inauguramos esta sección con unos toques
de humor —que la informática da para
todo— que hemos visto navegando por ahí.
El primero es un documento sobre la salud
de los programadores (o la falta de ella, debido a nuestra actividad). Se trata de “Geek Diet and Exercise Programs”, disponible en Coding Horrors (http://www.codinghorror.com/
blog/archives/000970.html). El otro es un chiste gráfico muy
bueno. A los especialistas en seguridad les encantará…
(http://imgs.xkcd.com/comics/exploits_of_a_mom.png).
utilidades del mes
Pipelines by TenFiftyTwo. Se trata de una utilidad para programar modificaciones individuales o masivas en ficheros de texto. Mediante una sintaxis muy simple, podemos programar estas modificaciones a voluntad, tanto en ficheros locales como en red. Es una utilidad
gratuita disponible en http://www.tenfiftytwo.co.uk/pipelines. En la página del sitio disponemos de una detallada explicación de uso.
Script Converter. Interesante utilidad que permite convertir código HTML en equivalentes en otros lenguajes, como PHP, ASP, JSP, Perl, Python y VBScript. Se puede descargar del sitio http://www.ethicdevelopment.com/freesoftware, donde el visitante encontrará
también otras utilidades de interés.

Documentos relacionados