¿Cómo se convierte una DataTable en una lista genérica?

185

Actualmente estoy usando:

DataTable dt = CreateDataTableInSomeWay();

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

¿Hay una manera mejor / mágica?

Iain Holder
fuente
2
¿Qué está tratando de lograr con una Lista que no puede hacer con su DataRowCollection?
Jason Kealey
La mía llega tarde pero la esperanza será útil. Solución de trabajo .. stackoverflow.com/a/58607820/9048996
Thameem

Respuestas:

276

Si está usando .NET 3.5, puede usar DataTableExtensions.AsEnumerable(un método de extensión) y luego, si realmente necesita un en List<DataRow>lugar de solo IEnumerable<DataRow>puede llamar Enumerable.ToList:

IEnumerable<DataRow> sequence = dt.AsEnumerable();

o

using System.Linq;
...
List<DataRow> list = dt.AsEnumerable().ToList();
Jon Skeet
fuente
cómo convertir esto lista json.
ACP
66
@Pandiya: Hay varias formas de convertir datos a JSON en .NET. Personalmente, siempre he usado la biblioteca JSON.NET, pero también hay otros enfoques.
Jon Skeet
1
@ Jon Skeet: Quiero obtener el valor en DataRow. ¿Hay algún método? En lugar de obtener como list.ItemArray [0].
Ramesh Durai
@ Jon, para su información: dt.AsEnumerable (). ToList () 'System.Data.EnumerableRowCollection <System.Data.DataRow>' no contiene una definición para 'ToList'
Pradip
66
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();
Darshan Pandya
fuente
El código anterior puede no funcionar bcs. dt.Rows no ha implementado 'AsEnumerable'. Esto se puede corregir de la siguiente manera: emp = (desde la fila DataRow en dt.AsEnumerable () seleccione un nuevo Empleado {_FirstName = row ["FirstName"]. ToString (), _LastName = row ["Last_Name"]. ToString ()}) .Listar();
Navin Pandit
37

Con C # 3.0 y System.Data.DataSetExtensions.dll,

List<DataRow> rows = table.Rows.Cast<DataRow>().ToList();
Marc Gravell
fuente
3
hacer esto ha ayudado al rendimiento con solo usar un foreach sobre un datarow en un 50% de tiempo.
lloydom
33

Podrías usar

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

dt.Select()devolverá todas las filas de su tabla, como una matriz de líneas de datos, y el Listconstructor acepta esa matriz de objetos como un argumento para llenar inicialmente su lista.

Kibbee
fuente
Select () no necesita ningún parámetro. La sobrecarga sin parámetros devolvería todas las filas.
Kon
Gracias, ajustando mi respuesta para que se ajuste a su sugerencia
Kibbee
15

Puede crear una función de extensión como:

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>();
Rahul Garg
fuente
No funciona: consulte dotnetfiddle.net/I22r2c También debe tenerse en cuenta que el uso de Reflection es lento y no se recomienda en el código crítico de rendimiento.
Almenon
Debe agregar información del tipo de datos para las columnas. DataTable dt = new DataTable (); dt.Columns.Add ("id", typeof (Int32)); dt.Columns.Add ("nombre", typeof (String)); dt.Columns.Add ("foo", typeof (DateTime)); for (int i = 0; i <= 1000; i ++) {dt.Rows.Add (i, "foo", DateTime.Now);}
Rahul Garg
Funciona ahora. Gracias.
Almenon
14

Si solo desea que se devuelva una lista de valores del campo int "ID", puede usar ...

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

He agregado algunas modificaciones al código de esta respuesta ( https://stackoverflow.com/a/24588210/4489664 ) porque para los tipos anulables devolverá una excepción

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;
}
Bondaryuk Vladimir
fuente
Eliminé la clase porque también puede funcionar con struct.
Mickey Perlstein
Esto funciona bastante bien, buen trabajo. Si alguien quiere jugar con el código, creé un violín .net en este enlace: dotnetfiddle.net/mTKevy
Almenon
@Almenon He agregado una pequeña modificación, debería aumentar un poco el rendimiento
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();
Morteza
fuente
¡Brillante! Me salvaste el día
Anynomous Khan
6

