Clase Singleton vs. estática

Esta es una duda que nos suele surgir muy a menudo a todos… ¿merece la pena desarrollar un Singleton, o me basta con una clase estática? ¿Cual es el enfoque correcto? ¿Es el Singleton la clase estática de los «pijos», o la clase estática el Singleton de los pobres? La verdad es que como siempre que hablamos de estos temas, suele haber opiniones para todos los gustos… así que contribuiré con una más a que el mundo sea todavía más caótico.


Bueno, como todos sabéis, una clase estática y una singleton tienen, a grandes rasgos, una gran diferencia: la clase Singleton es aquella para la que existe una única instancia accesible por nuestra aplicación y la clase estática es aquella para la que ni siquiera existe instancia (todos sus métodos y miembros son estáticos). Aparentemente sirven para lo mismo, así que… ¿por qué debería usar un Singleton?



  • El principal motivo, a mi entender, es la capacidad que tiene el Singleton de evolucionar hacia una clase que tenga más de una instancia. Puede que no vemos sentido a esto, pero el principio de encapsulación que hay detrás de un Singleton es el mismo que distingue una propiedad de un miembro, o un evento de un delegado. La clase que hace un GetInstance de un Singleton asume que está obteniendo «la instancia» de esa clase, ¡pero nada nos obliga a que esa clase tenga que ser única! Es decir… ¿cúanto nos costaría ampliar el código que os pongo abajo, para añadirle un contador de instancias y permitir un máximo de tres? (lo dejo como ejercicio espiritual para mentes ávidas de pensar). En resumen… un puntito para el Singleton.



    class Singleton
    {


       private static Singleton _instance;


       private Singleton()
       {
       }


       public static Singleton Instance
       {
       
    get
           
    {
              
    if (_instance == null)
               {
                   _instance =
    new Singleton();
               }
              
    return _instance;
            }
      
    }
    }



  • Otra ventaja del Singleton está en que los métodos no son estáticos, y por tanto se pueden sobreescribir en las clases herederas (son «overrideables»). No se puede hacer un override de un método estático. Vamos, que si pretendemos hacer una jerarquía de clases de instancia única, más nos vale que tiremos hacia el singleton… de lo contrario tendremos problemas. Singleton 2, Estática 0

  • La clase Singleton tiene instancia, y eso en sí ya es una ventaja. Por ejemplo, eso nos facilitará que nuestro Singleton pueda implementar interfaces que sencillamente una clase estática nunca podrá implementar. De nuevo, la clase Singleton puede «crecer» con más facilidad que la estática en este sentido. Singleton 3, Estática 0

  • En cuanto a qué es mejor para implementar una clase Thread Safe, fijaros que básicamente el problema que tenemos es el mismo: si tenemos varios threads modificando miembros de la clase, habrá que sincronizarlos y asegurarnos de que sólo uno es capaz de alterar un miembro cada vez. La clase Singleton tiene una peguilla adicional, y es que tenemos que proteger también el método que obtiene la instancia. No es un gran problema… hay un artículo muy interesante de cómo hacerlo AQUÍ. Yo diría que en cuanto a la seguridad de threads, ambas clases «empatan».

  • Algunos opinan que la clase Singleton es más adecuada para representar clases que mantienen un estado. En realidad esta puede ser más una cuestión de gusto (buen gusto, se entiende) que un requisito real. Ambas clases pueden contener perfectamente un estado. De nuevo otro empate.

  • ¿Y dónde gana la clase estática? En su simplicidad. Así que como el «Keep it Simple» es un argumento que vale por tres, ¡digamos que Singleton y clase estática empatan a puntos! Singleton 3, Estática 3

Vamos, que la idea con la que nos debemos quedar es que una clase Singleton va a hacer nuestro programa un pelín más difícil de comprender, así que como todo, es un recurso que sólo debemos utilizar si es necesario. Y yo, como siempre, que sigo sin mojarme… ¿quieres mojarte tú? ¡¡pues mándame tus comentarios!! Los espero ansioso.

El verdadero geek no se queja de que su reproductor de MP3 no ordene las canciones…

… el verdadeo geek, escribe una rutina que ordene alfabéticamente los archivos y directorios de un directorio FAT.


Sí, lo reconozco: tengo un reproductor de MP3 que es una verdadera porquería. Antes tenía un Creative MuVO V200, pero desde que me lo cargué con una actualización de firmware, mi vida se había vuelto gris y anodina. Sabía que si me compraba algo mejor que el MuVO, también acabaría tratando de «tunearlo» a toda costa, y por ende, cargándomelo, así que me decanté por un modelo baratito. Así, al menos, cuando me lo cargase, tampoco me daría demasiada pena.


El caso es que mi nuevo reproductor desde el principio me dejó desconcertado. Resulta que cuando cargaba canciones desde el Windows Media Player, estas no se reproducían en orden alfabético, sino en el orden en el que las había añadido al reproductor. ¡Qué invento del demonio!


