如何将 DataTable 转换为通用列表?

205

目前,我正在使用:

DataTable dt = CreateDataTableInSomeWay();

List<DataRow> list = new List<DataRow>(); 
foreach (DataRow dr in dt.Rows)
{
    list.Add(dr);
}

有更好/神奇的方法吗?


3
你用列表想要达到什么目的,而用DataRowCollection无法实现呢? - Jason Kealey
我的回复有些晚了,但希望对你有所帮助。这是一个有效的解决方案...https://dev59.com/ekbRa4cB1Zd3GeqPyTo8#58607820 - Thameem
29个回答

302
如果您正在使用.NET 3.5,则可以使用 DataTableExtensions.AsEnumerable(扩展方法),然后如果您真的需要一个List<DataRow>而不仅仅是IEnumerable<DataRow>,则可以调用Enumerable.ToList
IEnumerable<DataRow> sequence = dt.AsEnumerable();
using System.Linq;
...
List<DataRow> list = dt.AsEnumerable().ToList();

1
如何将这个 list 转换为 json。 - ACP
6
在.NET中,将数据转换为JSON有多种方式。个人而言,我一直使用JSON.NET库,但也有其他方法。 - Jon Skeet
1
@Jon Skeet:我想获取DataRow中的值。有没有什么方法?而不是像list.ItemArray[0]这样获取。 - Ramesh Durai
@Jon,FYI:dt.AsEnumerable().ToList()的'System.Data.EnumerableRowCollection<System.Data.DataRow>'没有'ToList'的定义。 - Pradip
1
@赞助商:是的,但不适用于.NET 3.0及以下版本 - .NET Core 2.0比.NET 3.5晚得多。请注意,此答案是在2008年编写的。 - Jon Skeet
显示剩余7条评论

71
List<Employee> emp = new List<Employee>();

//Maintaining DataTable on ViewState
//For Demo only

DataTable dt = ViewState["CurrentEmp"] as DataTable;

//read data from DataTable 
//using lamdaexpression


emp = (from DataRow row in dt.Rows

   select new Employee
   {
       _FirstName = row["FirstName"].ToString(),
       _LastName = row["Last_Name"].ToString()

   }).ToList();

1
以上代码可能无法正常工作,因为dt.Rows没有实现'AsEnumerable'。可以按以下方式进行更正:emp = (from DataRow row in dt.AsEnumerable() select new Employee { _FirstName = row["FirstName"].ToString(), _LastName = row["Last_Name"].ToString() }).ToList(); - Navin Pandit

42

借助 C# 3.0 和 System.Data.DataSetExtensions.dll,

List<DataRow> rows = table.Rows.Cast<DataRow>().ToList();

3
使用这种方法,相比于仅使用 foreach 遍历 DataRow,性能可以提高50%的时间。 - lloydom

36

您可以使用

List<DataRow> list = new List<DataRow>(dt.Select());

dt.Select()会将你的表中的所有行作为数据行数组返回,并且List构造函数接受该对象数组作为参数来初始化您的列表。


Select() 不需要任何参数。无参重载将返回所有行。 - Kon

18
您可以创建一个扩展函数,如下所示:
public static List<T> ToListof<T>(this DataTable dt)
{
    const BindingFlags flags = BindingFlags.Public | BindingFlags.Instance;
    var columnNames = dt.Columns.Cast<DataColumn>()
        .Select(c => c.ColumnName)
        .ToList();
    var objectProperties = typeof(T).GetProperties(flags);
    var targetList = dt.AsEnumerable().Select(dataRow =>
    {
        var instanceOfT = Activator.CreateInstance<T>();

        foreach (var properties in objectProperties.Where(properties => columnNames.Contains(properties.Name) && dataRow[properties.Name] != DBNull.Value))
        {
            properties.SetValue(instanceOfT, dataRow[properties.Name], null);
        }
        return instanceOfT;
    }).ToList();

    return targetList;
}


var output = yourDataInstance.ToListof<targetModelType>();

无法工作 - 请参见 https://dotnetfiddle.net/I22r2c还应该注意,使用反射会降低性能,不建议在性能关键代码中使用。 - Almenon
你需要为列添加数据类型信息。 DataTable dt = new DataTable(); dt.Columns.Add("id",typeof(Int32 )); dt.Columns.Add("name",typeof(String)); dt.Columns.Add("foo",typeof(DateTime )); for(int i=0;i<=1000;i++){dt.Rows.Add(i, "foo", DateTime.Now);} - Rahul Garg

17

如果你只想返回"ID"整型字段的值列表,可以使用...

List<int> ids = (from row in dt.AsEnumerable() select Convert.ToInt32(row["ID"])).ToList();

15

我对来自这个答案的代码进行了一些修改 (https://stackoverflow.com/a/24588210/4489664),因为对于可空类型它会返回异常。

public static List<T> DataTableToList<T>(this DataTable table) where T: new()
{
    List<T> list = new List<T>();
    var typeProperties = typeof(T).GetProperties().Select(propertyInfo => new
        {
            PropertyInfo = propertyInfo,
            Type = Nullable.GetUnderlyingType(propertyInfo.PropertyType) ?? propertyInfo.PropertyType
        }).ToList();

    foreach (var row in table.Rows.Cast<DataRow>())
    {
        T obj = new T();
        foreach (var typeProperty in typeProperties)
        {
            object value = row[typeProperty.PropertyInfo.Name];
            object safeValue = value == null || DBNull.Value.Equals(value)
                ? null
                : Convert.ChangeType(value, typeProperty.Type);

            typeProperty.PropertyInfo.SetValue(obj, safeValue, null);
        }
        list.Add(obj);
    }
    return list;
}

我删除了这个类,因为它也可以与结构体一起使用。 - Mickey Perlstein
这个很好地运行了,干得好。如果有人想玩一下这个代码,我在这个链接创建了一个 .net fiddle:https://dotnetfiddle.net/mTKevy - Almenon
@Almenon,我进行了小的修改,应该会稍微提高性能。 - Bondaryuk Vladimir

11
using System.Data;


var myEnumerable = myDataTable.AsEnumerable();

List<MyClass> myClassList =
    (from item in myEnumerable
     select new MyClass{
         MyClassProperty1 = item.Field<string>("DataTableColumnName1"),
         MyClassProperty2 = item.Field<string>("DataTableColumnName2")
    }).ToList();

6

再次,使用3.5版本您可以这样做:

dt.Select().ToList()

敬礼


6
// this is better suited for expensive object creation/initialization
IEnumerable<Employee> ParseEmployeeTable(DataTable dtEmployees)
{
    var employees = new ConcurrentBag<Employee>();

    Parallel.ForEach(dtEmployees.AsEnumerable(), (dr) =>
    {
        employees.Add(new Employee() 
        {
            _FirstName = dr["FirstName"].ToString(),
            _LastName = dr["Last_Name"].ToString()
        });
    });

    return employees;
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接