Imports System.Data.DataSetExtensions
Imports System.Runtime.CompilerServices
Imports System.Reflection
Imports System.Collections.Generic
Dim people as New DataTable
people.Columns.Add("ID", GetType(Integer))
people.Columns.Add("Name", GetType(String))
people.Rows.Add(2,"Susan")
Dim extendedInfo as New DataTable
extendedInfo.Columns.Add("PersonID", GetType(Integer))
extendedInfo.Columns.Add("SSN", GetType(String))
extendedInfo.Rows.Add(1,"555-55-5555")
extendedInfo.Rows.Add(2,"XXX-XX-XXXX")
Dim query = From person In people.AsEnumerable
Join extra In extendedInfo.AsEnumerable()
On person("ID") Equals extra("PersonID")
Dim newTable = query.CopyToDataTable()
For Each row In newTable.AsEnumerable()
Console.WriteLine("ID: " & row("ID") & " / " &
"Name: " & row("Name") & " / " &
Public Module CustomLINQtoDataSetMethods
Public Function CopyToDataTable(Of T)(ByVal source As IEnumerable(Of T)) As DataTable
Return New ObjectShredder(Of T)().Shred(source, Nothing, Nothing)
Public Function CopyToDataTable(Of T)(ByVal source As IEnumerable(Of T), ByVal table As DataTable, ByVal options As LoadOption?) As DataTable
Return New ObjectShredder(Of T)().Shred(source, table, options)
Public Class ObjectShredder(Of T)
Private _fi As FieldInfo()
Private _ordinalMap As Dictionary(Of String, Integer)
Private _pi As PropertyInfo()
Me._fi = Me._type.GetFields
Me._pi = Me._type.GetProperties
Me._ordinalMap = New Dictionary(Of String, Integer)
Public Function ShredObject(ByVal table As DataTable, ByVal instance As T) As Object()
Dim fi As FieldInfo() = Me._fi
Dim pi As PropertyInfo() = Me._pi
If (Not instance.GetType Is GetType(T)) Then
Me.ExtendTable(table, instance.GetType)
fi = instance.GetType.GetFields
pi = instance.GetType.GetProperties
Dim values As Object() = New Object(table.Columns.Count - 1) {}
values(Me._ordinalMap.Item(f.Name)) = f.GetValue(instance)
values(Me._ordinalMap.Item(p.Name)) = p.GetValue(instance, Nothing)
Public Function Shred(ByVal source As IEnumerable(Of T), ByVal table As DataTable, ByVal options As LoadOption?) As DataTable
If GetType(T).IsPrimitive Then
Return Me.ShredPrimitive(source, table, options)
If (table Is Nothing) Then
table = New DataTable(GetType(T).Name)
table = Me.ExtendTable(table, GetType(T))
Using e As IEnumerator(Of T) = source.GetEnumerator
table.LoadDataRow(Me.ShredObject(table, e.Current), options.Value)
table.LoadDataRow(Me.ShredObject(table, e.Current), True)
Public Function ShredPrimitive(ByVal source As IEnumerable(Of T),
ByVal table As DataTable,
ByVal options As LoadOption?) As DataTable
If (table Is Nothing) Then
table = New DataTable(GetType(T).Name)
If Not table.Columns.Contains("Value") Then
table.Columns.Add("Value", GetType(T))
Using e As IEnumerator(Of T) = source.GetEnumerator
Dim values As Object() = New Object(table.Columns.Count - 1) {}
values(table.Columns.Item("Value").Ordinal) = e.Current
table.LoadDataRow(values, options.Value)
table.LoadDataRow(values, True)
Public Function ExtendTable(ByVal table As DataTable, ByVal type As Type) As DataTable
For Each f In type.GetFields
If Not Me._ordinalMap.ContainsKey(f.Name) Then
dc = If(table.Columns.Contains(f.Name),
table.Columns.Item(f.Name),
table.Columns.Add(f.Name, f.FieldType))
Me._ordinalMap.Add(f.Name, dc.Ordinal)
For Each p In type.GetProperties
If Not Me._ordinalMap.ContainsKey(p.Name) Then
dc = If(table.Columns.Contains(p.Name),
table.Columns.Item(p.Name),
table.Columns.Add(p.Name, p.PropertyType))
Me._ordinalMap.Add(p.Name, dc.Ordinal)