<< Tanto esmero en ordenar mi biblioteca de emepetreses para que este chisme con vida propia decida que mi vida requiere un orden distinto al que yo he previsto. No me gusta… no, no, no y no. Me niego a la regir mi vida al ritmo de un tirano digital >>. Tenía que hacer algo, así que busqué una explicación a semejante ultraje.


Lo primero que pensé fue: «el ceporro que haya programado esto, ordena las canciones por fecha de creación del archivo». Es curioso… los programadores siempre pensamos que el resto de programadores son unos ceporros, ¿verdad? Y resulta que no es ni más ni menos certero que el nivel ceporrismo que tú, querido lector, puedas intuir en mis palabras. Lo cierto del caso es que el programador del firmware de mi MP3 no es un ceporro. Es un programador ágil, que no ha implementado ninguna característica porque nadie se la ha solicitado.


El problema de mi reproductor de MP3 es muy común, y no está causado por la mala baba de un programador que quiera dominar el mundo imponiendo su propio orden a nuestros archivos: está causado por la implementación del sistema de archivos FAT.


Pensemos en cómo es un directorio FAT:



Bueno, además de ver que soy un cutre que dibuja en las servilletas de papel, vemos que un directorio es una simple tabla con el nombre, extensión, atributos… y un apuntador al primer cluster en el que se encuentra el archivo. ¿Cual es el problema con mi reproductor de MP3? El problema es que cuando añadimos archivos o subdirectorios a un volumen FAT, estamos añadiendo secuencialmente entradas a esta tabla. El programador de mi reproductor, va leyendo y reproduciendo cada una de estas entradas, así que si las pilla desordenadas, las reproduce tal cual (provocando la ira de los que como a mi, nos gusta oir las canciones en un determinado orden).


¿Cómo se puede solucionar esto?


Lo primero que se nos ocurre es: ¡Ordenemos esa tabla! Se trata sólo de leer estructuras de tamaño fijo desde disco a un buffer, ordenarlas, y volver a volcarlas a disco. Es cierto, el problema es tan sencillo como ese… pero este enfoque tiene dos desventajas:



  • Requiere acceso directo a disco, por lo que en Windows sólo podría funcionar con permisos de administrador.

  • Las estructuras a manejar son distintas en función del tipo de FAT que tenga el disco (hay FAT12, 16 y 32, con ligeras diferencias en el directorio… nombres largos… etc).

La segunda solución requiere aprovecharse del conocimiento de la mente de los programadores. Sí amigos… pensad en cómo MOVERÍAIS archivos en un directorio FAT. Desde luego, a nadie se le ocurriría moverlos físicamente en disco… ¡basta con cambiar las entradas de la tabla!. Así que ese es el truco que siempre funciona para ordenar archivos en un volumen FAT: ir moviéndolos a un nuevo directorio, sabiendo que la nueva tabla se irá creando secuencialmente. Una vez completada la operación, sólo tenemos que sustituir el directorio original por el nuevo.


Así que ahora ya puedo ir tranquilamente por el mundo, escuchando mis canciones en el orden que a mi me gusta, y mirando por encima del hombro a todo aquel que tiene un iPod, Muvo, Zune, o cualquier reproductor de «pedigree». Vosotros tendréis un reproductor mejor que el mío, pero el mío venía con rompecabezas de regalo, ¡y lo he solucionado!.


PD: Os dejo los fuentes y binarios del programita que uso para ordenar el directorio de mi reproductor de MP3. A ver si alguien le pone una bonita interfaz gráfica de usuario, lo modifica para que ordene por distintos criterios al alfabético, ¡y de paso me lo envía de vuelta!



 

Transacciones en Integration Services

Uno de los puntos de SSIS que muchas veces nos pasa desapercibido y que sin embargo conviene conocer, utilizar y amar, es el soporte para transacciones. Estamos cargando un DataMart, y ¡zas! la carga falla a mitad de camino. ¿Qué hacemos? ¿Parece mentira que la ejecución de un ETL que puede hacer infinidad de cambios en nuestro DataMart se pueda deshacer en caso de fallo, verdad? ¡Pues se puede!


Para controlar las transacciones, todos los objetos del «Control Flow» (incluido el propio control flow) tienen una propiedad llamada «TransactionOption», que puede tomar los siguientes valores:



  • Required: Este elemento se ejecuta en el contexto de una nueva transacción.

  • Supported: El elemento se ejecuta en el contexto de la transacción que ya estuviese abierta (por alguno de sus «padres»). En caso de que no se hubiese iniciado ninguna transacción, el objeto no es transaccional. Es el valor por defecto que da Integration Services a todos los elementos del flujo de control.

  • NotSupported: El objeto no es transaccional, aunque esté ejecutándose en el contexto de una transacción



 


Entonces, hacer un ETL transaccional es tan sencillo como pulsar en el fondo del «Control Flow» de un paquete y establecer TransactionLevel a «Required». Eso hará que todos los elementos que arrastremos al flujo se ejecuten por defecto en el contexto de una transacción. Si uno de estos elementos falla, se desharán los cambios realizados por el resto de elementos que se hubiesen ejecutado dentro de la misma transacción.


