Jak przekonwertować DataTable na ogólną listę?

185

Obecnie używam:

DataTable dt = CreateDataTableInSomeWay();

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

Czy istnieje lepszy / magiczny sposób?

Iain Holder
źródło
2
Co próbujesz osiągnąć za pomocą listy, której nie możesz zrobić z DataRowCollection?
Jason Kealey
Mój się spóźnia, ale nadzieja się przyda. Działające rozwiązanie .. stackoverflow.com/a/58607820/9048996
Thameem

Odpowiedzi:

276

Jeśli używasz .NET 3.5, możesz użyć DataTableExtensions.AsEnumerable(metody rozszerzenia), a jeśli naprawdę potrzebujesz List<DataRow>zamiast IEnumerable<DataRow>, możesz zadzwonić Enumerable.ToList:

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

lub

using System.Linq;
...
List<DataRow> list = dt.AsEnumerable().ToList();
Jon Skeet
źródło
jak przekonwertować to listna json.
ACP
6
@ Pandiya: Istnieją różne sposoby konwersji danych do JSON w .NET. Osobiście zawsze korzystałem z biblioteki JSON.NET, ale są też inne podejścia.
Jon Skeet,
1
@Jon Skeet: Chcę uzyskać wartość w DataRow. Czy jest jakaś metoda? Zamiast być jak list.ItemArray [0].
Ramesh Durai
@Jon, FYI: dt.AsEnumerable (). ToList () 'System.Data.EnumerableRowCollection <System.Data.DataRow>' nie zawiera definicji „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();
darszan pandya
źródło
Powyższy kod może nie działać Bcs. dt.Rows nie wdrożył „AsEnumerable”. Można to poprawić w następujący sposób: emp = (z wiersza DataRow w dt.AsEnumerable () wybierz nowego pracownika {_FirstName = wiersz [„FirstName”]. ToString (), _LastName = row [„Last_Name”]. ToString ()}) .Notować();
Navin Pandit
37

W wersji C # 3.0 i System.Data.DataSetExtensions.dll

List<DataRow> rows = table.Rows.Cast<DataRow>().ToList();
Marc Gravell
źródło
3
robienie tego pomogło wydajności po prostu używając foreacha nad datarow o 50% czasu.
lloydom
33

Możesz użyć

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

dt.Select()zwróci wszystkie wiersze w tabeli jako tablicę datarows, a Listkonstruktor zaakceptuje tę tablicę obiektów jako argument do początkowego wypełnienia listy.

Kibee
źródło
Select () nie potrzebuje żadnych parametrów. Przeciążenie bez parametrów zwróci wszystkie wiersze.
Kon
Dzięki, dostosowuję moją odpowiedź do twojej sugestii
Kibbee
15

Możesz utworzyć funkcję rozszerzenia jako:

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
źródło
Nie działa - patrz dotnetfiddle.net/I22r2c Należy również zauważyć, że używanie Reflection jest powolne i nie jest zalecane w kodzie krytycznym dla wydajności.
Almenon
Musisz dodać informacje o typie danych dla kolumn. 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
Działa teraz Dzięki.
Almenon
14

Jeśli chcesz tylko zwrócić listę wartości z pola int „ID”, możesz użyć ...

List<int> ids = (from row in dt.AsEnumerable() select Convert.ToInt32(row["ID"])).ToList();
Stuart
źródło
12

Dodałem trochę modyfikacji do kodu z tej odpowiedzi ( https://stackoverflow.com/a/24588210/4489664 ), ponieważ dla typów zerowalnych zwróci wyjątek

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
źródło
Usunąłem klasę, ponieważ może ona również pracować ze strukturą.
Mickey Perlstein,
To działa całkiem dobrze, dobra robota. Jeśli ktoś chce się pobawić kodem, stworzyłem skrzynkę
Almenon
@Almenon Dodałem małą modyfikację, powinna nieco zwiększyć wydajność
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
źródło
Znakomity! Uratowałeś mi dzień
Anynomous Khan
6

Ponownie, używając 3.5 możesz to zrobić w następujący sposób:

dt.Select().ToList()

BRGDS

Guilherme Duarte
źródło
4

Bardziej „magiczny” sposób i nie wymaga .NET 3.5.

Jeśli na przykład DBDatatablezwracała jedną kolumnę Guids (unikalny identyfikator w SQL), możesz użyć:

Dim gList As New List(Of Guid)
gList.AddRange(DirectCast(DBDataTable.Select(), IEnumerable(Of Guid)))
Barney
źródło
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
źródło
3

DataTable.Select() nie podaje wierszy w kolejności, w jakiej występowały w danych.

Jeśli kolejność jest ważna, wydaje mi się, że iteruję po kolekcji datarow i tworzenie listy jest właściwą drogą, możesz też użyć przeciążenia DataTable.Select(string filterexpression, string sort).

Ale to przeciążenie może nie obsłużyć całego porządku (jak kolejność według przypadków ...), który zapewnia SQL.

129206
źródło
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
źródło
1

Użyj System.Dataprzestrzeni nazw, a otrzymasz .AsEnumerable().

rajashekar
źródło
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
         */

Poniżej znajdują się dwie funkcje, w których przekazujemy tabelę danych i klasę zdefiniowaną przez użytkownika. Następnie zwróci listę tej klasy z danymi 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;
        }

Ta metoda sprawdzi typ danych pola i skonwertuje wartość dataTable na ten typ danych.

    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();
    }

Aby wywołać poprzednią metodę, użyj następującej składni:

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

Zmień nazwę klasy Studenta i wartość dt zgodnie z własnymi wymaganiami. W takim przypadku nazwa kolumny DataTable i nazwa właściwości klasy powinny być takie same, w przeciwnym razie ta funkcja nie będzie działać poprawnie.

Saurin
źródło
1
Dziękuję za odpowiedź. Byłoby wspaniale, gdybyś dodał również krótkie wyjaśnienie i kilka komentarzy do swojego kodu. Pomaga to ludziom lepiej zrozumieć twoją odpowiedź.
Andre Hofmeister
0

To działało dla mnie: Potrzebuję przynajmniej .Net Framework 3.5, poniższy kod wyświetla DataRow zwrócony do Generic.IEnumerable, comboBox1 został użyty dla lepszej ilustracji.

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
źródło
0

Wynik

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
źródło
0

Możemy użyć metody ogólnej do konwersji DataTablena Listzamiast ręcznej konwersji DataTablena List.

Uwaga: DataTable„s ColumnNamei Type” s PropertyNamepowinna być taka sama.

Wywołaj poniższą metodę:

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
źródło
0

Możesz użyć ogólnej metody takiej jak w przypadku listy danych generycznej

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
źródło
0

Konwertowanie DataTablena ogólnyDictionary

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;
}
Indygowiec
źródło
0

Użyj rozszerzenia:

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
źródło
0

Aby przypisać wiersze DataTable do ogólnej listy klas

  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
źródło
0

Najłatwiejszy sposób przekształcenia tabeli danych w ogólną listę klas

using Newtonsoft.Json;

var json = JsonConvert.SerializeObject(dataTable);
var model = JsonConvert.DeserializeObject<List<ClassName>>(json);
Maghalakshmi Saravana
źródło
0

możesz użyć następujących dwóch ogólnych funkcji

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;
    }

i użyj go w następujący sposób

List<StudentScanExamsDTO> studentDetails = ConvertDataTable<StudentScanExamsDTO>(dt);
hosam hemaily
źródło
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();

Link do działającego przykładu 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; }
}

}

pan R.
źródło