如何在C#中将DataTable转换为通用列表

5

我需要将 C# 的 DataTable 转换为通用集合列表。

DataTable Columns Respectively

 1. EmpId  (this is Int DataType)
 2. EmpName   (this is varchar DataType)
 3. EmpAddress  (this is varchar DataType)
 4. EmpPhone  (this is varchar DataType)
 5. Status   (this is Boolean DataType)
 6. EmpRelationKey (this is int DataType)

我的DataTable包含上述字段的值。在这里,我需要将这个值分配给我的列表。

我的列表变量分别为:

class Employee
{
protected int EmpId  ;
protected string EmpName =String.Empty;
protected string EmpAddress  = String.Empty;
protected string EmpPhone  = String.Empty;
protected bool Status ;
protected int EmpRelationKey ;
}

Declaring List
List<Employee> Emp= new List<Employee>

现在我需要将DataTable的值分配给这个List。代码应该非常专业。
我尝试过以下方法:
List<Employee>employees = new List<Employee>();  

foreach (DataRow row in dt.Rows)  
{  
   employees.Add(new Employee  
   {  
   EmpId  = Convert.ToInt32(row["EmpId"]), 
   EmpName = row["EmpName"].ToString() ,
   EmpAddress =   row["EmpName"].ToString(),
   Emphone =   row["EmpPhone"].ToString(),
   Status = Convert.toBoolean(row["Status"])
   });  
}   

但我不想提及列名,是否有其他方法可以从DataTable中分配值而不需要逐个提到每个列名?


reflection............ - Imad
如何使用或应用于此。 - kselva
你能帮我解决这个问题并发布相关代码供参考吗? - kselva
1
但是我的列表是用户定义的类型。 - kselva
4个回答

11
using System.Reflection;

那么,

public static List<T> BindList<T>(DataTable dt)
{
    // Example 1:
    // Get private fields + non properties
    //var fields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);

    // Example 2: Your case
    // Get all public fields
    var fields = typeof(T).GetFields();

    List<T> lst = new List<T>();

    foreach (DataRow dr in dt.Rows)
    {
        // Create the object of T
        var ob = Activator.CreateInstance<T>();

        foreach (var fieldInfo in fields)
        {
            foreach (DataColumn dc in dt.Columns)
            {
                // Matching the columns with fields
                if (fieldInfo.Name == dc.ColumnName)
                {
                    // Get the value from the datatable cell
                    object value = dr[dc.ColumnName];

                    // Set the value into the object
                    fieldInfo.SetValue(ob, value);
                    break;
                }
            }
        }

        lst.Add(ob);
    }

    return lst;
}

使用示例:

DataTable dt1 = SqlHelper.GetTable("select * from employee;");
List<Employee> employees = BindList<Employee>(dt1);

DataTable dt2 = SqlHelper.GetTable("select * from membership;");
List<Membership> lstMembership = BindList<Membership>(dt2);

DataTable dt3 = SqlHelper.GetTable("select * from car order by name;");
List<Car> lstCar = BindList<Car>(dt3);

=====================

扩展版本

以上示例假设DataTable中保存的数据与您的类对象的字段具有相同的数据类型

如果数据与您的类对象的字段不同呢?

例如null

因此,您可能希望扩展该方法以防万一两种数据类型不同。

public static List<T> BindList<T>(DataTable dt)
{
    // Example 1:
    // Get private fields + non properties
    //var fields = typeof(T).GetFields(BindingFlags.NonPublic | BindingFlags.Instance);

    // Example 2: Your case
    // Get all public fields
    var fields = typeof(T).GetFields();

    List<T> lst = new List<T>();

    foreach (DataRow dr in dt.Rows)
    {
        // Create the object of T
        var ob = Activator.CreateInstance<T>();

        foreach (var fieldInfo in fields)
        {
            foreach (DataColumn dc in dt.Columns)
            {
                // Matching the columns with fields
                if (fieldInfo.Name == dc.ColumnName)
                {
                    Type type = fieldInfo.FieldType;

                    // Get the value from the datatable cell
                    object value = GetValue(dr[dc.ColumnName], type);

                    // Set the value into the object
                    fieldInfo.SetValue(ob, value);
                    break;
                }
            }
        }

        lst.Add(ob);
    }

    return lst;
}

static object GetValue(object ob, Type targetType)
{
    if (targetType == null)
    {
        return null;
    }
    else if (targetType == typeof(String))
    {
        return ob + "";
    }
    else if (targetType == typeof(int))
    {
        int i = 0;
        int.TryParse(ob + "", out i);
        return i;
    }
    else if (targetType == typeof(short))
    {
        short i = 0;
        short.TryParse(ob + "", out i);
        return i;
    }
    else if (targetType == typeof(long))
    {
        long i = 0;
        long.TryParse(ob + "", out i);
        return i;
    }
    else if (targetType == typeof(ushort))
    {
        ushort i = 0;
        ushort.TryParse(ob + "", out i);
        return i;
    }
    else if (targetType == typeof(uint))
    {
        uint i = 0;
        uint.TryParse(ob + "", out i);
        return i;
    }
    else if (targetType == typeof(ulong))
    {
        ulong i = 0;
        ulong.TryParse(ob + "", out i);
        return i;
    }
    else if (targetType == typeof(double))
    {
        double i = 0;
        double.TryParse(ob + "", out i);
        return i;
    }
    else if (targetType == typeof(DateTime))
    {
        // do the parsing here...
    }
    else if (targetType == typeof(bool))
    {
        // do the parsing here...
    }
    else if (targetType == typeof(decimal))
    {
        // do the parsing here...
    }
    else if (targetType == typeof(float))
    {
        // do the parsing here...
    }
    else if (targetType == typeof(byte))
    {
        // do the parsing here...
    }
    else if (targetType == typeof(sbyte))
    {
        // do the parsing here...
    }
    else if........
    ..................

