Probando Firebird Embedded con C#

Desde hace mucho tiempo tengo planeado desarrollar una aplicación cuya versión 0.1 desarrollé hace ya varios años en C++ y Fox Toolkit, perdida ya en el museo del olvido.

La aplicación es muy sencilla.  Inicialmente son dos módulos pero la idea es agregarle después algunos otros.  El problema es que cada vez que me siento a pensar en ella termino haciendo un diseño complejo y lleno de cositas que me pondrían a estudiar.  Esto no sería malo sino fuera porque de esta manera se va a terminar cuando el tiempo tienda a infinito.

Por esto he cambiado mi plan.  Voy a hacer una aplicación pequeña y la voy a ir hacer creciendo, aunque esto signifique que alguna versión deba volverla a hacer desde scratch (cero).

Con respecto al lenguaje de programación había elegido Java para retomar mi estudio pero en medio de la moda local y temporal de .NET he decidido empezar a implementar en C#.

El primer reto a resolver es la escogencia de un motor de base de datos ya que la aplicación va a ser pequeña, me interesa que se pueda ejecutar en equipos no necesariamente robustos, no quiero despilfarrar recursos y quiero que la instalación y copia de seguridad sean fáciles: sólo copiar archivos.  Por esto he pensado en utilizar una base de datos basada en archivos o embedded.

Para Java hay varias de donde escoger, pero para .NET la oferta, al parecer, no es tan amplia.  Encontré SQLite (ya lo he utilizado varias veces pero he tenido problemas con bloqueos) VistaDB (comercial, no me sirve) y a SharpHSQL hermanita de HSQL (Java) para .NET, sin embargo no estoy seguro de cuan activo está ese proyecto.  Las mejores opciones por ahora parecen ser FireBird Embedded y SQL Server Compact, ambas prometen muchas cosas, incluyendo un bajo footprint ~ 2MB.

Para la primera prueba elegí a FireBird ya que esa fue la primera base de datos que utilicé en un proyecto después de graduado como ingeniero y me unen a ella emotivos recuerdos 🙂

Instalación.

Para desarrollar una aplicación con FireBird inmerso se requieren dos recursos.

El FirebirdClient se instala como cualquier aplicación Windows sin ninguna opción específica.  Me imagino que habrá que instalarlo también en los clientes cuando se haga el despliegue (por confirmar).

La distribución se debe descomprimir y mover los siguientes archivo al mismo directorio donde se encontrará el archivo ejecutable de la aplicación: <ruta>binDebug (desarrollo) o <ruta>binRelease (producción).

  • fbembed.dll
  • icudt30.dll
  • icuin30.dll
  • icuuc30.dll
  • firebird.conf
  • firebird.msg

Para la implementación del proyecto estoy utilizando Visual C# Express 2008 y en él es necesario incluír la referencia a FirebirdSql.Data.FirebirdClient instalada con el Data Provider.

Creación de la cadena de conexión.

Es fácil construír la ConnectionString con la ayuda del FbConnectionStringBuilder de la siguiente manera.

            FbConnectionStringBuilder csb = new FbConnectionStringBuilder();

            csb.ServerType = FbServerType.Embedded;
            csb.UserID     = "SYSDBA";
            csb.Password   = "masterkey";
            csb.Dialect    = 3;
            csb.Database   = @"datadatabase.fdb";
            csb.Charset    = "UTF8";

Sólo los parámetros ServerType y Database son obligatorios, los demás son opcionales y puede encontrarse mayor información sobre ellos en ConectionStringParameters, también se puede encontrar mayor información acerca de los Charset disponibles en Firebird Character Sets and Collations.

La ubicación de la base de datos (parámetro Database) es relativo a la ubicación del archivo fembed.dll, es decir, al ejecutable de la aplicación.

Creación de la base de datos.

Desde la misma aplicación es posible crear la base de datos (manera programática) a la cual hace referencia la cadena de conexión.

           FbConnection.CreateDatabase(csb.ToString());

Debe tenerse en cuenta que la ruta de directorios bajo la cual se ubicará la base de datos ya debe existir previa la creación del archivo de datos, de lo contrario la creación fallará.

Conexión a la base de datos.

          con = new FbConnection(csb.ToString());
          con.Open();

Verificación del estado de la conexión.

            if (con.State == System.Data.ConnectionState.Open)
                Console.WriteLine("Established");
            else
                Console.WriteLine("NOT established");

Otros posibles estados definidos por ConnectionState son Broken, Closed, Connecting, Executing y Fetching.

Verificación de la existencia de una tabla.

            FbCommand cmd = new FbCommand("SELECT COUNT(RDB$RELATION_NAME)" +
                                          "FROM RDB$RELATIONS WHERE (RDB$RELATION_NAME = 'users')" +
                                          "AND RDB$VIEW_SOURCE IS NULL", con);

            int tableCount = (int)cmd.ExecuteScalar();

            if (tableCount == 0)
                Console.WriteLine("No");
            else
                Console.WriteLine("Yes");