El segundo parámetro a tener en cuenta es IsolationLevel, que especifica cómo gestionará SSIS la transacción. Podéis buscar información en MSDN sobre este parámetro… yo sólo os dejo caer un par de notas:



  • Cuanto menos restrictivo sea el modo de funcionamiento de la transacción, mayor será el rendimiento. Por ejemplo, ReadUncommited y Chaos permiten ver los cambios antes de que estos se hagan permanentes con un Commit. O sea, que si estamos cargando un DataMart y mientras tanto alguien accede al mismo, obtendrá datos inconsistentes. Si nuestras circunstancias permiten «evitar» el acceso al DataMart mientras esté siendo cargado, puede ser una buena opción.

  • Serializable es el más restrictivo, pero a la vez más seguro: evita que se pueda acceder a los datos nuevos que insertemos en el DataMart hasta que se haga un Commit, y evita que se modifiquen los datos anteriores al inicio de la transacción. Generalmente es el más adecuado para cargar un DataMart (es la opción por defecto). 

  • Snapshot es nuevo en SQL Server 2005, y permite trabajar a la transacción sobre una «copia» de los datos tal y como estaban al inicio de la transacción, sin bloquearlos. Al finalizar la transacción, se consolidan los cambios. Esta es la que menos impacto supondrá en el resto de aplicaciones que accedan al DataMart mientras lo estamos cargando, pero a costa de usar parte de nuestro espacio de almacenamiento. Personalmente no le veo demasiado uso en la carga de un DataMart, porque por su propia naturaleza, los datos del DataMart no suelen sufrir modificaciones (por lo que Serializable puede ser una mejor opción)

Espero que después de esto en vuestras oficinas dejen de oirse gritos y lamentos debido a ese corte de luz que sucede siempre justo en medio de la ejecución de un ETL…


Y ya sabés, feliz navidad…. ¡y prósperas transformaciones!

¿Te gusta mi Software Factory?

Hay dos cosas que me pierden: el merchandising de los eventos de Microsoft, y casi todo lo nuevo que voy probando. Sí, si, lo se… soy un poco «pattern happy», un poco amante de los gadgets y muy, muy «friki» (en el sentido más puramente hispano de la palabra). Actualmente me está atrayendo poderosamente la idea de las Software Factories y los Domain Specific Languajes (DSL). Me atrae tan poderosamente que, como me conozco, empiezo a dudar sobre si lo que me atrae es real, o son cantos de sirena.


¿Software factoqué?


Cuando el famoso cocinero de la tele nos prepara un delicioso Marmitako, o nos deleita con una chuletitas de cordero ricas, ricas, ricas… nos está enseñando una receta. Nos da un conjunto de pasos, técnicas y conocimientos, que nos ayudan a triunfar en un determinado dominio: la cocina. Digamos que la distancia para sorprender a nuestra churri, es más corta si seguimos una receta que si tratamos de improvisar en los fogones.


Una Software Factory sigue el mismo principio: es un conjunto de patrones, documentación (recetas), plantillas y mucha, mucha, mucha… ¡generación automática de código! Personalmente estoy usando la Smart Client Software Factory, que te ayuda a desarrollar Smart Clients que se sustentan sobre Composite UI Application Block (CAB).


Vale, eso tiene sentido… ¿y los DSL?


Las Software Factory casi siempre van de la mano de los DSL, o «Domain Specific Languajes». ¿Te imaginas que el programa de Arguiñano se hiciese en el plató de Bricomanía? ¿Sería raro, verdad? Cada situación requiere un conjunto distinto de herramientas, y cada dominio, un lenguaje adaptado a su contexto. El problema de los lenguajes de propósito general, es que son como el plató de Bricomanía… por el simple hecho de que tengamos una sierra de calar, podemos sentir la tentación de usarla para trinchar el pavo. La solución para evitar el error, es deshacerse de la sierra de calar.


Hace poco, alguien, tratando de hacerme bajar de mi nube, me decía que hace 20 años que tratan de vendernos los lenguajes de 4ª generación, y me insinuaba que esto es el mismo perro con distinto collar. Bueno, en cierto modo los DSL se parecen a los 4GL en que buscan ser declarativos (yo digo lo que quiero, y no cómo lo quiero) pero no pretenden ser tan ambiciosos. Ciñéndose a un dominio, es más fácil tener éxito con un lenguaje declarativo.


Lo cierto es que cada vez que abrimos el diseñador de interfaces de Visual Studio, ya estamos usando un DSL. Nosotros añadimos controles a un formulario y enlazamos eventos con código usando un lenguaje visual, basado en el paradigma de arrastrar y soltar. Lo mismo ocurre cuando desarrollamos un ETL con Integration Services… los DSL llevan años acompañándonos.


En algunos casos, el DSL ni siquiera requiere que se abandone el lenguaje de propósito general… simplemente, se limita a ocultar la complejidad del mismo, y nos guía para que sólo utilicemos aquella parte del lenguaje adecuada a nuestros fines. Llevamos años encapsulando para tratar de ocultar la complejidad… ¿por qué somos tan reacios a que una herramienta nos abstraiga del propio lenguaje procedural?


