如何将 List<T> 转换为 DataSet?

Posted

技术标签:

【中文标题】如何将 List<T> 转换为 DataSet?【英文标题】:How do I transform a List<T> into a DataSet? 【发布时间】:2010-10-06 02:34:48 【问题描述】:

给定一个对象列表,我需要将其转换为一个数据集,其中列表中的每个项目由一行表示,每个属性是该行中的一列。然后,此 DataSet 将被传递给 Aspose.Cells 函数,以创建 Excel 文档作为报告。

假设我有以下内容:

public class Record

   public int ID  get; set; 
   public bool Status  get; set; 
   public string Message  get; set; 

给定一个 List 记录,我如何将其转换为 DataSet,如下所示:

ID Status Message
1  true   "message" 
2  false  "message2" 
3  true   "message3" 
...

目前我唯一能想到的如下:

DataSet ds = new DataSet
ds.Tables.Add();
ds.Tables[0].Add("ID", typeof(int));    
ds.Tables[0].Add("Status", typeof(bool));
ds.Tables[0].Add("Message", typeof(string));

foreach(Record record in records)

    ds.Tables[0].Rows.Add(record.ID, record.Status, record.Message);

但是这种方式让我认为必须有更好的方法,因为至少如果将新属性添加到 Record 中,那么它们将不会显示在 DataSet 中......但同时它允许我控制每个属性添加到行中的顺序。

有人知道更好的方法吗?

【问题讨论】:

【参考方案1】:

我在 Microsoft 论坛上找到了此代码。这是迄今为止最简单的方法之一,易于理解和使用。这为我节省了几个小时。我已将此自定义为扩展方法,而对实际实现没有任何更改。下面是代码。它不需要太多解释。

您可以使用两个具有相同实现的函数签名

1) public static DataSet ToDataSetFromObject(this object dsCollection)

2) public static DataSet ToDataSetFromArrayOfObject(this object[] arrCollection)。我将在下面的示例中使用这个。

// <summary>
// Serialize Object to XML and then read it into a DataSet:
// </summary>
// <param name="arrCollection">Array of object</param>
// <returns>dataset</returns>

public static DataSet ToDataSetFromArrayOfObject( this object[] arrCollection)

    DataSet ds = new DataSet();
    try 
        XmlSerializer serializer = new XmlSerializer(arrCollection.GetType);
        System.IO.StringWriter sw = new System.IO.StringWriter();
        serializer.Serialize(sw, dsCollection);
        System.IO.StringReader reader = new System.IO.StringReader(sw.ToString());
        ds.ReadXml(reader);
     catch (Exception ex) 
        throw (new Exception("Error While Converting Array of Object to Dataset."));
    
    return ds;

在代码中使用这个扩展

Country[] objArrayCountry = null;
objArrayCountry = ....;// populate your array
if ((objArrayCountry != null)) 
    dataset = objArrayCountry.ToDataSetFromArrayOfObject();

【讨论】:

你有没有测试过这个,因为它显然不能编译? 请注意,对象需要可序列化才能使用它! Minh,很遗憾听到它不适合你。我已将这种方法广泛用于我的项目迁移。从那以后,我转向了不同的技术。我不能说微软框架的最新变化是破坏它还是我的代码有问题。 例如,“dsCollection”变量在哪里?它应该是 arrCollection。 arrCollection.GetType 也缺少括号,它应该是 arrCollection.GetType()【参考方案2】:

我对 CMS 的扩展方法进行了一些更改,以处理 List 包含原始元素或 String 元素的情况。在这种情况下,生成的DataTable 将只有一个Column,列表中的每个值都有一个Row

起初我想包括所有值类型(不仅是原始类型),但我不想包括结构(它们是值类型)。

此更改源于我需要将 List(Of Long)List&lt;long&gt; 转换为 DataTable 以将其用作 MS SQL 2008 存储过程中的表值参数。

很抱歉,我的代码在 VB 中,尽管这个问题被标记为 c#;我的项目在 VB 中(不是我的选择),在 c# 中应用更改应该不难。