Verifica la existencia o no de la tabla users en la base de datos conectada.

Ejecución de un INSERT.

            String sql = "INSERT INTO "users" VALUES ('" + user[0] + "', '" + user[1] + "')";
            cmd = new FbCommand(sql, con);
            cmd.ExecuteNonQuery();

Ejecución de una consulta de datos.

            String select = "SELECT username, name FROM "users"";
            cmd = new FbCommand(select, con);
            FbDataReader reader = cmd.ExecuteReader();

            try
            {
                while (reader.Read())
                {
                    String username = reader.GetString(0).Trim();
                    String name = reader.GetString(1).Trim();

                    Console.WriteLine("t{'" + username + "', '" + name + "'}");
                }
            }
            finally
            {
                reader.Close();
            }

Finalización de la conexión.

            con.Close();

Conclusiones.

Esta primera aproximación fue sencilla, práctica y funcional.  Se encuentra buena documentación del motor de base de datos y movimiento en los foros en su respecto.  A pesar de que, al menos por estas latitudes, no es muy utilizada Firebird parece ser que en otras partes si lo es.

Los archivos requeridos (dlls) ocupan 2.87MB mientras que todos los archivos, incluyendo los opcionales, ocupan 4.94MB.

Voy a revisar también cuan viable es utilizar el SQL Server Compact, sin embargo en los últimos días que hemos tenido un poco de contacto con SQL Server han aparecido con él algunos detalles desagradables que desde mi punto de vista de conocer poco acerca de este motor, me desaniman de utilizarlo.  Ya veremos.

Enlace: Aplicación de prueba – Firebird Embedded Demo 0.1.

Serialización de objetos en XML con C#.NET

De manera análoga a como en Java es posible realizar la serialización de clases a XML, encontré que también es posible hacerlo en C#.NET.  Para esto es necesario que la clase a ser serializada exponga los atributos que desea publicar a través de propiedades.

Para la demostración creé una clase XMLSerializable de la cual heredarán las otras que requieran de este tipo de serialización.  Esta clase abstracta provee los métodos load y save para realizar automáticamente la serialización de sus hijos.

La serialización de los objetos se realiza con el siguiente código.

        public void save(String filename)
        {
            // Creates the writing stream

            StreamWriter w = new StreamWriter(filename);

            // Creates the XML serializer with its type

            XmlSerializer s = new XmlSerializer(this.GetType());

            // Serializes the object (this self -> son)

            s.Serialize(w, this);

            // Closes the output stream

            w.Close();
        }

La carga de objetos serializados requiere de un par de verificiones adicionales.

        public bool load(String filename)
        {
            bool success = false;

            // Checks if the source file exists, otherwise there is nothing to do

            if (File.Exists(filename))
            {
                // Creates the reading stream

                StreamReader sr = new StreamReader(filename);

                // Creates the text reader from the stream

                XmlTextReader xr = new XmlTextReader(sr);

                // Creates the XML (de)serializer with its type

                XmlSerializer xs = new XmlSerializer(this.GetType());

                // Temporary storage object

                object c;

                // Checks if the received data can be deserialized

                if (xs.CanDeserialize(xr))
                {
                    // Deserialize the incoming data

                    c = xs.Deserialize(xr);

                    // Gets the type (definition) of the object

                    Type t = this.GetType();

                    // Retrieve the exposed attributes by properties

                    PropertyInfo[] properties = t.GetProperties();

                    // Walks thru all properties and loads its values on
                    // the local attributes

                    foreach (PropertyInfo p in properties)
                    {
                        p.SetValue(this, p.GetValue(c, null), null);
                    }

                    success = true;
                }

                // Closes the reader and the stream

                xr.Close();
                sr.Close();
            }

            return success;
        }

Con estas facilidades se crea una clase Empleado con atributos básicos.

    public class Empleado : XMLSerializable
    {
        ////////////////////////////////////////////////////////////////

        private int      codigo;
        private String   nombre;
        private float    salario;
        private DateTime fechaNacimiento;

        ////////////////////////////////////////////////////////////////

        // Resto del codigo
    }

Es necesario para la serialización que el objeto tenga propiedades de los atributos a exponer.

        public int Codigo
        {
            get { return codigo; }
            set { codigo = value; }
        }

        public String Nombre
        {
            get { return nombre; }
            set { nombre = value; }
        }

        public float Salario
        {
            get { return salario; }
            set { salario = value; }
        }

        public DateTime FechaNacimiento
        {
            get { return fechaNacimiento; }
            set { fechaNacimiento = value; }
        }

