منتدى فيجوال بيسك لكل العرب | منتدى المبرمجين العرب

نسخة كاملة : كيفية تحويل DataTable إلى Object List
أنت حالياً تتصفح نسخة خفيفة من المنتدى . مشاهدة نسخة كاملة مع جميع الأشكال الجمالية .
كاتب الموضوع : Islam Ibrahim

السلام عليكم ورحمة الله وبركاته

في موضوع سابق للأخ أبو عابد عن كيفية تحويل DataTable إلى List(Of Object) أثار هذا الموضوع اهتمامي وقررت بناءًا على المعلومات التي أملك بالإضافة إلى مكتبة MSDN إيجاد كيفية لحل هذا اللغز,وأعتقد أنني وصلت للحل الأقرب باستخدام Reflection بحيث في المثال التالي سأقوم بإنشاء نوع جديد ديناميكيًا إنظلاقاً من المعلومات الموجودة في كائن DataTable.

قمت بكتابة دالتين الأولى GetTableType لإنشاء نوع جديد إنطلاقاً من ال DataTable, ودالة أخرى ToObjectList للحصول على جميع البيانات داخل DataTable وإضافتها لل List, طبعاً استخدمت ال List(Of Object) لأن المترجم لن يستطيع عمل Initializing ل List(Of T) وهو لا يعرف أي معلومات عن هذا النوع الجديد لأني أقوم بتعريفه باستخدام فئات الإنعكاس Reflection وبالتحديد باستخدام الكلاس Emit.TypeBuilder وبعص الفئات المساعدة مثل FieldBuilder, و PropertyBuilder بالإضافة لل ILGenerator. ولتعريف كائنات جديدة من هذا النوع الجديد سنحتاج لاستخدام الدالة Activator.CreateInstance, ومن ثم لتمرير القيم لخصائص تلك الكائنات الجديدة سنستخدم الدالة Type.InvokeMember.

عيب هذه الطريقة أنها بطيئة لأننا سنظطر للتعامل مع الربط المتأخر Late Binding للحصول على خصائص الكائن الذي نريده بمعلومية إسم العمود في الجدول, كما أن Intellisence في هذه الحالة لن يكون مدعوم هو الآخر.

هذا هو نص الدالتين, مع العلم لم أقم بإضافة أي كود للتحقق من صلاحية البيانات المدخلة وأترك المجال لكم وبالتأكيد النوع الجديد الذي سيتم إنشاؤه ستقبل خصائصه نفس نوع البيانات الخاص بالعمود في DataTable , وبالتأكيد يمكن استخدام هذه الطريقة مع أي DataTable:



كود :
Imports System.Threading
Imports System.Reflection
Imports System.Reflection.Emit
Imports System.Collections.Generic
Imports System.Runtime.CompilerServices

Module DataTableExtensions

<Extension()> Public Function GetTableType(ByVal DTable As DataTable) As Type

' Create needed TypeBuilder helpers
Dim myDomain As AppDomain = Thread.GetDomain()
Dim myAsmName As New AssemblyName("Anonymous")
Dim myAsmBuilder As AssemblyBuilder = _
myDomain.DefineDynamicAssembly(myAsmName, AssemblyBuilderAccess.Run)

Dim myModBuilder As ModuleBuilder = myAsmBuilder.DefineDynamicModule(myAsmName.Name)
Dim myTypeBuilder As TypeBuilder = myModBuilder.DefineType(DTable.TableName, TypeAttributes.Public)

For Each col As DataColumn In DTable.Columns

Dim PropertyName = col.ColumnName
Dim PropertyType = col.DataType

Dim PropertyFieldBuilder As FieldBuilder = myTypeBuilder.DefineField("_" & PropertyName.ToLower, _
PropertyType, FieldAttributes.Private)

Dim PBuilder As PropertyBuilder = _
myTypeBuilder.DefineProperty(PropertyName, PropertyAttributes.HasDefault, col.DataType, Nothing)

Dim getSetAttr As MethodAttributes = _
MethodAttributes.Public Or MethodAttributes.SpecialName Or MethodAttributes.HideBySig

Dim getPropertyBuilder As MethodBuilder = _
myTypeBuilder.DefineMethod("get" & PropertyName, getSetAttr, col.DataType, Type.EmptyTypes)

' Constructing IL Code for get and set Methods.
Dim GetPropGenerator As ILGenerator = getPropertyBuilder.GetILGenerator()

GetPropGenerator.Emit(OpCodes.Ldarg_0)
GetPropGenerator.Emit(OpCodes.Ldfld, PropertyFieldBuilder)
GetPropGenerator.Emit(OpCodes.Ret)

Dim setPropertyBuulder As MethodBuilder = _
myTypeBuilder.DefineMethod("set_" & PropertyName, getSetAttr, Nothing, New Type() {col.DataType})

Dim SetPropGenerator As ILGenerator = setPropertyBuulder.GetILGenerator()

SetPropGenerator.Emit(OpCodes.Ldarg_0)
SetPropGenerator.Emit(OpCodes.Ldarg_1)
SetPropGenerator.Emit(OpCodes.Stfld, PropertyFieldBuilder)
SetPropGenerator.Emit(OpCodes.Ret)

PBuilder.SetGetMethod(getPropertyBuilder)
PBuilder.SetSetMethod(setPropertyBuulder)

Next

Dim objCtor As ConstructorInfo = GetType(Object).GetConstructor(New Type(-1) {})
Dim pointCtor As ConstructorBuilder = myTypeBuilder.DefineConstructor(MethodAttributes.Public, _
CallingConventions.Standard, Type.EmptyTypes)

Dim ctorIL As ILGenerator = pointCtor.GetILGenerator()

' Constructing IL Code for the Type Constructor.
ctorIL.Emit(OpCodes.Ldarg_0)
ctorIL.Emit(OpCodes.Call, objCtor)
ctorIL.Emit(OpCodes.Ret)

Return myTypeBuilder.CreateType()

End Function

<Extension()> _
Function ToObjectList(ByVal DTable As DataTable) As List(Of Object)

Dim list As New List(Of Object)
Dim tabletype As Type = DTable.GetTableType

For i = 0 To DTable.Rows.Count - 1
' Create an Instance of the Type for each row in DataTable
Dim ThisRow = Activator.CreateInstance(tabletype)
For j = 0 To DTable.Columns.Count - 1
tabletype.InvokeMember(DTable.Columns(j).ColumnName, BindingFlags.SetProperty, Nothing, _
ThisRow, New Object() {DTable.Rows(i).Item(j)})

Next
list.Add(ThisRow)

Next

Return list
End Function

End Module
طبعًا المستخدم بالتأكيد لابد له من معرفة أسماء الأعمدة وإلا لن يستطيع الإستفادة بهذا الكود.

بالتوفيق