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");
}
}
}
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
profe, gracias por su valioso aporte alcanzándonos una herramienta muy útil. saludos,
ResponderBorrar