Lo pasamos por la batidora, y directo al horno (220ºC)


Microsoft nos está preparando un buen plato a base de DSLs, patrones, Software Factories y demás ingredientes. Los muchachos se llaman GAX, GAT (Guided Automation Extensions y Guided Application Toolkit, respectivamente) y DSLTools y están a punto de llegar a este lado del Mississippi.


Esta pretende ser una revolución en la forma en la que desarrollamos software. Paremonos a pensar en la mayoría de ISVs y sus catálogos de software… es habitual que los ISV se especialicen en un dominio, ¿verdad? Los hay quienes hacen herramientas de desarrollo, software de tratamiento de imágenes, productos de seguridad… pero generalmente, igual que cada cabra tira a su monte, cada ISV va a su terreno. Luego hay consultoras capaces de hacer de todo (hasta botijos, si estuviesen bien pagados), pero bueno, estamos hablando de desarrollo de software, y no de tratantes de carne humana (huy lo que he dichoooooo…), y estoy seguro que hasta éstas pueden beneficiarse de las Software Factories.


Imaginaos este mundo idílico, en el que nuestro arquitecto desarrolla la Software Factory adecuada a nuestro dominio, y nuestros desarrolladores la utilizan para programar aplicaciones específicas para el dominio. ¿Acaso no es una forma estupenda de transferir el conocimiento?


¡Camarero una manzanilla!


Para acabar sin indigestión, os voy a recetar un poco de descanso (a poder ser en vuestro sofá más cómodo) y cuando vuestro cerebro pida guerra, unos cuantos links:



Y ahora, ya puedes opinar… ¿Estamos hablando de la Piedra Rossetta del desarrollo de software, o son cantos de sirena?

Ya tenemos RadarCube NET Windows Forms for Microsoft Analysis Services

El mercado de los controles del tipo «pivot table» empieza a activarse. Señores… hay demanda para un sustituto de los Office Web Controls, y RadarSoft está echando el resto. Hoy han lanzado RadarCube NET Windows Forms, un control Winforms para .NET 2.0 que permite presentar datos multidimensionales en nuestras aplicaciones Windows. Podéis encontrar más información y unos cuantos pantallazos en esta URL: http://www.radar-soft.com/products/radarwin_msas.aspx.


Además hoy han anunciado que bajan los precios del RadarCube ASP.NET.


Dos estupendas noticias… a ver si hay suerte y se empiezan a popularizar herramientas que hasta ahora estaban reservadas sólo a una élite. Como diría Groucho… ¡¡¡Más madera!!!

Testeo unitario de cubos OLAP

En Internet no se encuentran muchas páginas sobre cómo testear un cubo OLAP… probablemente, como en toda tecnología «emergente», cada uno se las arregle como pueda y pocas cosas estén ampliamente extendidas. Sin embargo, con el resto de tecnologías «más convencionales» todo el mundo tiene una opinión formada sobre el testeo unitario o el «test driven development» (generalmente para bien). ¿Se pueden conciliar ambos terminos? ¿Puedo escribir «unit tests» para mis cubos OLAP?


La respuesta es «SIN DUDA». El mundo del Business Intelligence puede beneficiarse en gran manera del testeo unitario, gracias a lo que se conoce como «Data Driven Unit Test». ¿De qué va esto?


Analicemos las siguientes sentencias SQL y MDX, lanzadas respectivamente contra la base de datos «Adventure Works» y el cubo de Analysis Services correspondiente:




  • SELECT Production.Product.ProductNumber AS Product, 
        SUM(Sales.SalesOrderDetail.OrderQty) AS Quantity
    FROM Sales.SalesOrderDetail, Production.Product
    WHERE Sales.SalesOrderDetail.ProductID = Production.Product.ProductID
    GROUP BY Production.Product.ProductNumber
    ORDER BY Product ASC



  • SELECT NON EMPTY [Product].[Product Key].[Product Key] ON ROWS,
        [Measures].[Order Quantity]
    ON COLUMNS
    FROM
    [Adventure Works]


Si las ejecutamos, ambas producen resultados similares… una sobre el sistema OLTP y la otra sobre el sistema OLAP. Vaya, empezamos a entender… ¡si ambos sistemas producen resultados similares, eso debe significar que nuestro sistema OLAP funciona correctamente! Eso es el Data Driven Unit Testing, y nos vendrá de miedo para probar nuestros cubos. Recordemos que el escenario habitual en un desarrollo de BI será partir de un sistema existente, así que… ¿qué mejor prueba de que nuestro sistema funciona, que demostrar que devuelve los mismos resultados que el sistema de partida?. Cuantos más cruces de dimensiones comparemos con salidas del sistema OLTP, mejor será nuestra batería de tests.


Es difícil, por no decir imposible, tener una cobertura del 100% del cubo, pero como se suele decir, testear algo es infinitamente mejor que no testear.


Data Driven Unit Test en Visual Studio Team System