Imports System.Runtime.CompilerServices
Imports System.Reflection

Module Extensions

    <Extension()>
    Public Function ToDataTable(Of T)(ByVal collection As IEnumerable(Of T)) As DataTable
        Dim dt As DataTable = New DataTable("DataTable")
        Dim type As Type = GetType(T)
        Dim pia() As PropertyInfo = type.GetProperties()

        ' For a collection of primitive types create a 1 column DataTable
        If type.IsPrimitive OrElse type.Equals(GetType(String)) Then
            dt.Columns.Add("Column", type)
        Else
            ' Inspect the properties and create the column in the DataTable
            For Each pi As PropertyInfo In pia
                Dim ColumnType As Type = pi.PropertyType
                If ColumnType.IsGenericType Then
                    ColumnType = ColumnType.GetGenericArguments()(0)
                End If
                dt.Columns.Add(pi.Name, ColumnType)
            Next

        End If

        ' Populate the data table
        For Each item As T In collection
            Dim dr As DataRow = dt.NewRow()
            dr.BeginEdit()
            ' Set item as the value for the lone column on each row
            If type.IsPrimitive OrElse type.Equals(GetType(String)) Then
                dr("Column") = item
            Else
                For Each pi As PropertyInfo In pia
                    If pi.GetValue(item, Nothing) <> Nothing Then
                        dr(pi.Name) = pi.GetValue(item, Nothing)
                    End If
                Next
            End If
            dr.EndEdit()
            dt.Rows.Add(dr)
        Next
        Return dt
    End Function

End Module

【讨论】:

【参考方案3】:

我自己编写了一个小型库来完成这项任务。它仅在第一次将对象类型转换为数据表时使用反射。它发出一个方法来完成转换对象类型的所有工作。

它的速度非常快。你可以在这里找到它:ModelShredder on GoogleCode

【讨论】:

【参考方案4】:

您可以通过反射和泛型来检查底层类型的属性。

考虑一下我使用的这种扩展方法:

    public static DataTable ToDataTable<T>(this IEnumerable<T> collection)
    
        DataTable dt = new DataTable("DataTable");
        Type t = typeof(T);
        PropertyInfo[] pia = t.GetProperties();

        //Inspect the properties and create the columns in the DataTable
        foreach (PropertyInfo pi in pia)
        
            Type ColumnType = pi.PropertyType;
            if ((ColumnType.IsGenericType))
            
                ColumnType = ColumnType.GetGenericArguments()[0];
            
            dt.Columns.Add(pi.Name, ColumnType);
        

        //Populate the data table
        foreach (T item in collection)
        
            DataRow dr = dt.NewRow();
            dr.BeginEdit();
            foreach (PropertyInfo pi in pia)
            
                if (pi.GetValue(item, null) != null)
                
                    dr[pi.Name] = pi.GetValue(item, null);
                
            
            dr.EndEdit();
            dt.Rows.Add(dr);
        
        return dt;
    

【讨论】:

好吧,社区已经发声了,所以我会投票给这个答案,即使我无法将它用于我的目的,因为我希望能够控制参数的顺序。但我一定会牢记这个解决方案...... 嘿,我刚刚测试了你的扩展,发现如果你想控制列在数据表中出现的顺序,那么你需要按照你希望它们在对象中的顺序声明它们类型 T 传递给扩展。太棒了!【参考方案5】:

除了额外使用Reflection 来确定类Record 的属性来处理添加新属性之外,就差不多了。

【讨论】:

你可能是对的……尽管这不是我想听到的。我想要么我需要提高我对 DataSets 的了解,要么微软的某个人需要想出一个更好的方法。

以上是关于如何将 List<T> 转换为 DataSet?的主要内容,如果未能解决你的问题,请参考以下文章

如何轻松将 DataReader 转换为 List<T>? [复制]

如何将 List<T> 转换为 DataSet?

如何将 List<T> 转换为 DataSet?

C# - 如何将 Lazy<List<T>> 转换为 Lazy<List<U>>?

如何在 C# 中将 List<DataRow> 转换为 List<T>?

如何将DataTable转换成List<T>