Nuevamente, usando 3.5 puede hacerlo como:

dt.Select().ToList()

BRGDS

Guilherme Duarte
fuente
4

Una forma más 'mágica', y no necesita .NET 3.5.

Si, por ejemplo, DBDatatabledevuelve una sola columna de Guías (identificador único en SQL), puede usar:

Dim gList As New List(Of Guid)
gList.AddRange(DirectCast(DBDataTable.Select(), IEnumerable(Of Guid)))
Barney
fuente
4
// 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;
}
Nathan
fuente
3

DataTable.Select() no da las Filas en el orden en que estaban presentes en la tabla de datos.

Si el orden es importante, creo que iterar sobre la colección de datos y formar una Lista es el camino correcto o también podría usar la sobrecarga de DataTable.Select(string filterexpression, string sort).

Pero esta sobrecarga puede no manejar todos los pedidos (como ordenar por caso ...) que proporciona SQL.

usuario129206
fuente
3
DataTable dt;   // datatable should contains datacolumns with Id,Name

List<Employee> employeeList=new List<Employee>();  // Employee should contain  EmployeeId, EmployeeName as properties

foreach (DataRow dr in dt.Rows)
{
    employeeList.Add(new Employee{EmployeeId=dr.Id,EmplooyeeName=dr.Name});
}
syed ali abbas
fuente
1

Use el System.Dataespacio de nombres y obtendrá .AsEnumerable().

rajashekar
fuente
1
        /* This is a generic method that will convert any type of DataTable to a List 
         * 
         * 
         * Example :    List< Student > studentDetails = new List< Student >();  
         *              studentDetails = ConvertDataTable< Student >(dt);  
         *
         * Warning : In this case the DataTable column's name and class property name
         *           should be the same otherwise this function will not work properly
         */

Las siguientes son las dos funciones en las que si pasamos una DataTable y una clase definida por el usuario. Luego devolverá la Lista de esa clase con los datos de DataTable.

        public static List<T> ConvertDataTable<T>(DataTable dt)
        {
            List<T> data = new List<T>();
            foreach (DataRow row in dt.Rows)
            {
                T item = GetItem<T>(row);
                data.Add(item);
            }
            return data;
        }


        private static T GetItem<T>(DataRow dr)
        {
            Type temp = typeof(T);
            T obj = Activator.CreateInstance<T>();

            foreach (DataColumn column in dr.Table.Columns)
            {
                foreach (PropertyInfo pro in temp.GetProperties())
                {
                   //in case you have a enum/GUID datatype in your model
                   //We will check field's dataType, and convert the value in it.
                    if (pro.Name == column.ColumnName){                
                    try
                    {
                        var convertedValue = GetValueByDataType(pro.PropertyType, dr[column.ColumnName]);
                        pro.SetValue(obj, convertedValue, null);
                    }
                    catch (Exception e)
                    {         
                       //ex handle code                   
                        throw;
                    }
                        //pro.SetValue(obj, dr[column.ColumnName], null);
                }
                    else
                        continue;
                }
            }
            return obj;
        }

Este método verificará el tipo de datos del campo y convertirá el valor de dataTable en ese tipo de datos.

    private static object GetValueByDataType(Type propertyType, object o)
    {
        if (o.ToString() == "null")
        {
            return null;
        }
        if (propertyType == (typeof(Guid)) || propertyType == typeof(Guid?))
        {
            return Guid.Parse(o.ToString());
        }
        else if (propertyType == typeof(int) || propertyType.IsEnum) 
        {
            return Convert.ToInt32(o);
        }
        else if (propertyType == typeof(decimal) )
        {
            return Convert.ToDecimal(o);
        }
        else if (propertyType == typeof(long))
        {
            return Convert.ToInt64(o);
        }
        else if (propertyType == typeof(bool) || propertyType == typeof(bool?))
        {
            return Convert.ToBoolean(o);
        }
        else if (propertyType == typeof(DateTime) || propertyType == typeof(DateTime?))
        {
            return Convert.ToDateTime(o);
        }
        return o.ToString();
    }

