【问题标题】:How do I transform a List<T> into a DataSet?如何将 List<T> 转换为 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 中......但同时它允许我控制每个属性添加到行中的顺序。

有人知道更好的方法吗?

【问题讨论】:

    标签: c# list dataset transformation


    【解决方案1】:

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

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

        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 传递给扩展。太棒了!
    【解决方案2】:

    我在 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()
    【解决方案3】:

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

    【讨论】:

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

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

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

    【讨论】:

      【解决方案5】:

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

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

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

      很抱歉我的代码在 VB 中,尽管这个问题被标记为;我的项目在 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
      

      【讨论】:

        猜你喜欢
        • 2018-01-25
        • 2014-02-05
        • 2022-11-07
        • 1970-01-01
        • 2019-04-13
        • 1970-01-01
        • 2019-02-03
        • 1970-01-01
        相关资源
        最近更新 更多