【问题标题】:How to convert a List into DataTable如何将列表转换为数据表
【发布时间】:2025-12-10 18:05:01
【问题描述】:

我从另一个数据表中获取值作为列表的输入。现在我需要将这些列表值保存到另一个 DataTable 中。

列表:

List<DataRow> list = slectedFieldsTable.AsEnumerable().ToList();
foreach (DataRow dr in slectedFieldsTable.Rows)
{
    list.Add(dr);
}

新数据表:

DataRow newRow = tempTable.NewRow();
newRow["Field Name"] = fieldLabel;
newRow["Field Type"] = fieldType;

for(int gg =0 ; gg<list.Count; gg++)
{
    tempTable.Rows.Add(????);
}

我被困在这里将行添加到新数据表中。

【问题讨论】:

标签: c# list ado.net datatable datarow


【解决方案1】:
public static DataTable ToDataTable<T>(List<T> items)
{
        DataTable dataTable = new DataTable(typeof(T).Name);

        //Get all the properties
        PropertyInfo[] Props = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (PropertyInfo prop in Props)
        {
            //Setting column names as Property names
            dataTable.Columns.Add(prop.Name);
        }
        foreach (T item in items)
        {
           var values = new object[Props.Length];
           for (int i = 0; i < Props.Length; i++)
           {
                //inserting property values to datatable rows
                values[i] = Props[i].GetValue(item, null);
           }
           dataTable.Rows.Add(values);
      }
      //put a breakpoint here and check datatable
      return dataTable;
}

【讨论】:

    【解决方案2】:

    变量声明:

        DataTable tempTable = new DataTable();
        DataTable slectedFieldsTable = new DataTable();
        DataRow newRow;
        List<object> list = new List<object>();
    

    在数据表中添加列:

        slectedFieldsTable = new DataTable();
        slectedFieldsTable.Columns.Add("Field Name");
        slectedFieldsTable.Columns.Add("Field Type");
    

    在数据表中添加值:

        slectedFieldsTable.Rows.Add("1", "AAA");
        slectedFieldsTable.Rows.Add("2", "BBB");
        slectedFieldsTable.Rows.Add("3", "CCC");
    

    将数据表转换为列表:

      foreach (DataRow dr in slectedFieldsTable.Rows)
        {
            list.Add(dr);
        }
    

    在另一个数据表中添加列:

        tempTable.Columns.Add("Field Name", typeof(string));
        tempTable.Columns.Add("Field Type", typeof(string));
    

    将列表转换为数据表:

       foreach(DataRow dr in list)
        {
            newRow = tempTable.NewRow();
            newRow["Field Name"] = dr.ItemArray[0].ToString();
            newRow["Field Type"] = dr.ItemArray[1].ToString();
            tempTable.Rows.Add(newRow);
            tempTable.AcceptChanges();
        }
    

    【讨论】:

      【解决方案3】:

      使用 CopyToDataTable() 方法。 CopyToDataTable

      IEnumerable<DataRow> query = TempselectedFieldsTable.AsEnumerable().ToList();
      
      // Create a table from the query.
      DataTable boundTable = query.CopyToDataTable<DataRow>();
      

      【讨论】:

        【解决方案4】:

        提供ToDataTable 的答案是一个非常好的开始,但它缺少一些关键元素。即,它忽略了List 项目属性可能:

        • ...标记为ReadOnly
        • ...使用DisplayName 属性
        • ...有一个DefaultValue DataColumn 应该知道的
        • ...成为Nullable
        • ...标记为BrowsableAttribute(false)

        以下是返回DataTable 的扩展方法,可以说明上述情况,也可以为您的代码提供应用它们的方法。它还使用Interface 从类对象而不是Reflection 中获取值。

        public static DataTable ToDataTable<T>(this IList<T> lst, bool includeAll = true)
        {
            DataTable dt = new DataTable();
            DataColumn dc;
            PropertyDescriptor pd;
            bool Browsable;
        
            PropertyDescriptorCollection propCol = TypeDescriptor.GetProperties(typeof(T));
        
            for (int n = 0; n < propCol.Count; n++)
            {
                pd = propCol[n];
                Type propT = pd.PropertyType;
        
                dc = new DataColumn(pd.Name);
        
                // if Nullable, get underlying type
                // the first test may not be needed
                if (propT.IsGenericType && Nullable.GetUnderlyingType(propT) != null )
                {
                    propT = Nullable.GetUnderlyingType(propT);
                    dc.DataType = propT;
                    dc.AllowDBNull = true;
                }
                else
                {
                    dc.DataType = propT;
                    dc.AllowDBNull = false;
                }
        
                // is it readonly?
                if (pd.Attributes[typeof(ReadOnlyAttribute)] != null)
                {
                    dc.ReadOnly = ((ReadOnlyAttribute)pd.
                             Attributes[typeof(ReadOnlyAttribute)]).IsReadOnly;
                }         
        
                // DefaultValue ...
                if (pd.Attributes[typeof(DefaultValueAttribute)] != null)
                {
                    dc.DefaultValue = ((DefaultValueAttribute)pd.
                           Attributes[typeof(DefaultValueAttribute)]).Value;
                }
        
                // caption / display name
                dc.ExtendedProperties.Add("DisplayName", dc.Caption);
                if (pd.Attributes[typeof(DisplayNameAttribute)] != null)
                {
                    // these are usually present but blank
                    string theName = ((DisplayNameAttribute)pd.
                           Attributes[typeof(DisplayNameAttribute)]).DisplayName;
                    dc.Caption = string.IsNullOrEmpty(theName) ? dc.Caption : theName;
                    // DGV doesnt use Caption...save for later
                    dc.ExtendedProperties["DisplayName"] = dc.Caption;
                }
        
                Browsable = true;
                dc.ExtendedProperties.Add("Browsable", Browsable);
                var foo = pd.Attributes[typeof(BrowsableAttribute)];
                if (pd.Attributes[typeof(BrowsableAttribute)] != null) 
                {
                    Browsable = ((BrowsableAttribute)pd.Attributes[typeof(BrowsableAttribute)]).Browsable;
                    // no such thing as a NonBrowsable DataColumn
                    dc.ExtendedProperties["Browsable"] = Browsable;
                }
        
                // ToDo: add support for custom attributes
        
                if (includeAll || Browsable)
                {
                    dt.Columns.Add(dc);
                }
            }
        
            // the lst could be empty such as creating a typed table
            if (lst.Count == 0) return dt;
        
            if (lst[0] is IDataValuesProvider)
            {
                IDataValuesProvider dvp;
                // copy the data - let the class do the work
                foreach (T item in lst)
                {
                    dvp = (IDataValuesProvider)item;
                    dt.Rows.Add(dvp.GetDataValues(includeAll).ToArray());
                }
            }
            else
            {
                List<object> values;
                foreach (T item in lst)
                {
                    values =  new List<object>();
                    // only Browsable columns added
                    for (int n = 0; n < dt.Columns.Count; n++)
                    {
                         values.Add(propCol[dt.Columns[n].ColumnName].GetValue(item));
                    }
                    dt.Rows.Add(values.ToArray());
                }
            }   
        
            return dt;
        }
        

        该方法允许您指定是否应将非Browsable 属性的列添加到DataTable。如果需要,您可以完全省略它们,而不是稍后隐藏这些列。

        接口证明了按顺序从集合成员获取数据值的方法(作为反射循环的替代方案):

        public interface IDataValuesProvider
        {
            IEnumerable<object> GetDataValues(bool includeAll);
        }
        
        ... on the class:
        public class StockItem : IDataValuesProvider
        {
            public int Id { get; set; }
            public string ItemName {get; set;}
            [Browsable(false), DisplayName("Ignore")]
            public string propA {get; set;}
            [ReadOnly(true)]
            public string Zone { get; set; }
            public string Size {get; set;}
            [DisplayName("Nullable")]
            public int? Foo { get; set; }
            public int OnHand {get; set;}
            public string ProdCode {get; set;}
            [Browsable(false)]
            public string propB { get; set; }
            public DateTime ItemDate {get; set;}
        
            // IDataValuesProvider implementation
            public IEnumerable<object> GetDataValues(bool IncludeAll)
            {
                List<object> values = new List<object>();
        
                values.AddRange(new object[] {Id, ItemName });
                if (IncludeAll) values.Add(propA);
        
                values.AddRange(new object[] { Zone, Size, Foo, OnHand, ProdCode });
                if (IncludeAll) values.Add(propB);
        
                values.Add(ItemDate);
        
                return values;
            }
        }
        

        按照您在班级中列出的顺序添加数据值;添加属性时请务必更新它。反射版本仍然存在,因此您可以使用任何一种方式。

        最后,有一些常见的Attributes 没有相关的DataColumn 属性。该方法将这些存储为ExtendedProperties,让您可以轻松地将它们应用到 DGV:

        var dtX = someData.ToDataTable();
        
        dgvB.SuspendLayout();
        dgvB.DataSource = dtX;
        
        // process extended props
        foreach (DataColumn dc in dtX.Columns)
        {
            // no need to test, the code adds them everytime
            //if (dc.ExtendedProperties.ContainsKey("DisplayName"))
            //{ 
                dgvB.Columns[dc.ColumnName].HeaderText = dc.ExtendedProperties["DisplayName"].ToString(); 
            //}
            //if (dc.ExtendedProperties.ContainsKey("Browsable"))
            //{
                dgvB.Columns[dc.ColumnName].Visible = (bool)dc.ExtendedProperties["Browsable"];
            //}
        }
        dgvB.ResumeLayout();
        

        使用上面显示的类列表的结果:

        OnHandFoo 都显示 DisplayNamePropAPropB 都是隐藏的。最重要的是,为 ReadOnlyNullable 属性创建的列会相应地起作用。

        【讨论】:

          【解决方案5】:

          试试这个:

               foreach (DataRow dr in list)
               {
                  tempTable.Rows.Add(dr);
               }
          

          【讨论】: