- Crear una tabla con la estructura necesaria
- Recorrer la lista secuencialmente y agregar filas a la tabla
Pero pensando como programador, se me ocurrió que seguramente volviera a necesitar esta función y posiblemente la necesitaría con diferentes tipos de listas.
Como era lógico, pensé en Generics, entonces me creé una función que recibe una System.Collections.Generic.List(Of T) y devuelve un System.Data.DataTable con la siguiente firma:
Public Function ListToDataTable(Of T)(List As System.Collections.Generic.List(Of T)) As System.Data.DataTable
después creé toda la lógica utilizando System.Reflection para examinar las propiedades, y aprovechando los CustomAttributes para poder ocultar propiedades, entonces para hacer un Test, hice una clase con la siguiente estructura:
Public Class MiClase
Private mId As Integer
Private mNombre As String
Private mCantidad As Integer
<System.ComponentModel.Browsable(False)> _
Public Property Id() As Integer
Get
Return mId
End Get
Set(ByVal value As Integer)
mId = value
End Set
End Property
Public Property Nombre() As String
Get
Return mNombre
End Get
Set(ByVal value As String)
mNombre = value
End Set
End Property
Public Property Cantidad() As Integer
Get
Return mCantidad
End Get
Set(ByVal value As Integer)
mCantidad = value
End Set
End Property
Public Sub New()
End Sub
Public Sub New(ByVal Id As Integer, ByVal Nombre As String, ByVal Cantidad As Integer)
mId = Id
mNombre = Nombre
mCantidad = Cantidad
End Sub
End Class
como ven, la propiedad Id tiene un CustomAttribute, el System.ComponentModel.BrowsableAttribute seteado con el valor False, o sea que esa propiedad estaría excluida de la exportación.
Todo muy lindo, funcionó a la perfección... pero se me ocurrió algo mejor... usar Extension Methods o métodos de extensión, esa nueva posibilidad que nos brinda el .Net Framework 3.5 de extender clases sin la necesidad de tener el código fuente de la misma.
Entonces creé un módulo llamado ListExtension y le asigné el mismo Namespace de la lista, System.Collections.Generic.
Imports System.Reflection
Imports System.ComponentModel
Imports System.Runtime.CompilerServices
Namespace System.Collections.Generic
Module ListExtension
''' <summary>
''' Gets a Datatable with all Browsable properties of T as columns containing all Items in the List.
''' </summary>
''' <typeparam name="T"></typeparam>
''' <param name="List">System.Collections.Generic.List(Of T)</param>
''' <returns>System.Data.DataTable</returns>
''' <remarks>http://aprendiendonet.blogspot.com</remarks>
<Extension()> _
Public Function ToDataTable(Of T)(ByVal List As List(Of T)) As DataTable
Dim dt As New DataTable()
Dim tipo As Type = GetType(T)
Dim members As MemberInfo() = tipo.GetMembers() ' Obtenemos todos los Miembros de la clase correspondiente al tipo T
For Each m As MemberInfo In members
If m.MemberType = MemberTypes.Property Then ' Sólo nos interesan las propiedades
Dim skip As Boolean = False
Dim atts As Object() = m.GetCustomAttributes(GetType(BrowsableAttribute), False) ' Chequeamos si tiene BrowsableAttribute
If atts.Length > 0 Then
If CType(atts(0), BrowsableAttribute).Browsable = False Then
skip = True ' Seteamos un flag para no agregar la columna
End If
End If
If Not skip Then
Dim c As DataColumn = Nothing
Try
c = New DataColumn(m.Name, CType(m, PropertyInfo).PropertyType) ' Nueva columna con el nombre de la propiedad y el tipo de la misma
Catch ex As Exception
c = New DataColumn(m.Name, GetType(String)) ' En caso de error intento crearla como String
End Try
dt.Columns.Add(c)
End If
End If
Next
For Each itm As T In List ' Recorro la lista y agrego una fila por cada item de la misma
Dim r As DataRow = dt.NewRow()
For Each c As DataColumn In r.Table.Columns
Dim aux As MemberInfo() = tipo.GetMember(c.ColumnName)
If aux.Length > 0 Then
r(c.ColumnName) = CType(aux(0), PropertyInfo).GetValue(itm, Nothing)
End If
Next
dt.Rows.Add(r)
Next
Return dt
End Function
End Module
End Namespace
Entonces ahora en toda mi solución tengo la posibilidad de exportar mis Listas a DataTable con tan solo llamar a este método de extensión:
Dim auxDT as DataTable = miLista.ToDataTable()