lunes, 16 de junio de 2014

El Demo del Día: Convertir DataTable en Lista de Objetos y Viceversa

Convertir DataTable en Lista de Objetos y Viceversa

Cuando trabajamos en forma distribuida, usamos Librerías, Clases y Métodos, algunos trabajamos con Listas de Objetos y otros con DataTables y DataSets. En ambos casos, muchas veces necesitamos la funcionalidad de la otra estructura de datos, por ejemplo si usamos Listas de Objetos y queremos migrar o insertar gran cantidad de datos hacia SQL Server mejor sería usar SqlBulkCopy, pero el metodo WriteToServer necesita como parámetro un DataTable, es por eso que presento un par de métodos que permiten realizar el cambio de uno a otro para cuando se necesite.

Paso 1: Crear una Clase Genérica para crear los Métodos que usen la Lista de Objetos
using System.Reflection;
using System.Data;

namespace Conversion_DataTable_Lista
{
    public class ucTablaLista<T>
    {
    }
}

Paso 2: Función Genérica para Convertir un DataTable en Lista de Objetos
        public static List<T> TablaALista(DataTable tabla)
        {
            List<T> lista = new List<T>();
            Type tipo = typeof(T);
            object obe = null;
            string tipoDato;
            string campo;
            for (int i = 0; i < tabla.Rows.Count; i++)
            {
                obe = Activator.CreateInstance(tipo);
                for(int j=0;j<tabla.Columns.Count;j++)
                {
                    campo = tabla.Columns[j].ColumnName;
                    tipoDato = tabla.Columns[j].DataType.ToString().ToLower();
                    if (tipoDato.Contains("int16")) obe.GetType().GetProperty(campo).
                      SetValue(obe, (short)tabla.Rows[i][j]);
                    else
                    {
                        if (tipoDato.Contains("int32")) obe.GetType().GetProperty(campo).
                          SetValue(obe, (int)tabla.Rows[i][j]);
                        else
                        {
                            if (tipoDato.Contains("decimal")) obe.GetType().GetProperty(campo).
                              SetValue(obe, (decimal)tabla.Rows[i][j]);
                            else obe.GetType().GetProperty(campo).SetValue(obe,
                              tabla.Rows[i][j].ToString());
                        }
                    }                  
                }
                lista.Add((T)obe);
            }
            return (lista);
        }

Paso 3: Función Genérica para Convertir una Lista de Objetos en DataTable
        public static DataTable ListaATabla(List<T> lista)
        {
            DataTable tabla = new DataTable();
            //Crear la Estructura de la Tabla a partir de la Lista de Objetos
            PropertyInfo[] propiedades =  lista[0].GetType().GetProperties();
            for (int i = 0; i < propiedades.Length; i++)
            {
                tabla.Columns.Add(propiedades[i].Name, propiedades[i].PropertyType);
            }
            //Llenar la Tabla desde la Lista de Objetos
            DataRow fila = null;
            for (int i = 0; i < lista.Count; i++)
            {
                propiedades = lista[i].GetType().GetProperties();
                fila = tabla.NewRow();
                for (int j = 0; j < propiedades.Length; j++)
                {
                    fila[j] = propiedades[j].GetValue(lista[i], null);
                }
                tabla.Rows.Add(fila);
            }
            return (tabla);
        }

Paso 4: Crear una Clase Entidad para Almacenar los Productos
    public class beProducto
    {
        public int ProductID { get; set; }
        public string ProductName { get; set; }
        public int SupplierID { get; set; }
        public int CategoryID { get; set; }
        public decimal UnitPrice { get; set; }
        public short UnitsInStock { get; set; }
    }

Paso 5: Crear un  Formulario con una Grilla que pueda usar las 2 Funciones
Asumiendo que las 2 funciones estáticas fueron creadas en una clase llamada "ucTablaLista" y hemos creado un formulario llamado: "frmProducto" conteniendo un control DataGridView llamado: "dgvProducto" y 2 botones llamados: "btnDataTableALista" y "btnListaADataTable", tal como se muestra en la siguiente imagen:


Escribir el siguiente código en el formulario:

public partial class frmProducto : Form
    {
        private DataTable tabla;
        private List<beProducto> lista;

        public frmProducto()
        {
            InitializeComponent();
        }

        private void listarProductos(object sender, EventArgs e)
        {
            using (SqlConnection con = new SqlConnection("uid=UsuarioNW;pwd=123456;
            data source=NombreServidor;initial catalog=Northwind"))
            {
                try
                {
                    tabla = new DataTable();
                    lista = new List<beProducto>();
                    con.Open();
                    SqlDataAdapter dap = new SqlDataAdapter("Select ProductID,ProductName,
                      SupplierID,CategoryID,UnitPrice,UnitsInStock From Products", con);
                    dap.Fill(tabla);
                    dgvProducto.DataSource = tabla;
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message);
                }
            }
        }

        private void btnDataTableALista_Click(object sender, EventArgs e)
        {
            lista = ucTablaLista<beProducto>.TablaALista(tabla);
            dgvProducto.DataSource = lista;
            this.Text = String.Format("Objetos en la Lista: {0}", lista.Count);
        }

        private void btnListaADataTable_Click(object sender, EventArgs e)
        {
            tabla = ucTablaLista<beProducto>.ListaATabla(lista);
            dgvProducto.DataSource = tabla;
            this.Text = String.Format("Filas en la Tabla: {0}", tabla.Rows.Count);
        }
    }

Paso 6: Ejecutar la aplicación y Probar
Para ejecutar la aplicación se tiene que cambiar la cadena de conexión: usuario, password y nombre del servidor, la base de datos usada es Northwind.


Descarga:
Demo03_Conversion_DataTable_Lista

4 comentarios: