jueves, 3 de julio de 2014

El Demo del Día: Convertir Lista de Objetos en un Archivo DBF y Viceversa

Convertir Lista de Objetos en un Archivo DBF y Viceversa

Requerimiento

Se desea leer datos de una Base de Datos de SQL Server y a partir de estos crear un archivo DBF.

Solución

Usamos una Lista de Objetos para recibir los datos de SQL Server y luego usamos OLEDB y Reflection para crear una tabla e insertar los registros en esta.

Crear la Clase con los Métodos de Conversión

Crear una aplicación Windows Forms en C# llamada "Exportar_DBF" y luego crear una clase con 2 métodos estáticos, uno que genere un archivo DBF a partir de una lista de objetos y otro que a partir de un archivo DBF genere una Lista de Objetos, tal como se muestra en el siguiente código:

using System;
using System.Text; //StringBuilder
using System.Collections.Generic; //List<T>
using System.Reflection; //PropertyInfo, Assembly
using System.Data; //CommandBehavior
using System.Data.OleDb; //OledbConnection, OledbCommand, OleDbDataReader
using System.IO; //File, Path

namespace Exportar_DBF
{
    public class ucListaDBF<T>
    {
        public static void ListaADBF(List<T> lista,string nombreTabla)
        {
            StringBuilder sbCreate = new StringBuilder();
            sbCreate.Append("Create Table ");
            sbCreate.Append(nombreTabla);
            sbCreate.AppendLine(" (");
            PropertyInfo[] propiedades = lista[0].GetType().GetProperties();
            for (int i = 0; i < propiedades.Length; i++)
            {
                sbCreate.Append(propiedades[i].Name);
                sbCreate.Append(" ");
                sbCreate.Append(convertirTipo(propiedades[i].PropertyType.ToString()));
                if (i < propiedades.Length - 1) sbCreate.AppendLine(",");
            }
            sbCreate.AppendLine("");
            sbCreate.AppendLine(")");

            string ruta = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            string archivo = string.Format("{0}\\{1}.dbf", ruta, nombreTabla);
            if (File.Exists(archivo)) File.Delete(archivo);
            using (OleDbConnection con = new OleDbConnection
                ("provider=Microsoft.Jet.Oledb.4.0;data source=" + ruta + ";
                  extended properties=dBase IV"))
            {
                con.Open();
                OleDbCommand cmd = new OleDbCommand(sbCreate.ToString(), con);
                cmd.ExecuteNonQuery();

                StringBuilder sbInsert = new StringBuilder();
                for (int j = 0; j < lista.Count; j++)
                {
                    sbInsert.Clear();
                    propiedades = lista[j].GetType().GetProperties();
                    sbInsert.Append("Insert Into ");
                    sbInsert.Append(nombreTabla);
                    sbInsert.Append(" Values (");
                    for (int i = 0; i < propiedades.Length; i++)
                    {
                        if (propiedades[i].PropertyType.ToString().Contains("String")
                            || propiedades[i].PropertyType.ToString().Contains("DateTime"))
                            sbInsert.Append("'");
                        sbInsert.Append(propiedades[i].GetValue(lista[j], null).ToString().Replace("'",""));
                        if (propiedades[i].PropertyType.ToString().Contains("String")
                            || propiedades[i].PropertyType.ToString().Contains("DateTime"))
                            sbInsert.Append("'");
                        if (i < propiedades.Length - 1) sbInsert.Append(",");
                    }
                    sbInsert.AppendLine(")");
                    cmd.CommandText = sbInsert.ToString();
                    cmd.ExecuteNonQuery();
                }
            }
        }

        private static string convertirTipo(string tipoNET)
        {
            string tipoOLEDB = "";
            tipoNET = tipoNET.ToLower();
            if (tipoNET.Contains("int")) tipoOLEDB = "Long";
            else
            {
                if (tipoNET.Contains("decimal")) tipoOLEDB = "Double";
                else
                {
                    if (tipoNET.Contains("datetime")) tipoOLEDB = "DateTime";
                    else
                    {
                        tipoOLEDB = "Text(100)";
                    }
                }
            }
            return (tipoOLEDB);
        }

        public static List<T> DBFALista(string nombreTabla)
        {
            List<T> lista = new List<T>();
            Type tipo = typeof(T);
            string ruta = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
            using (OleDbConnection con = new OleDbConnection
                ("provider=Microsoft.Jet.Oledb.4.0;data source=" + ruta + ";
                  extended properties=dBase IV"))
            {
                con.Open();
                OleDbCommand cmd = new OleDbCommand("Select * From " + nombreTabla, con);
                OleDbDataReader drd = cmd.ExecuteReader(CommandBehavior.SingleResult);
                if (drd != null)
                {
                    object obe = null;
                    string tipoDato;
                    PropertyInfo propiedad;
                    while (drd.Read())
                    {
                        obe = Activator.CreateInstance(tipo);
                        for (int i = 0; i < drd.FieldCount; i++)
                        {
                            propiedad = obe.GetType().GetProperty(drd.GetName(i).ToLower(),
                                BindingFlags.IgnoreCase|BindingFlags.Public|BindingFlags.Instance);
                            tipoDato = drd[i].GetType().ToString().ToLower();
                            if (tipoDato.Contains("int16")) propiedad.SetValue(obe, drd.GetInt16(i));
                            else
                            {
                                if (tipoDato.Contains("int32")) propiedad.SetValue(obe, drd.GetInt32(i));
                                else
                                {
                                    if (tipoDato.Contains("decimal")) propiedad.SetValue
                                      (obe, drd.GetDecimal(i));
                                    else
                                    {
                                        if (tipoDato.Contains("datetime")) propiedad.SetValue
                                          (obe, drd.GetDateTime(i));
                                        else
                                        {
                                            if (tipoDato.Contains("double")) propiedad.SetValue
                                              (obe, (int)drd.GetDouble(i));
                                            else propiedad.SetValue(obe, drd.GetString(i));
                                        }
                                    }
                                }
                            }
                        }
                        lista.Add((T)obe);
                    }
                }
            }
            return (lista);
        }
    }
}

Nota: Por defecto OLEDB crea en mayúsculas el nombre de la tabla y los campos, es por eso que para leer la propiedad se usa el método "GetProperty" con 2 parámetros: el nombre del campo y un flag que indica que no sea case sensitive: BindingFlags.IgnoreCase|BindingFlags.Public|BindingFlags.Instance.

Crear el Procedimiento Almacenado en la Base de Datos de SQL Server

Create Procedure [dbo].[uspEmployeesListar]
As
Select EmployeeID,LastName,FirstName,
IsNull(BirthDate,'1/1/1900') As BirthDate
From Employees Order By 1

Crear la Clase con la Entidad del Negocio

using System;
namespace Exportar_DBF
{
    public class beEmpleado
    {
        public int IdEmpleado { get; set; }
        public string Apellido { get; set; }
        public string Nombre { get; set; }
        public DateTime FechaNac { get; set; }
    }
}

Nota: Para el caso de trabajar con DBF tener cuidado que los nombres de las propiedades tengan como máximo 10 caracteres, de lo contrario el método "DBFALista" generará un error.

Crear la Clase de Datos

using System;
using System.Data; //CommandType
using System.Data.SqlClient; //SqlConnection, SqlCommand, SqlDataReader
using System.Collections.Generic; //List

namespace Exportar_DBF
{
    public class daEmpleado
    {
        public List<beEmpleado> listar(SqlConnection con)
        {
            List<beEmpleado> lbeEmpleado = null;
            SqlCommand cmd = new SqlCommand("uspEmployeesListar", con);
            cmd.CommandType = CommandType.StoredProcedure;
            SqlDataReader drd = cmd.ExecuteReader(CommandBehavior.SingleResult);
            if (drd != null)
            {
                lbeEmpleado = new List<beEmpleado>();
                int posIdEmpleado = drd.GetOrdinal("EmployeeID");
                int posApellido = drd.GetOrdinal("LastName");
                int posNombre = drd.GetOrdinal("FirstName");
                int posFechaNacimiento = drd.GetOrdinal("BirthDate");
                beEmpleado obeEmpleado;
                while (drd.Read())
                {
                    obeEmpleado = new beEmpleado();
                    obeEmpleado.IdEmpleado = drd.GetInt32(posIdEmpleado);
                    obeEmpleado.Apellido = drd.GetString(posApellido);
                    obeEmpleado.Nombre = drd.GetString(posNombre);
                    obeEmpleado.FechaNac = drd.GetDateTime(posFechaNacimiento);
                    lbeEmpleado.Add(obeEmpleado);
                }
                drd.Close();
            }
            return (lbeEmpleado);
        }
    }
}

Crear la Clase con las Reglas del Negocio

using System;
using System.Collections.Generic;
using System.Data.SqlClient; //SqlConnection
using System.Configuration; //ConfigurationManager

namespace Exportar_DBF
{
    public class brEmpleado
    {
        public string Conexion { get; set; }

        public brEmpleado()
        {
            Conexion = ConfigurationManager.ConnectionStrings
                ["conNW"].ConnectionString;
        }

        public List<beEmpleado> listar()
        {
            List<beEmpleado> lbeEmpleado = null;
            using (SqlConnection con = new SqlConnection(Conexion))
            {
                try
                {
                    con.Open();
                    daEmpleado odaEmpleado = new daEmpleado();
                    lbeEmpleado = odaEmpleado.listar(con);
                }
                catch (SqlException ex)
                {
                    //Capturar el error y grabar un Log
                }
            } //con.Close(); con.Dispose(); con = null;
            return (lbeEmpleado);
        }
    }
}

Modificar el Archivo de Configuración de la Aplicación

Abrir el archivo App.Config y aumentar la cadena de conexión a Northwind (conNW):

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <connectionStrings>
      <add name="conNW" providerName="SQLServer"
         connectionString="uid=UsuarioNW;pwd=123456;
         data source=DSOFT\Sqlexpress;database=Northwind"/>
    </connectionStrings>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>

Crear el Formulario Windows Forms de Prueba

Cambiar el nombre del formulario a "frmListaDBF" y arrastrar 3 controles:
- Un DataGridView llamado "dgvEmpleado"
- Un Button llamado "btnExportarDBF"
- Un Button llamado "btnCargarDBF"


Escribir el siguiente código en el formulario:

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Windows.Forms;

namespace Exportar_DBF
{
    public partial class frmListaDBF : Form
    {
        private List<beEmpleado> lbeEmpleado;

        public frmListaDBF()
        {
            InitializeComponent();
        }

        private void cargarDatosSQL(object sender, EventArgs e)
        {
            brEmpleado obrEmpleado = new brEmpleado();
            lbeEmpleado = obrEmpleado.listar();
            dgvEmpleado.DataSource = lbeEmpleado;
        }

        private void exportarListaDBF(object sender, EventArgs e)
        {
            ucListaDBF<beEmpleado>.ListaADBF(lbeEmpleado, "Empleado");
            MessageBox.Show("DBF creado");
        }

        private void cargarDBF(object sender, EventArgs e)
        {
            lbeEmpleado = ucListaDBF<beEmpleado>.DBFALista("Empleado");
            MessageBox.Show("Lista creada");
        }
    }
}

Ejecutar y Probar el Formulario Creado

Grabar la aplicación, compilar y ejecutar, clic al botón "Exportar a DBF" y se creará en el directorio de la aplicación "Exportar_DBF\bin\Debug" el archivo "EMPLEADO.DBF", luego para comprobar que el DBF fue creado clic al botón "Cargar DBF" que lee el DBF en una lista de objetos y lo muestra en la misma grilla.


Comentario Final

Usando OLEDB y Reflection hemos creado dinámicamente un archivo DBF con datos almacenados en una lista de objetos, también hemos realizado la carga del archivo DBF en una Lista de Objetos. El único cuidado que debemos tener es que los nombres de campos no deben pasar los 10 caracteres porque hay una limitación de los campos en un archivo DBF.

Descarga:
Demo09_Exportar_DBF

Charlas Gratuitas en Universidades e Institutos

Charlas Gratuitas en Instituciones Educativas

A pedido de algunas personas, estoy tomando la iniciativa de nuevamente difundir Programación en .NET para los estudiantes de Institutos y Universidades del Perú.

Las charlas en cada institución serán entre 2 y 3 horas y serán libres, aquellos interesados que quieren llevar a sus centros de estudios una charla deberán enviarme un mail a: Luis.duenash@gmail.com con el asunto: "Charlas en Instituciones Educativas", indicando el Centro de Estudios, Lugar, Contacto, Fecha propuesta y Numero aproximado de participantes.

Las charlas deberán ser entre Lunes y Viernes en la tarde o en la noche. Para tener alguna constancia de la charla seria bueno al final un Certificado de participación como expositor. Por ahora sera en Lima ya que para provincias es un poco complicado, pero se podría llegar a un acuerdo.

Mi misión es compartir el conocimiento especializado en el desarrollo de aplicaciones, lo cual lo vengo haciendo mas de 20 años, pero el sector con el que siempre he trabajo ha sido el profesional, es decir, los que ya trabajan desarrollando, pero creo que los estudiantes que son los que recién empiezan son los que mas necesitan ver en lo que van a pasar muchos años de su vida y hay que hacerlo lo mejor posible.

Saludos.

El Libro del Día: Learning jQuery Deferreds

El Libro del Día: 2014-07-03

Titulo: Learning jQuery Deferreds
Autor: Terry Jones, Nicholas H Tollervey
Editorial: O'Reilly
Nro Paginas: 131

Capítulos:
1. Introduction
2. The jQuery Deferred API
3. Deferred Recipes
4. More Time in the Mental Gymnasium
A. Hints for Selected Challenges
B. The Promises/A+ Specification
C. Converting an ArrayBuffer to Base 64

Descarga:
Learning_jQuery_Deferreds