Para realizar testeo unitario guiado por datos, tenemos que partir de una tabla de base de datos en la que tendremos almacenado cada uno de nuestros juegos de pruebas. En el ejemplo que he puesto, si no disponemos de tabla, tendremos que crear una vista.



  • CREATE VIEW Test.vOrderQuantityPerProductKey AS
    SELECT
    Production.Product.ProductNumber AS Product,
    SUM(Sales.SalesOrderDetail.OrderQty) AS Quantity
    FROM Sales.SalesOrderDetail, Production.Product
    WHERE Sales.SalesOrderDetail.ProductID = Production.Product.ProductID
    GROUP BY Production.Product.ProductNumber

Este es el punto que menos me gusta, tener que partir obligatoriamente de una tabla accedida mediante OleDB y no poder partir de una sentencia SQL… pero bueno, sigamos a lo nuestro. Nos ponemos las botas de programar, nuestra música favorita, y creamos un nuevo proyecto de tipo «Test Project».



Visual Studio creará por nosotros una clase de test, con un método de test. Tendremos que indicar que el método de test es «Data Driven», para eso, editamos las propiedades del test e indicamos:



  • Data Connection String: Cadena de conexión a nuestra base de datos.

  • Data Table Name: El nombre de la tabla donde se encuentran los valores a probar, junto con los resultados esperados.

  • Data Access Method: Squential. Con esto indicamos que se deberá probar secuencialmente con todos y cada uno de los valores de la tabla.



Como resultado de la edición de estas tres propiedades, Visual Studio decorará nuestro método de test con un atributo DataSource, señal clara de que nuestro test es guiado por datos.


        [DataSource(«System.Data.SqlClient»,
            «Data Source=.;Initial Catalog=AdventureWorks;Integrated Security=True»,
            «Test.vOrderQuantityPerProductKey»,
            DataAccessMethod.Sequential),
            TestMethod()]


Rematando la faena: escritura del test


Escribir el test unitario es sencillo… lo primero que tenemos que hacer es tener una propiedad de tipo TestContext, llamada TestContext. En esta propiedad, Visual Studio nos irá dejando el contexto de cada test, es decir, en nuestro caso, los valores para cada uno de los juegos de prueba.


        private TestContext _myTestContext;


        public TestContext TestContext
        {
            get { return _myTestContext; }
            set { _myTestContext = value; }
        }


Lo siguiente es escribir el test:



        public void OrderQuantityPerProductKeyTestMethod()
        {
            // Construimos la sentencia MDX para el producto actual, y obtenemos la cantidad del cubo.
            String mdxQuery = «SELECT [Measures].[Order Quantity] ON COLUMNS »  
                              + «FROM [Adventure Works] WHERE [Product].[Product Key].[«
                              + TestContext.DataRow[«Product»]
                              + «]»;
            AdomdCommand mdxQueryCommand = new AdomdCommand(mdxQuery, _adomdConnection);
            CellSet cubeResult = mdxQueryCommand.ExecuteCellSet();


            // Comparamos el resultado del cubo, con el resultado de la base de datos.
            int result = (int)cubeResult.Cells[0].Value;
            int expected = (int)TestContext.DataRow[«Quantity»];
            Assert.AreEqual(expected, result, 16);


        }


Como veis, me limito a ejecutar una sentencia MDX que me devuelva de los cubos OLAP el valor equivalente al del juego de ensayo. Para obtener el juego de ensayo, leo del diccionario «DataRow» del TestContext cada una de las columnas de mi tabla de pruebas, y por último pruebo con un Assert que los resultados del cubo sean los esperados.


La invocación de Assert.AreEqual tiene un tercer parámetro que vale 16… ¿qué significa?


Este parámetro, es el «delta» de la función. Muchas veces, cuando metemos datos en un cubo por medio de un ETL, durante el proceso de limpieza se pierde precisión en los datos. Como resultado de la limpieza, los valores extraidos de la base de datos OLTP y OLAP podrían no coincidir… pues bien, el «delta», es la desviación que toleraremos en nuestra prueba (en este caso, +/- 16 enteros). Podéis descargaros el proyecto completo que he adjuntado a este post, y experimentar todo lo que queráis (por ejemplo, probad a bajar el valor del delta y observad qué pasa…).


Despidiendo el post


Hemos visto cómo los test unitarios no están de más en el mundo de la inteligencia de negocios… y recordad que tal y cómo dice SCRUM, un proyecto no está terminado mientras no esté probado.


Happy testing!

RadarCube for Analysis Services (o cómo el ratón a veces rinde más que el puma)

Ahora que los Office Web Control ya son historia, Microsoft nos ofrece MOSS 2007 y su espectacular Excel Services como solución de representación de datos multidimensionales. Hoy lo he estado probando y ver cómo se muestran en la web las hojas de trabajo de Excel 2007 por obra y arte de AJAX es espectacular (hablo de la tecnología, no de cierto producto de limpieza) . Si tenemos Sharepoint implantado en nuestra empresa, no cabe duda de que sería una locura no aprovecharlo para representar nuestros tableros de mando… ¿pero merece la pena implantar Microsoft Office Sharepoint Server 2007 si sólo queremos representar tableros de mando ¿Necesitamos toda esta artillería para cazar perdices? Sharepoint es probablemente la solución que nos permite realizar un "Dashboard" en el menor tiempo, además de ser una de las soluciones más versátiles y personalizables que he visto en mi vida. Un auténtico puma.

