Procedure: CopyToDataTable<T> implementeren waarbij het algemene type T geen DataRow is
Het DataTable object wordt vaak gebruikt voor gegevensbinding. De CopyToDataTable methode neemt de resultaten van een query en kopieert de gegevens naar een DataTable, die vervolgens kan worden gebruikt voor gegevensbinding. De CopyToDataTable methoden werken echter alleen op een IEnumerable<T> bron waarvan de algemene parameter T
van het type DataRowis. Hoewel dit handig is, kunnen tabellen niet worden gemaakt op basis van een reeks scalaire typen, van query's die anonieme typen projecteren of van query's die tabeldeelnames uitvoeren.
In dit onderwerp wordt beschreven hoe u twee aangepaste CopyToDataTable<T>
extensiemethoden implementeert die een algemene parameter T
van een ander type accepteren dan DataRow. De logica voor het maken van een DataTable IEnumerable<T> bron bevindt zich in de ObjectShredder<T>
klasse, die vervolgens wordt verpakt in twee overbelaste uitbreidingsmethoden CopyToDataTable<T>
. De Shred
methode van de ObjectShredder<T>
klasse retourneert de choropleten DataTable en accepteert drie invoerparameters: een IEnumerable<T> bron, een DataTableenumeratie LoadOption . Het oorspronkelijke schema van het geretourneerde DataTable schema is gebaseerd op het schema van het type T
. Als er een bestaande tabel als invoer wordt opgegeven, moet het schema consistent zijn met het schema van het type T
. Elke openbare eigenschap en elk veld van het type T
wordt geconverteerd naar een DataColumn in de geretourneerde tabel. Als de bronreeks een type bevat dat is afgeleid van T
, wordt het geretourneerde tabelschema uitgebreid voor eventuele aanvullende openbare eigenschappen of velden.
Zie Een gegevenstabel maken op basis van een query voor voorbeelden van het gebruik van de aangepaste CopyToDataTable<T>
methoden.
De aangepaste CopyToDataTable<T-methoden> implementeren in uw toepassing
Implementeer de
ObjectShredder<T>
klasse om een DataTable van een IEnumerable<T> bron te maken:public class ObjectShredder<T> { readonly FieldInfo[] _fi; readonly PropertyInfo[] _pi; readonly Dictionary<string, int> _ordinalMap; readonly Type _type; // ObjectShredder constructor. public ObjectShredder() { _type = typeof(T); _fi = _type.GetFields(); _pi = _type.GetProperties(); _ordinalMap = new Dictionary<string, int>(); } /// <summary> /// Loads a DataTable from a sequence of objects. /// </summary> /// <param name="source">The sequence of objects to load into the DataTable.</param> /// <param name="table">The input table. The schema of the table must match that /// the type T. If the table is null, a new table is created with a schema /// created from the public properties and fields of the type T.</param> /// <param name="options">Specifies how values from the source sequence will be applied to /// existing rows in the table.</param> /// <returns>A DataTable created from the source sequence.</returns> public DataTable Shred(IEnumerable<T> source, DataTable table, LoadOption? options) { // Load the table from the scalar sequence if T is a primitive type. if (typeof(T).IsPrimitive) { return ShredPrimitive(source, table, options); } // Create a new table if the input table is null. table ??= new DataTable(typeof(T).Name); // Initialize the ordinal map and extend the table schema based on type T. table = ExtendTable(table, typeof(T)); // Enumerate the source sequence and load the object values into rows. table.BeginLoadData(); foreach (T item in source) { if (options != null) { table.LoadDataRow(ShredObject(table, item), (LoadOption)options); } else { table.LoadDataRow(ShredObject(table, item), true); } } table.EndLoadData(); // Return the table. return table; } public DataTable ShredPrimitive(IEnumerable<T> source, DataTable table, LoadOption? options) { // Create a new table if the input table is null. table ??= new DataTable(typeof(T).Name); if (!table.Columns.Contains("Value")) { table.Columns.Add("Value", typeof(T)); } // Enumerate the source sequence and load the scalar values into rows. table.BeginLoadData(); using (IEnumerator<T> e = source.GetEnumerator()) { var values = new object[table.Columns.Count]; while (e.MoveNext()) { values[table.Columns["Value"].Ordinal] = e.Current; if (options != null) { table.LoadDataRow(values, (LoadOption)options); } else { table.LoadDataRow(values, true); } } } table.EndLoadData(); // Return the table. return table; } public object[] ShredObject(DataTable table, T instance) { FieldInfo[] fi = _fi; PropertyInfo[] pi = _pi; if (instance.GetType() != typeof(T)) { // If the instance is derived from T, extend the table schema // and get the properties and fields. ExtendTable(table, instance.GetType()); fi = instance.GetType().GetFields(); pi = instance.GetType().GetProperties(); } // Add the property and field values of the instance to an array. var values = new object[table.Columns.Count]; foreach (FieldInfo f in fi) { values[_ordinalMap[f.Name]] = f.GetValue(instance); } foreach (PropertyInfo p in pi) { values[_ordinalMap[p.Name]] = p.GetValue(instance, null); } // Return the property and field values of the instance. return values; } public DataTable ExtendTable(DataTable table, Type type) { // Extend the table schema if the input table was null or if the value // in the sequence is derived from type T. foreach (FieldInfo f in type.GetFields()) { if (!_ordinalMap.ContainsKey(f.Name)) { // Add the field as a column in the table if it doesn't exist // already. DataColumn dc = table.Columns.Contains(f.Name) ? table.Columns[f.Name] : table.Columns.Add(f.Name, f.FieldType); // Add the field to the ordinal map. _ordinalMap.Add(f.Name, dc.Ordinal); } } foreach (PropertyInfo p in type.GetProperties()) { if (!_ordinalMap.ContainsKey(p.Name)) { // Add the property as a column in the table if it doesn't exist // already. DataColumn dc = table.Columns.Contains(p.Name) ? table.Columns[p.Name] : table.Columns.Add(p.Name, p.PropertyType); // Add the property to the ordinal map. _ordinalMap.Add(p.Name, dc.Ordinal); } } // Return the table. return table; } }
Public Class ObjectShredder(Of T) ' Fields Private _fi As FieldInfo() Private _ordinalMap As Dictionary(Of String, Integer) Private _pi As PropertyInfo() Private _type As Type ' Constructor Public Sub New() Me._type = GetType(T) Me._fi = Me._type.GetFields Me._pi = Me._type.GetProperties Me._ordinalMap = New Dictionary(Of String, Integer) End Sub 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 ' If the instance is derived from T, extend the table schema ' and get the properties and fields. Me.ExtendTable(table, instance.GetType) fi = instance.GetType.GetFields pi = instance.GetType.GetProperties End If ' Add the property and field values of the instance to an array. Dim values As Object() = New Object(table.Columns.Count - 1) {} Dim f As FieldInfo For Each f In fi values(Me._ordinalMap.Item(f.Name)) = f.GetValue(instance) Next Dim p As PropertyInfo For Each p In pi values(Me._ordinalMap.Item(p.Name)) = p.GetValue(instance, Nothing) Next ' Return the property and field values of the instance. Return values End Function ' Summary: Loads a DataTable from a sequence of objects. ' source parameter: The sequence of objects to load into the DataTable.</param> ' table parameter: The input table. The schema of the table must match that ' the type T. If the table is null, a new table is created ' with a schema created from the public properties and fields ' of the type T. ' options parameter: Specifies how values from the source sequence will be applied to ' existing rows in the table. ' Returns: A DataTable created from the source sequence. Public Function Shred(ByVal source As IEnumerable(Of T), ByVal table As DataTable, ByVal options As LoadOption?) As DataTable ' Load the table from the scalar sequence if T is a primitive type. If GetType(T).IsPrimitive Then Return Me.ShredPrimitive(source, table, options) End If ' Create a new table if the input table is null. If (table Is Nothing) Then table = New DataTable(GetType(T).Name) End If ' Initialize the ordinal map and extend the table schema based on type T. table = Me.ExtendTable(table, GetType(T)) ' Enumerate the source sequence and load the object values into rows. table.BeginLoadData() Using e As IEnumerator(Of T) = source.GetEnumerator Do While e.MoveNext If options.HasValue Then table.LoadDataRow(Me.ShredObject(table, e.Current), options.Value) Else table.LoadDataRow(Me.ShredObject(table, e.Current), True) End If Loop End Using table.EndLoadData() ' Return the table. Return table End Function Public Function ShredPrimitive(ByVal source As IEnumerable(Of T), ByVal table As DataTable, ByVal options As LoadOption?) As DataTable ' Create a new table if the input table is null. If (table Is Nothing) Then table = New DataTable(GetType(T).Name) End If If Not table.Columns.Contains("Value") Then table.Columns.Add("Value", GetType(T)) End If ' Enumerate the source sequence and load the scalar values into rows. table.BeginLoadData() Using e As IEnumerator(Of T) = source.GetEnumerator Dim values As Object() = New Object(table.Columns.Count - 1) {} Do While e.MoveNext values(table.Columns.Item("Value").Ordinal) = e.Current If options.HasValue Then table.LoadDataRow(values, options.Value) Else table.LoadDataRow(values, True) End If Loop End Using table.EndLoadData() ' Return the table. Return table End Function Public Function ExtendTable(ByVal table As DataTable, ByVal type As Type) As DataTable ' Extend the table schema if the input table was null or if the value ' in the sequence is derived from type T. Dim f As FieldInfo Dim p As PropertyInfo For Each f In type.GetFields If Not Me._ordinalMap.ContainsKey(f.Name) Then Dim dc As DataColumn ' Add the field as a column in the table if it doesn't exist ' already. dc = IIf(table.Columns.Contains(f.Name), table.Columns.Item(f.Name), table.Columns.Add(f.Name, f.FieldType)) ' Add the field to the ordinal map. Me._ordinalMap.Add(f.Name, dc.Ordinal) End If Next For Each p In type.GetProperties If Not Me._ordinalMap.ContainsKey(p.Name) Then ' Add the property as a column in the table if it doesn't exist ' already. Dim dc As DataColumn dc = IIf(table.Columns.Contains(p.Name), table.Columns.Item(p.Name), table.Columns.Add(p.Name, p.PropertyType)) ' Add the property to the ordinal map. Me._ordinalMap.Add(p.Name, dc.Ordinal) End If Next ' Return the table. Return table End Function End Class
In het voorgaande voorbeeld wordt ervan uitgegaan dat de eigenschappen en velden van de
DataColumn
velden niet nullable waardetypen zijn. Gebruik de volgende code om eigenschappen en velden met null-waardetypen te verwerken:// Nullable-aware code for properties. DataColumn dc = table.Columns.Contains(p.Name) ? table.Columns[p.Name] : table.Columns.Add(p.Name, Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType); // Nullable-aware code for fields. DataColumn dc = table.Columns.Contains(f.Name) ? table.Columns[f.Name] : table.Columns.Add(f.Name, Nullable.GetUnderlyingType(f.FieldType) ?? f.FieldType);
Implementeer de aangepaste
CopyToDataTable<T>
extensiemethoden in een klasse:public static class CustomLINQtoDataSetMethods { public static DataTable CopyToDataTable<T>(this IEnumerable<T> source) => new ObjectShredder<T>().Shred(source, null, null); public static DataTable CopyToDataTable<T>(this IEnumerable<T> source, DataTable table, LoadOption? options) => new ObjectShredder<T>().Shred(source, table, options); }
Public Module CustomLINQtoDataSetMethods <Extension()> _ Public Function CopyToDataTable(Of T)(ByVal source As IEnumerable(Of T)) As DataTable Return New ObjectShredder(Of T)().Shred(source, Nothing, Nothing) End Function <Extension()> _ 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) End Function End Module
Voeg de
ObjectShredder<T>
klasse- enCopyToDataTable<T>
extensiemethoden toe aan uw toepassing.
Module Module1
Sub Main()
' Your application code using CopyToDataTable<T>.
End Sub
End Module
Public Module CustomLINQtoDataSetMethods
…
End Module
Public Class ObjectShredder(Of T)
…
End Class
class Program
{
static void Main(string[] args)
{
// Your application code using CopyToDataTable<T>.
}
}
public static class CustomLINQtoDataSetMethods
{
…
}
public class ObjectShredder<T>
{
…
}