Para llamar al método anterior, use la siguiente sintaxis:

List< Student > studentDetails = new List< Student >();  
studentDetails = ConvertDataTable< Student >(dt); 

Cambie el nombre de la clase del estudiante y el valor dt según sus requisitos. En este caso, el nombre de la columna DataTable y el nombre de la propiedad de clase deberían ser los mismos, de lo contrario, esta función no funcionará correctamente.

Saurin
fuente
1
Gracias por tu respuesta. Sería genial si también agregara una breve explicación y algunos comentarios a su código. Esto ayuda a las personas a comprender mejor su respuesta.
Andre Hofmeister el
0

Esto funcionó para mí: necesito al menos .Net Framework 3.5, el siguiente código muestra DataRow convertido en Generic.IEnumerable, comboBox1 se ha utilizado para una mejor ilustración.

using System.Linq;

DataTable dt = new DataTable();            
dt = myClass.myMethod();                 
List<object> list = (from row in dt.AsEnumerable() select (row["name"])).ToList();
comboBox1.DataSource = list;
Levi
fuente
0

Salida

public class ModelUser
{
    #region Model

    private string _username;
    private string _userpassword;
    private string _useremail;
    private int _userid;

    /// <summary>
    /// 
    /// </summary>
    public int userid
    {
        set { _userid = value; }
        get { return _userid; }
    }


    /// <summary>
    /// 
    /// </summary>

    public string username
    {
        set { _username = value; }
        get { return _username; }
    }

    /// <summary>
    /// 
    /// </summary>
    public string useremail
    {
        set { _useremail = value; }
        get { return _useremail; }
    }

    /// <summary>
    /// 
    /// </summary>
    public string userpassword
    {
        set { _userpassword = value; }
        get { return _userpassword; }
    }
    #endregion Model
}

public List<ModelUser> DataTableToList(DataTable dt)
{
    List<ModelUser> modelList = new List<ModelUser>();
    int rowsCount = dt.Rows.Count;
    if (rowsCount > 0)
    {
        ModelUser model;
        for (int n = 0; n < rowsCount; n++)
        {
            model = new ModelUser();

            model.userid = (int)dt.Rows[n]["userid"];
            model.username = dt.Rows[n]["username"].ToString();
            model.useremail = dt.Rows[n]["useremail"].ToString();
            model.userpassword = dt.Rows[n]["userpassword"].ToString();

            modelList.Add(model);
        }
    }
    return modelList;
}

static DataTable GetTable()
{
    // Here we create a DataTable with four columns.
    DataTable table = new DataTable();
    table.Columns.Add("userid", typeof(int));
    table.Columns.Add("username", typeof(string));
    table.Columns.Add("useremail", typeof(string));
    table.Columns.Add("userpassword", typeof(string));

    // Here we add five DataRows.
    table.Rows.Add(25, "Jame", "[email protected]", DateTime.Now.ToString());
    table.Rows.Add(50, "luci", "[email protected]", DateTime.Now.ToString());
    table.Rows.Add(10, "Andrey", "[email protected]", DateTime.Now.ToString());
    table.Rows.Add(21, "Michael", "[email protected]", DateTime.Now.ToString());
    table.Rows.Add(100, "Steven", "[email protected]", DateTime.Now.ToString());
    return table;
}

protected void Page_Load(object sender, EventArgs e)
{
    List<ModelUser> userList = new List<ModelUser>();

    DataTable dt = GetTable();

    userList = DataTableToList(dt);

    gv.DataSource = userList;
    gv.DataBind();
}[enter image description here][1]

</asp:GridView>
</div>
mrtwin
fuente
0

Podemos usar un método genérico para convertir DataTablea en Listlugar de convertir manualmente DataTableaList .

Nota: DataTable's ColumnNamey Type' sPropertyName debe ser el mismo.

Llame al siguiente método:

long result = Utilities.ConvertTo<Student>(dt ,out listStudent);

// Generic Method
public class Utilities
{
    public static long ConvertTo<T>(DataTable table, out List<T> entity)
    {
        long returnCode = -1;
        entity = null;

        if (table == null)
        {
            return -1;
        }

        try
        {
            entity = ConvertTo<T>(table.Rows);
            returnCode = 0;
        }

        catch (Exception ex)
        {
            returnCode = 1000;
        }

        return returnCode;
    }