Sin embargo, tiene algunos aspectos que pueden restarle puntos a la hora de convertirse en la estrella de nuestros proyectos:

  • MOSS 2007 es complejo de desplegar y administrar. Bueno, esta es la opinión de un total principante en Sharepoint, pero a pesar de lo que nos diga Microsoft, no parece que sea la mejor opción para una solución "out of the box" (aunque sólo sea por el número de servicios que tenemos que activar y desplegar). No olvidemos que se trata de un CMS completo, y que el "Excel Services" sólo es un pequeño apéndice del todo.
  • El coste de la licencia podría ser prohibitivo para pequeñas empresas. Todavía no he encontrado ninguna aclaración de lo que podría costar, pero si nos ponemos en los precios de Sharepoint 2003, la broma podría costarnos cerca de los 1500$ – 3000$ por implantación (dependiendo de los tipos de licencia que elijamos… etc)
    (http://reviews.cnet.com/Microsoft_Office_SharePoint_Portal_Server_2003/4014-3526_7-30541138.html)
  • Requiere una máquina potente. Recomiendan que se instale en una máquina con 2Gb de memoria. Yo lo he instalado en un virtual server con 640Mb asiganados a la máquina virtual, y creedme que en mi equipo con 1 Gb de memoria física se arrastra.
  • Requiere Excel 2007 para publicar nuestras pivot table: al final Excel Services representa los datos de una hoja Excel, con lo que estamos condenados a que Excel sea nuestra herramienta de desarrollo de "dashboards" (cierto es que Excel es el software preferido de los analistas de negocios, así que en cierto modo esta desventaja no es muy seria).

A veces, en proyectos pequeños, nos sobra el puma y nos basta con un ratón. Buscando ese ratón, he dado con el nuevo RadarCube ASP.NET for Microsoft Analysis Services. Es algo menos espectacular que Excel Services, pero a cambio nos ofrece:

  • Precios de 450$ para un único desarrollador y servidor, 2500$ en una versión libre de royalties para un equipo de desarrollo no distribuido, y 5000$ en versión ilimitada, con fuentes. ¡Calderilla para un desarrollador de Bilbao!
  • Posibilidad de conectarse a SSAS o a cubos locales (archivos "cub")
  • Informes preconfigurados, o interfaz de tipo "pivot table".
  • Posibilidad de conectarse con los controles gráficos de Dundas, para representar de forma gráfica los datos seleccionados en el control
  • Apariencia personalizable por medio de hojas de estilo

El control de RadarSoft es para ASP.NET 2.0, soporta Ajax, y presenta este aspecto:

Bueno, en la web del producto encontraréis ejemplos mucho más espectaculares que el mío (no he dedicado demasiado tiempo a "embellecer" el ejemplo). Podéis consultarla en:
http://www.radar-soft.com/products/radaraspnet_msas.aspx

Como puntos negativos que le he visto a la versión 1.03:

  • Fallitos al representar las "Measures" estructuradas con "DisplayFolders" (carpetas). No graves: en la base de datos de AdventureWorks los DisplayFolders los coge en inglés (a pesar de estar la máquina configurada en castellano). En una base de datos mía, con metadatos en castellano e inglés, cogía correctamente los metadatos en, pero sólo me cogía el primer nivel de carpetas (no subcarpetas). Los hechos más allá del primer nivel de subcarpetas, me los mostraba en la raíz.
  • Cuando una dimensión tiene un único nivel, no muestra dimensión y nivel… sino que muestra directamente el nivel de la dimensión. Es un pelín raro, aunque uno se acostumbra.
  • En tiempo de diseño, deja establecer la propiedad "Cube" por medio de un ComboBox. Pues bien, este combo, mezcla cubos, perspectivas y dimensiones… hay que tener cuidado al seleccionar algo que sea un cubo o una perspectiva.
  • En general… es una primera versión, y tiene algunos signos de inmadurez, pero no es nada que parezca especialmente grave.

Bueno, pues aunque os parezca mentira, RadarSoft no me ha pagado por escribir esto… La moraleja que quería transmitir es que es el proyecto quien manda, y es el proyecto quien nos dirá si se requiere un puma, un ratón, o si tenemos que arreglarnos con un jilguero con careta de rapaz. Así que desde aquí animo a las empresas a que produzcan controles de representación de datos multidimensionales: es un segmento del mercado en el que todavía cabe mucha fauna, y en el que nosotros los desarrolladores, sabemos apreciar la diversidad.

UDM, posiblemente lo mejor de Analysis Services 2005

El salto cualitativo que ha dado Analysis Services es tal, que cuando uno trata de elegir su característica favorita lo puede tener francamente complicado. Hay nuevos algoritmos de minería de datos, tiene la posibilidad de tener cubos con varias tablas de hechos, las "linked measures", los ETL con flujos de datos y flujos de control separados… sin embargo, si me preguntasen cual es para mi la mejor característica de SSAS 2005 no dudaría ni un segundo en responder que en el "UDM".

El UDM, o Unified Dimensional Model, es una capa intermedia que permite a SSAS abstraerse de la fuente de datos en la que se almacenan los hechos y dimensiones de un cubo. De esta forma, ya no necesitamos disponer físicamente de una base de datos con un modelo relacional en estrella, sino que podemos partir de cualquier modelo relacional y construir una "estrella virtual" sobre el UDM. Esta estrella virtual es algo parecido a si organizásemos una estrella en base a vistas sobre una base de datos relacional, pero tiene la ventaja de que puede tomar datos desde distintos orígenes.

 

Parece sencillo, ¿verdad? ¿y por qué es a mi entender, la mejor característica de SSAS2005? Puede que no parezca una característica revolucionaria, pero el UDM nos abre las puertas de la "minería de datos operacional".

Tradicionalmente, las aplicaciones basadas en tecnología OLAP se han utilizado para el análisis de datos históricos: existía un entorno OLTP, un Data Warehouse y un proceso batch, llamado ETL, que transladaba datos de uno a otro. Los cubos OLAP se montaban sobre los datos del Data Warehouse, y por lo tanto estaban disponibles para su análisis únicamente tras la ejecución del ETL (y en el caso de los cubos MOLAP, tras el procesado de los cubos, en los que se cargan los distintos niveles de dimensiones y se construye la base de datos de agregados).

Con UDM, podemos prescindir del Data Warehouse y tomar los hechos directamente del almacén OLTP (de ahí el nombre de "minería de datos operacional"). Incluso podemos tomar datos de varios almacenes OLTP, de una combinación de almacenes OLTP y DataMarts, o por qué no, de datos obtenidos en tiempo real que pudiesen representarse en forma de tabla (cualquier origen OLEDB o ADO.NET puede aportar tablas a nuestro modelo UDM).

 

Una vez configurados todos los orígenes de datos (DataSources) podremos unificarlos y darles el aspecto que queramos utilizando un DataSourceView, en el que podremos:

  • Relacionar tablas
  • Establecer vistas
  • Establecer campos calculados

¿Qué ventaja obtenemos con esto? Sencillo… el UDM nos permite romper con la barrera del tiempo y realizar análisis sobre los datos según se producen. Por supuesto, esto tiene sus implicaciones de rendimiento y su ciencia particular: tendremos que usar ROLAP o HOLAP (configurando adecuadamente la Proactive Cache) para prescindir del procesado de cubos en batch al que nos obliga MOLAP. ¡Uau! ¡Cubos OLAP sin procesos batch! En resumen UDM es una verdadera "revolución silenciosa" que abre las puertas del OLAP en tiempo real.

Business Intelligence y privacidad

El otro día, mientras probaba unos cubos OLAP en la oficina, alguien se me acercó y curioseó a mi espalda. Increíble… estaba cruzando dimensiones y generando informes que hasta ahora las aplicaciones corporativas no podían obtener. Por arte de magia, estaba cruzando averías surgidas en un conjunto de máquinas con los operarios que las manejaban en ese momento. Por supuesto se trataba de datos falsos y para mi sólo era una prueba explorando las capacidades de los nuevos cubos… pero quien estaba tras de mí no pudo dejar de recordarme que yo era poco menos que el demonio.

Desde entonces he pensado mucho en el tema. Curiosamente, los grandes volúmenes de información que se tiene de nosotros, garantizan en cierta forma nuestro anonimato. Esta es una afirmación un tanto chocante y que a más de uno le puede hacer chirriar las tuercas, pero no deja de ser por eso menos verdadera. Todos los días generamos cientos de "pedacitos" de información que van quedando en uno y otro lugar, entremezclados con los pedacitos de información que han dejado otros. El volumen de datos es tal, que es imposible obtener información de ellos, sencillamente porque el bosque es tan inmenso y cerrado, que nos es imposible encontrar un árbol concreto. Pero vaya… las tecnologías de Business Intelligence nos facilitan  acceder al árbol que buscamos… ¡y a todos sus parientes genéticos! ¡y con un par de clicks de ratón!

Mi primer pensamiento fue…

Bien, con mi sistema, se puede se puede poner nombre y apellidos a información que de otra forma serían puras estadísticas anónimas. Así que podemos saber que Serapio es más hábil manejando una fresadora que Fulgencio, y que a Fulgencio se le estropea cada dos por tres. ¿Perjudica esta información a Serapio y Fulgencio? Evidentemente, perjudica a Fulgencio y Beneficia a Serapio. ¿Pero es esto malo? ¿Soy el demonio?

Mmm… el sistema beneficia a quien lo hace bien, y perjudica a quien lo hace mal. Además lo hace basándose en una enorme cantidad de variables, y de forma totalmente imparcial. A mi me parece bastante justo, ¿no? Ese día pude dormir tranquilamente, a sabiendas de que mi sistema, en buenas manos, sería una herramienta que permitiría impartir justicia.

Pero esa noche tuve una pesadilla… ¿En buenas manos?

Al día siguiente, al llenar el depósito de mi coche, pasé la "Travel" en mi gasolinera habitual… y pensé, ¡allá va mi pedacito de información!. Pero me quedé inquieto. ¿Irá ese pedazo de información a buenas manos? ¿Qué se podría hacer con esa información? Amigos míos… ¿imagináis qué se puede hacer con las tecnologías actuales de business intelligence sobre la base de datos de "Travel Club"? Y por supuesto, no es sólo la Travel… son mis "tracking cookies" cuando navego por Internet, mi "historial" en los distintos sitios web en los que me autentico con un Microsoft Passport (o Windows Live Id) y un montón de actividades que realizo a diario. Muchos, muchos, muchos pedacitos de información.

No me considero el demonio… ¡pero estoy seguro de que la misma tecnología sobre la que escribo en este blog, es un arma de destrucción masiva en manos del Demonio! De hecho, me pregunto qué sería de las empresas que desarrollan spyware sin la ayuda del "business intelligence".

Y llegamos al punto que quería atacar… ¿quién, y sobre todo cómo, me protege de los abusos? Más de uno dirá, ¡para eso está la legislación, amigo! Entonces comparo mi trabajo con el de quien explota información obtenida del spyware, y… ¡vaya! Realmente en algunos casos los hechos pueden ser tremendamente similares, y las tecnologías que empleamos muy parecidas. Realmente nos diferencian las intenciones… ¡pero es que se juzgan los hechos, y no las intenciones!

Bueno, todo esto me ha hecho empatizar con los legisladores… uff, ¡qué difícil tiene que ser legislar contra el spyware! Y me surge otra pregunta… ¿y si alguien utiliza un sistema creado con buena fe, para obtener información que se podría emplear con mala fe… el desarrollador del sistema es responsable en parte?

No me ha quitado demasiado el sueño, porque sigo pensando que la tecnología es éticamente neutra, y que lo moral o inmoral es el uso que se hace de ella. Seguro que los metereólogos podrían sacar chispas al "business intelligence". Mi conciencia está tranquila. Pero los años me recuerdan que habitualmente, las leyes van tres pasos por detrás de la moral.

Paths relativos en Integration Services

Uno de los aspectos más molestos de SSIS, es el hecho de que no permita utilizar rutas relativas en las cadenas de conexión a archivos. Esto es un problema si queremos realizar un ETL que se vaya a desplegar en una ruta distinta a la utilizada en tiempo de diseño… pero por fortuna, aunque un poco rebuscado, hay un truco que permite utilizar paths relativos.

Atended pues, a esta receta con la que podréis desarrollar un ETL rico, rico, rico… que os permita:

  • Utilizar archivos como orígenes de datos sin tener que colocarlos en rutas fijas
  • Lanzar archivos "dtsx" que se almacenen en el directorio en el que se está ejecutando el ETL
  • … etc, etc, etc…

Vamos pues, paso a paso con el truquillo:

  1. Lo primero que hay que hacer es definir una variable con el path en el que se desplegará el ETL. Definidla de forma que sea global al archivo "dtsx" principal en el que se haya implementado el ETL. Ya sabéis, se va a la pestañita de "Control Flow" y se define la varible desde allí.
  2. Cuando creamos una conexión a un archivo, Integration Services nos obliga a establecer como cadena de conexión, la ruta absoluta a un archivo existente. Pues bien, satisfaremos los deseos de SSIS haciendo que la conexión apunte a un archivo existente.
  3. Seleccionamos “Propiedades” de la conexión sobre la que queremos establecer la ruta relativa.
  4. Seleccionar “Expresiones” (pinchamos en los tres puntitos al lado de "expressions" en la ventana de propiedades).

  5. Seleccionar “ConnectionString”

  6. Establecer una expresión que construya un path absoluto en función de la variable con el path de despliegue: en mi ejemplo, @[User::DeploymentPath] + "\Data\DayOfWeekNameValues.txt". Esta expresión, se resolverá en tiempo de ejecución y sobreescribirá el valor de la propiedad que hubiésemos establecido en tiempo de diseño.

  7. Por último, deberemos propagar el valor de la variable a paquetes hijos, por medio de la opción "SSIS-> Package Configurations". De todas la opciones disponibles, seleccionaremos la opción de tomar una configuración de una variable desde el paquete padre.

  8. La variable que marca la ruta al paquete, también puede configurarse desde "SSIS-> Package Configurations". La opción más adecuada en este caso, es tomar su valor de una entrada de registro, o de una variable de entorno (consultad los libros en pantalla, que las entradas de registro tienen que tener un formato específico).

Bueno, espero que os haya gustado mi primer post tras la vuelta de vacaciones. Os deseo un buen comienzo de curso, y ya sabéis… ¡felices transformaciones!