    return ob;
}

这个工作正常。 - kselva
DataTable dt1 = SqlHelper.GetTable("select * from employee;"); List<Employee> employees = BindList<Employee>(dt); 数据表 dt1 = SqlHelper.GetTable("select * from employee;"); List<Employee> 员工列表 = BindList<Employee>(dt); - kselva
几个月前,我问了同样的问题。许多人向我分享了他们的知识。现在,轮到我将答案传授给其他人 :) - mjb
1
我尝试了扩展实例,但在使用var fields = typeof(T).GetFields();时它没有返回字段。因此,对于自定义类,var fields = typeof(T).GetFields();可能无法正常工作。 经过一番探索,我发现应该使用var fields = typeof(T).GetRuntimeFields();我的解决方案无法放在一个评论中,所以下一个评论是解决方案。 - MacSpudster
1
使用 typeof(T).GetRuntimeFields(),你会得到类似于 <EmpName>k__BackingField 的字段名,这与 fieldInfo.Name == dc.ColumnName 不匹配,其中 dc.ColumnName 是 "EmpName"。相反,你需要将每个 dc.ColumnName 转换为与 fieldInfo.Name 匹配。我使用一个字符串模板 string fnTemplate = "<{0}>k__BackingField"; 然后使用 tFN = string.Format(fnTemplate, dc.ColumnName).ToLower(); 进行比较,如 if (fieldInfo.Name.ToLower() == tFN) - MacSpudster
显示剩余4条评论

3
您可以尝试这样做:
List<Employee> listObject = dTable.AsEnumerable()
                                  .Select(x => new Employee()
                                  {
                                    EmpId = x.Field<int>("EmpId"),
                                    EmpName = x.Field<string>("EmpName"),
                                    EmpAddress = x.Field<string>("EmpName"),
                                    EmpPhone = x.Field<string>("EmpPhone"),
                                    Status = x.Field<bool>("Status"),
                                    EmpRelationKey = x.Field<int>("EmpRelationKey")
                                  }).ToList();

但我不想提及列名。 - CodeCaster
请使用列索引而不是其他方式。 - sujith karivelil
是的,@sujithkarivelil,使用列索引也不是动态的,这正是kselva所寻求的... - MacSpudster

3

使用 LinQ,您可以做到:

  List<DataRow> list = Datatablle.AsEnumerable().ToList();

更新的答案。创建辅助类将Datatable转换为List。

public static class Helper
{

    public static List<T> ConvertDataTableToList<T>(this DataTable table) where T : class, new()
    {
        try
        {
            List<T> list = new List<T>();

            foreach (var row in table.AsEnumerable())
            {
                T obj = new T();

                foreach (var prop in obj.GetType().GetProperties())
                {
                    try
                    {
                        PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
                        propertyInfo.SetValue(obj, Convert.ChangeType(row[prop.Name], propertyInfo.PropertyType), null);
                    }
                    catch
                    {
                        continue;
                    }
                }

                list.Add(obj);
            }

            return list;
        }
        catch
        {
            return null;
        }
    }
}

那么,

DataTable dtDatatable = GetEmployee();
List<Employee> employeeList = dtDatatable.ConvertDataTableToList<Employee>();

我需要直接将 DataTable 的值分配给这个列表。 - kselva
List<Employee> employees = new List<Employee>(); - kselva
请检查更新后的答案。 - Manish Goswami
是的,我会更新答案,我现在正在将其应用于我的代码中。 - kselva
我在这行代码中遇到了错误:dtDatatable.ConvertDataTableToList<Employee>(); - kselva

0
如评论中所提到的,每当您对DataTable的结构进行任何更改时,这些更改必须在Class中进行,并且如果您更改了Class,则需要将这些更改与DataTable的结构同步。
List<Employee>employees = new List<Employee>();  

foreach (DataRow row in dt.Rows)  
{  
    Employee emp= new Employee();

    PropertyInfo[] properties = typeof(Employee).GetProperties();
    for (int i = 0; i < properties.Length; i++)
    {
        property.SetValue(emp, Convert.ChangeType(row[i], property.GetType()));
    }
}

不要忘记使用 System.Reflection;

1
请记住,如果您更改类的列名或属性,则此代码将产生不正确的结果。 - Mikhail Tulubaev
该属性.setValue代表什么意思,错误发生在那一行。 - kselva
“S” 是大写字母,不是小写字母。出现了什么错误? - Imad

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