    static List<T> ConvertTo<T>(DataRowCollection rows)
    {
        List<T> list = null;
        if (rows != null)
        {
            list = new List<T>();

            foreach (DataRow row in rows)
            {
                T item = CreateItem<T>(row);
                list.Add(item);
            }
        }

        return list;
    }

    static T CreateItem<T>(DataRow row)
    {
        string str = string.Empty;
        string strObj = string.Empty;

        T obj = default(T);

        if (row != null)
        {
            obj = Activator.CreateInstance<T>();
            strObj = obj.ToString();
            NameValueCollection objDictionary = new NameValueCollection();

            foreach (DataColumn column in row.Table.Columns)
            {
                PropertyInfo prop = obj.GetType().GetProperty(column.ColumnName);

                if (prop != null)
                {
                    str = column.ColumnName;

                    try
                    {
                        objDictionary.Add(str, row[str].ToString());
                        object value = row[column.ColumnName];
                        Type vType = obj.GetType();

                        if (value == DBNull.Value)
                        {
                            if (vType == typeof(int) || vType == typeof(Int16)
                                                     || vType == typeof(Int32)
                                                     || vType == typeof(Int64)
                                                     || vType == typeof(decimal)
                                                     || vType == typeof(float)
                                                     || vType == typeof(double))
                            {
                                value = 0;
                            }

                            else if (vType == typeof(bool))
                            {
                                value = false;
                            }

                            else if (vType == typeof(DateTime))
                            {
                                value = DateTime.MaxValue;
                            }

                            else
                            {
                                value = null;
                            }

                            prop.SetValue(obj, value, null);
                        }

                        else
                        {
                            prop.SetValue(obj, value, null);
                        }
                    }

                    catch(Exception ex)
                    {

                    }
                }
            }

            PropertyInfo ActionProp = obj.GetType().GetProperty("ActionTemplateValue");

            if (ActionProp != null)
            {
                object ActionValue = objDictionary;
                ActionProp.SetValue(obj, ActionValue, null);
            }
        }

        return obj;
    }
}
Jayaprakash
fuente
0

Puede usar un método genérico como ese para datatable a lista genérica

public static List<T> DataTableToList<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);
                    if (propertyInfo.PropertyType.IsEnum)
                    {
                        propertyInfo.SetValue(obj, Enum.Parse(propertyInfo.PropertyType, row[prop.Name].ToString()));
                    }
                    else
                    {
                        propertyInfo.SetValue(obj, Convert.ChangeType(row[prop.Name], propertyInfo.PropertyType), null);
                    }                          
                }
                catch
                {
                    continue;
                }
            }

            list.Add(obj);
        }

        return list;
    }
    catch
    {
        return null;
    }
}
Ömer Ceylan
fuente
0

Convertir DataTablea genéricoDictionary

public static Dictionary<object,IList<dynamic>> DataTable2Dictionary(DataTable dt)
{
    Dictionary<object, IList<dynamic>> dict = new Dictionary<dynamic, IList<dynamic>>();

    foreach(DataColumn column in dt.Columns)
    {
        IList<dynamic> ts = dt.AsEnumerable()
                              .Select(r => r.Field<dynamic>(column.ToString()))
                              .ToList();
        dict.Add(column, ts);
    }
    return dict;
}
Añil
fuente
0

Usar extensión:

public static class Extensions
{
    #region Convert Datatable To List
    public static IList<T> ToList<T>(this DataTable table) where T : new()
    {
        IList<PropertyInfo> properties = typeof(T).GetProperties().ToList();
        IList<T> result = new List<T>();

        foreach (var row in table.Rows)
        {
            var item = CreateItemFromRow<T>((DataRow)row, properties);
            result.Add(item);
        }
        return result;
    }
    private static T CreateItemFromRow<T>(DataRow row, IList<PropertyInfo> properties) where T : new()
    {
        T item = new T();
        foreach (var property in properties)
        {
            property.SetValue(item, row[property.Name], null);
        }
        return item;
    }
    #endregion
}
Mohamed Mostafa
fuente
0