También es necesario que la clase cuente con un constructor sin parámetros además de los que requiera normalmente.

        public Empleado()
        {
            this.codigo          = -1;
            this.nombre          = "Sin nombre";
            this.salario         = -1;
            this.fechaNacimiento = new DateTime(1990, 01, 01);
        }

        ////////////////////////////////////////////////////////////////

        public Empleado(int codigo, String nombre, float salario, DateTime fechaNacimiento)
        {
            this.codigo          = codigo;
            this.nombre          = nombre;
            this.salario         = salario;
            this.fechaNacimiento = fechaNacimiento;
        }

Por facilidad también se sobreescribió el método ToString() para generar su representación en cadena.

        override public String ToString()
        {
            return String.Format("[Empleado: codigo = {0}, nombre = "{1}", salario = ${2}, fechaNacimiento = {3}]",
                                    this.codigo, this.nombre, this.salario, this.fechaNacimiento);
        }

Con todo esto, la aplicación de prueba crea en el primer paso a un objeto Empleado y le da valores iniciales a sus atributos, después de esto solicita su serialización.

            Empleado empleado = new Empleado(123, "Pepito Pimentón", 123456.789f, DateTime.Now);
            empleado.save("empleado.xml");

Posteriormente se crea un segundo Empleado que almacenará la información recuperada de la serialización.  Si la prueba tiene éxito, la información de los dos Empleados deberá coincidir.

            Empleado empleado2 = new Empleado();
            empleado2.load("empleado.xml");

La información presentada a través de salida estándar al ejecutar la aplicación de demostración deberá ser algo similar a lo siguiente.

Writing Object: [Empleado: codigo = 123, nombre = "Pepito Pimentón", salario = $123456,8, fechaNacimiento = 14/09/2008 11:12:10 p.m.]
Read Object:    [Empleado: codigo = 123, nombre = "Pepito Pimentón", salario = $123456,8, fechaNacimiento = 14/09/2008 11:12:10 p.m.]

El documento XML generado es bastante explícito y fácil de realizar su manipulación manual si se llegara a requerir.



  123
  Pepito Pimentón
  123456.789
  2008-09-14T22:39:52.888-05:00

Enlaces:

Microsoft Kid's Corner

Buscando información en internet para el próximo capítulo de la serie de documentos J2ME encontré algo interesante del otro lado de la de las plataformas de desarrollo.

Microsoft Kid’s Corner es una sección de documentación para enseñarle a programar a niños pequeños utilizando, obviamente, las herramientas de .NET que el mismo Microsoft desarrolla.

Sin profundizar en estos temas por ahora, se encuentran ya disponibles dos libros electrónicos acerca de la programación con C# y VB, así como varios cursos didácticos relacionados con estas tecnologías y haciendo énfasis en el desarrollo de aplicaciones de escritorio y aplicaciones web.

Para algo podrán servir en el futuro estos documentos, todo lo relacionado con educación es siempre bienvenido.

Enlace: Microsoft Kid’s Corner.

Servicio web entre C#.NET y Java

Aproveché este fin de semana para en un par de horas desarrollar un ejemplo de servicio web escrito en C#.NET para ser consumido por un cliente desarrollado en Java, utilizando las herramientas gratuitas Microsoft Visual Web Developer 2008 Express Edition y NetBeans 6.0.1 respectivamente.

El servicio como tal es muy simple, envía correo a través de un servidor SMTP, sin embargo es un ejercicio académico interesante. La documentación del desarrollo la escribí a manera de guía así que podrá ser de utilidad para quienes esten interesados en aprender a desarrollar este tipo de aplicaciones.

Enlaces:

  1. Guía de desarrollo
  2. Código fuente.

ASP.NET Development Server desde línea de comando

El ASP.NET Development Server es el pequeño servidor ASP.NET que trae consigo el Framework de .NET y el Visual Studio Express para realizar la depuración y pruebas de este tipo de aplicaciones. Esta semana descubrí que es posible ejecutarlo desde la línea de comando, es decir, no es necesario iniciar el Visual Web Developer Express completo para utilizarlo. Para esto se debe ejecutar el siguiente comando en una ventana de Símbolo del sistema.

prompt> set DEVSRV=C:Program FilesCommon Filesmicrosoft sharedDevServer9.0
prompt> "%DEVSRV%"webdev.webserver.exe /port:8080 /path:"RUTA_PROYECTO" /vpath:/SERVICIO

Utilizo la variable %DEVSRV% para evitar digitar la ruta del servidor (webdev.webserver.exe) en cada llamado. Aparentemente su ubicación puede variar según la versión del .NET Framework que se tenga instalada.

En la segunda línea se debe reemplazar la cadena RUTA_PROYECTO con la ubicación del serivicio web: G:ProyectosWS_Tallerservicio por ejemplo y la cadena SERVICIO por la ruta virtual web bajo la cual quedará publicada el webservice. Si no se especifica un puerto se utiliza el puerto 80 por defecto.