Para asignar las filas de DataTable a la lista genérica de clase

  List<Candidate> temp = new List<Candidate>();//List that holds the Candidate Class,
    //Note:The Candidate class contains RollNo,Name and Department
    //tb is DataTable
    temp = (from DataRow dr in tb.Rows
                        select new Candidate()
                        {
                            RollNO = Convert.ToInt32(dr["RollNO"]),
                            Name = dr["Name"].ToString(),
                            Department = dr["Department"].ToString(),

                        }).ToList();
Maghalakshmi Saravana
fuente
0

La forma más fácil de convertir DataTable en la lista genérica de clase

usando Newtonsoft.Json;

var json = JsonConvert.SerializeObject(dataTable);
var model = JsonConvert.DeserializeObject<List<ClassName>>(json);
Maghalakshmi Saravana
fuente
0

puedes usar las siguientes dos funciones genéricas

private static List<T> ConvertDataTable<T>(DataTable dt)
    {
        List<T> data = new List<T>();
        foreach (DataRow row in dt.Rows)
        {
            T item = GetItem<T>(row);
            data.Add(item);
        }
        return data;
    }
    private static T GetItem<T>(DataRow dr)
    {

        Type temp = typeof(T);
        T obj = Activator.CreateInstance<T>();

        foreach (DataColumn column in dr.Table.Columns)
        {
            foreach (PropertyInfo pro in temp.GetProperties())
            {
                if (pro.Name == column.ColumnName)
                    pro.SetValue(obj, dr[column.ColumnName].ToString(), null);
                else
                    continue;
            }
        }
        return obj;
    }

y úsalo de la siguiente manera

List<StudentScanExamsDTO> studentDetails = ConvertDataTable<StudentScanExamsDTO>(dt);
hosam hemaily
fuente
0
lPerson = dt.AsEnumerable().Select(s => new Person()
        {
            Name = s.Field<string>("Name"),
            SurName = s.Field<string>("SurName"),
            Age = s.Field<int>("Age"),
            InsertDate = s.Field<DateTime>("InsertDate")
        }).ToList();

Enlace al ejemplo de trabajo DotNetFiddle

using System;
using System.Collections.Generic;   
using System.Data;
using System.Linq;
using System.Data.DataSetExtensions;

public static void Main()
{
    DataTable dt = new DataTable();
    dt.Columns.Add("Name", typeof(string));
    dt.Columns.Add("SurName", typeof(string));
    dt.Columns.Add("Age", typeof(int));
    dt.Columns.Add("InsertDate", typeof(DateTime));

    var row1= dt.NewRow();
    row1["Name"] = "Adam";
    row1["SurName"] = "Adam";
    row1["Age"] = 20;
    row1["InsertDate"] = new DateTime(2020, 1, 1);
    dt.Rows.Add(row1);

    var row2 = dt.NewRow();
    row2["Name"] = "John";
    row2["SurName"] = "Smith";
    row2["Age"] = 25;
    row2["InsertDate"] = new DateTime(2020, 3, 12);
    dt.Rows.Add(row2);

    var row3 = dt.NewRow();
    row3["Name"] = "Jack";
    row3["SurName"] = "Strong";
    row3["Age"] = 32;
    row3["InsertDate"] = new DateTime(2020, 5, 20);
    dt.Rows.Add(row3);

    List<Person> lPerson = new List<Person>();
    lPerson = dt.AsEnumerable().Select(s => new Person()
    {
        Name = s.Field<string>("Name"),
        SurName = s.Field<string>("SurName"),
        Age = s.Field<int>("Age"),
        InsertDate = s.Field<DateTime>("InsertDate")
    }).ToList();

    foreach(Person pers in lPerson)
    {
        Console.WriteLine("{0} {1} {2} {3}", pers.Name, pers.SurName, pers.Age, pers.InsertDate);
    }
}   

public class Person
{
    public string Name { get; set; }
    public string SurName { get; set; }
    public int Age { get; set; }
    public DateTime InsertDate { get; set; }
}

}

Sr. R
fuente