una net llenar lista convertir convert con array c# list generics datatable

c# - net - de lista a datatable



Convertir lista genérica/Enumerable a DataTable? (19)

Aquí hay una buena actualización de 2013 usando FastMember de NuGet:

IEnumerable<SomeType> data = ... DataTable table = new DataTable(); using(var reader = ObjectReader.Create(data)) { table.Load(reader); }

Esto utiliza la API de meta-programación de FastMember para el máximo rendimiento. Si desea restringirlo a miembros particulares (o hacer cumplir la orden), puede hacerlo también:

IEnumerable<SomeType> data = ... DataTable table = new DataTable(); using(var reader = ObjectReader.Create(data, "Id", "Name", "Description")) { table.Load(reader); }

Des / reclamante del editor: FastMember es un proyecto de Marc Gravell. ¡Es oro y lleno!

Sí, esto es exactamente lo contrario de this ; la reflexión sería suficiente, o si necesita más rápido, HyperDescriptor en 2.0, o tal vez Expression en 3.5. En realidad, HyperDescriptor debería ser más que adecuado.

Por ejemplo:

// remove "this" if not on C# 3.0 / .NET 3.5 public static DataTable ToDataTable<T>(this IList<T> data) { PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T)); DataTable table = new DataTable(); for(int i = 0 ; i < props.Count ; i++) { PropertyDescriptor prop = props[i]; table.Columns.Add(prop.Name, prop.PropertyType); } object[] values = new object[props.Count]; foreach (T item in data) { for (int i = 0; i < values.Length; i++) { values[i] = props[i].GetValue(item); } table.Rows.Add(values); } return table; }

Ahora, con una línea puede hacer esto muchas veces más rápido que la reflexión (habilitando HyperDescriptor para el tipo de objeto T ).

editar consulta de rendimiento; Aquí hay una plataforma de prueba con resultados:

Vanilla 27179 Hyper 6997

Sospecho que el cuello de botella ha pasado del acceso de los miembros al rendimiento de DataTable ... Dudo que mejore mucho en eso ...

código:

using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; public class MyData { public int A { get; set; } public string B { get; set; } public DateTime C { get; set; } public decimal D { get; set; } public string E { get; set; } public int F { get; set; } } static class Program { static void RunTest(List<MyData> data, string caption) { GC.Collect(GC.MaxGeneration, GCCollectionMode.Forced); GC.WaitForPendingFinalizers(); GC.WaitForFullGCComplete(); Stopwatch watch = Stopwatch.StartNew(); for (int i = 0; i < 500; i++) { data.ToDataTable(); } watch.Stop(); Console.WriteLine(caption + "/t" + watch.ElapsedMilliseconds); } static void Main() { List<MyData> foos = new List<MyData>(); for (int i = 0 ; i < 5000 ; i++ ){ foos.Add(new MyData { // just gibberish... A = i, B = i.ToString(), C = DateTime.Now.AddSeconds(i), D = i, E = "hello", F = i * 2 }); } RunTest(foos, "Vanilla"); Hyper.ComponentModel.HyperTypeDescriptionProvider.Add( typeof(MyData)); RunTest(foos, "Hyper"); Console.ReadLine(); // return to exit } }

Tengo pocos métodos que devuelven diferentes listas genéricas.

¿Existe en .net cualquier clase de método estático o lo que sea para convertir cualquier lista en una tabla de datos? Lo único que puedo imaginar es usar Reflexión para hacer esto.

Si tengo esto:

List<Whatever> whatever = new List<Whatever>();

(El siguiente código no funciona, por supuesto, pero me gustaría tener la posibilidad de:

DataTable dt = (DataTable) whatever;


Esta es la sencilla aplicación de consola para convertir Lista a Datatable.

using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data; using System.ComponentModel; namespace ConvertListToDataTable { public static class Program { public static void Main(string[] args) { List<MyObject> list = new List<MyObject>(); for (int i = 0; i < 5; i++) { list.Add(new MyObject { Sno = i, Name = i.ToString() + "-KarthiK", Dat = DateTime.Now.AddSeconds(i) }); } DataTable dt = ConvertListToDataTable(list); foreach (DataRow row in dt.Rows) { Console.WriteLine(); for (int x = 0; x < dt.Columns.Count; x++) { Console.Write(row[x].ToString() + " "); } } Console.ReadLine(); } public class MyObject { public int Sno { get; set; } public string Name { get; set; } public DateTime Dat { get; set; } } public static DataTable ConvertListToDataTable<T>(this List<T> iList) { DataTable dataTable = new DataTable(); PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T)); for (int i = 0; i < props.Count; i++) { PropertyDescriptor propertyDescriptor = props[i]; Type type = propertyDescriptor.PropertyType; if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) type = Nullable.GetUnderlyingType(type); dataTable.Columns.Add(propertyDescriptor.Name, type); } object[] values = new object[props.Count]; foreach (T iListItem in iList) { for (int i = 0; i < values.Length; i++) { values[i] = props[i].GetValue(iListItem); } dataTable.Rows.Add(values); } return dataTable; } } }


Esta es una mezcla simple de las soluciones. Funciona con tipos Nullable.

public static DataTable ToDataTable<T>(this IList<T> list) { PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T)); DataTable table = new DataTable(); for (int i = 0; i < props.Count; i++) { PropertyDescriptor prop = props[i]; table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); } object[] values = new object[props.Count]; foreach (T item in list) { for (int i = 0; i < values.Length; i++) values[i] = props[i].GetValue(item) ?? DBNull.Value; table.Rows.Add(values); } return table; }


Este enlace en MSDN merece una visita: Cómo: Implementar CopyToDataTable <T> Donde el Tipo T genérico no es un DataRow

Esto agrega un método de extensión que te permite hacer esto:

// Create a sequence. Item[] items = new Item[] { new Book{Id = 1, Price = 13.50, Genre = "Comedy", Author = "Gustavo Achong"}, new Book{Id = 2, Price = 8.50, Genre = "Drama", Author = "Jessie Zeng"}, new Movie{Id = 1, Price = 22.99, Genre = "Comedy", Director = "Marissa Barnes"}, new Movie{Id = 1, Price = 13.40, Genre = "Action", Director = "Emmanuel Fernandez"}}; // Query for items with price greater than 9.99. var query = from i in items where i.Price > 9.99 orderby i.Price select i; // Load the query results into new DataTable. DataTable table = query.CopyToDataTable();


La respuesta de Marc Gravell pero en VB.NET

Public Shared Function ToDataTable(Of T)(data As IList(Of T)) As DataTable Dim props As PropertyDescriptorCollection = TypeDescriptor.GetProperties(GetType(T)) Dim table As New DataTable() For i As Integer = 0 To props.Count - 1 Dim prop As PropertyDescriptor = props(i) table.Columns.Add(prop.Name, prop.PropertyType) Next Dim values As Object() = New Object(props.Count - 1) {} For Each item As T In data For i As Integer = 0 To values.Length - 1 values(i) = props(i).GetValue(item) Next table.Rows.Add(values) Next Return table End Function


Me doy cuenta de que esto ha estado cerrado por un tiempo; sin embargo, tuve una solución a este problema específico, pero necesitaba un pequeño giro: las columnas y la tabla de datos debían estar predefinidas / ya instanciadas. Entonces necesitaba simplemente insertar los tipos en la tabla de datos.

Así que aquí hay un ejemplo de lo que hice:

public static class Test { public static void Main() { var dataTable = new System.Data.DataTable(Guid.NewGuid().ToString()); var columnCode = new DataColumn("Code"); var columnLength = new DataColumn("Length"); var columnProduct = new DataColumn("Product"); dataTable.Columns.AddRange(new DataColumn[] { columnCode, columnLength, columnProduct }); var item = new List<SomeClass>(); item.Select(data => new { data.Id, data.Name, data.SomeValue }).AddToDataTable(dataTable); } } static class Extensions { public static void AddToDataTable<T>(this IEnumerable<T> enumerable, System.Data.DataTable table) { if (enumerable.FirstOrDefault() == null) { table.Rows.Add(new[] {string.Empty}); return; } var properties = enumerable.FirstOrDefault().GetType().GetProperties(); foreach (var item in enumerable) { var row = table.NewRow(); foreach (var property in properties) { row[property.Name] = item.GetType().InvokeMember(property.Name, BindingFlags.GetProperty, null, item, null); } table.Rows.Add(row); } } }


Otro enfoque es el anterior:

List<WhateEver> lst = getdata(); string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst); DataTable pDt = JsonConvert.DeserializeObject<DataTable>(json);


Se probó el método para que acepte campos con nulo.

// remove "this" if not on C# 3.0 / .NET 3.5 public static DataTable ToDataTable<T>(IList<T> data) { PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T)); DataTable table = new DataTable(); Type Propiedad = null; for (int i = 0; i < props.Count; i++) { PropertyDescriptor prop = props[i]; Propiedad = prop.PropertyType; if (Propiedad.IsGenericType && Propiedad.GetGenericTypeDefinition() == typeof(Nullable<>)) { Propiedad = Nullable.GetUnderlyingType(Propiedad); } table.Columns.Add(prop.Name, Propiedad); } object[] values = new object[props.Count]; foreach (T item in data) { for (int i = 0; i < values.Length; i++) { values[i] = props[i].GetValue(item); } table.Rows.Add(values); } return table; }


Si está utilizando VB.NET, esta clase hace el trabajo.

Imports System.Reflection '''''' <summary> '''''' Convert any List(Of T) to a DataTable with correct column types and converts Nullable Type values to DBNull '''''' </summary> Public Class ConvertListToDataset Public Function ListToDataset(Of T)(ByVal list As IList(Of T)) As DataTable Dim dt As New DataTable() ''/* Create the DataTable columns */ For Each pi As PropertyInfo In GetType(T).GetProperties() If pi.PropertyType.IsValueType Then Debug.Print(pi.Name) End If If IsNothing(Nullable.GetUnderlyingType(pi.PropertyType)) Then dt.Columns.Add(pi.Name, pi.PropertyType) Else dt.Columns.Add(pi.Name, Nullable.GetUnderlyingType(pi.PropertyType)) End If Next ''/* Populate the DataTable with the values in the Items in List */ For Each item As T In list Dim dr As DataRow = dt.NewRow() For Each pi As PropertyInfo In GetType(T).GetProperties() dr(pi.Name) = IIf(IsNothing(pi.GetValue(item)), DBNull.Value, pi.GetValue(item)) Next dt.Rows.Add(dr) Next Return dt End Function End Class


Si tiene propiedades en su clase, ¡esta línea de código está bien!

PropertyDescriptorCollection props = TypeDescriptor.GetProperties(typeof(T));

pero si tiene todos los campos públicos, use esto:

public static DataTable ToDataTable<T>( IList<T> data) { FieldInfo[] myFieldInfo; Type myType = typeof(T); // Get the type and fields of FieldInfoClass. myFieldInfo = myType.GetFields(BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Public); DataTable dt = new DataTable(); for (int i = 0; i < myFieldInfo.Length; i++) { FieldInfo property = myFieldInfo[i]; dt.Columns.Add(property.Name, property.FieldType); } object[] values = new object[myFieldInfo.Length]; foreach (T item in data) { for (int i = 0; i < values.Length; i++) { values[i] = myFieldInfo[i].GetValue(item); } dt.Rows.Add(values); } return dt; }

La respuesta original es desde arriba, acabo de editar para usar campos en lugar de propiedades.

y para usarlo haz esto

DataTable dt = new DataTable(); dt = ToDataTable(myBriefs); gridData.DataSource = dt; gridData.DataBind();


También es posible a través de XmlSerialization. La idea es: serializar a XML y luego al método readXml de DataSet.

Yo uso este código (de una respuesta en SO, se me olvidó dónde)

public static string SerializeXml<T>(T value) where T : class { if (value == null) { return null; } XmlSerializer serializer = new XmlSerializer(typeof(T)); XmlWriterSettings settings = new XmlWriterSettings(); settings.Encoding = new UnicodeEncoding(false, false); settings.Indent = false; settings.OmitXmlDeclaration = false; // no BOM in a .NET string using (StringWriter textWriter = new StringWriter()) { using (XmlWriter xmlWriter = XmlWriter.Create(textWriter, settings)) { serializer.Serialize(xmlWriter, value); } return textWriter.ToString(); } }

entonces es tan simple como:

string xmlString = Utility.SerializeXml(trans.InnerList); DataSet ds = new DataSet("New_DataSet"); using (XmlReader reader = XmlReader.Create(new StringReader(xmlString))) { ds.Locale = System.Threading.Thread.CurrentThread.CurrentCulture; ds.ReadXml(reader); }

No estoy seguro de cómo se enfrenta a todas las otras respuestas de esta publicación, pero también es una posibilidad.


También tuve que encontrar una solución alternativa, ya que ninguna de las opciones enumeradas aquí funcionó en mi caso. Estaba usando un IEnumerable que devolvió un IEnumerable y no se pudieron enumerar las propiedades. Esto hizo el truco:

// remove "this" if not on C# 3.0 / .NET 3.5 public static DataTable ConvertToDataTable<T>(this IEnumerable<T> data) { List<IDataRecord> list = data.Cast<IDataRecord>().ToList(); PropertyDescriptorCollection props = null; DataTable table = new DataTable(); if (list != null && list.Count > 0) { props = TypeDescriptor.GetProperties(list[0]); for (int i = 0; i < props.Count; i++) { PropertyDescriptor prop = props[i]; table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); } } if (props != null) { object[] values = new object[props.Count]; foreach (T item in data) { for (int i = 0; i < values.Length; i++) { values[i] = props[i].GetValue(item) ?? DBNull.Value; } table.Rows.Add(values); } } return table; }


Tuve que modificar el código de muestra de Mark Gravell para manejar los tipos anulables y los valores nulos. He incluido una versión de trabajo a continuación. Gracias Mark.

public static DataTable ToDataTable<T>(this IList<T> data) { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T)); DataTable table = new DataTable(); foreach (PropertyDescriptor prop in properties) table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); foreach (T item in data) { DataRow row = table.NewRow(); foreach (PropertyDescriptor prop in properties) row[prop.Name] = prop.GetValue(item) ?? DBNull.Value; table.Rows.Add(row); } return table; }


Un pequeño cambio en la respuesta de Mark para que funcione con tipos de valor como List<string> a la tabla de datos:

public static DataTable ListToDataTable<T>(IList<T> data) { DataTable table = new DataTable(); //special handling for value types and string if (typeof(T).IsValueType || typeof(T).Equals(typeof(string))) { DataColumn dc = new DataColumn("Value"); table.Columns.Add(dc); foreach (T item in data) { DataRow dr = table.NewRow(); dr[0] = item; table.Rows.Add(dr); } } else { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T)); foreach (PropertyDescriptor prop in properties) { table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); } foreach (T item in data) { DataRow row = table.NewRow(); foreach (PropertyDescriptor prop in properties) { try { row[prop.Name] = prop.GetValue(item) ?? DBNull.Value; } catch (Exception ex) { row[prop.Name] = DBNull.Value; } } table.Rows.Add(row); } } return table; }


Yo mismo he escrito una pequeña biblioteca para realizar esta tarea. Utiliza la reflexión solo por primera vez que un tipo de objeto se va a traducir a una base de datos. Emite un método que hará todo el trabajo traduciendo un tipo de objeto.

Está ardiendo rápido. Lo puedes encontrar aquí: ModelShredder en GoogleCode


prueba esto

public static DataTable ListToDataTable<T>(IList<T> lst) { currentDT = CreateTable<T>(); Type entType = typeof(T); PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType); foreach (T item in lst) { DataRow row = currentDT.NewRow(); foreach (PropertyDescriptor prop in properties) { if (prop.PropertyType == typeof(Nullable<decimal>) || prop.PropertyType == typeof(Nullable<int>) || prop.PropertyType == typeof(Nullable<Int64>)) { if (prop.GetValue(item) == null) row[prop.Name] = 0; else row[prop.Name] = prop.GetValue(item); } else row[prop.Name] = prop.GetValue(item); } currentDT.Rows.Add(row); } return currentDT; } public static DataTable CreateTable<T>() { Type entType = typeof(T); DataTable tbl = new DataTable(DTName); PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType); foreach (PropertyDescriptor prop in properties) { if (prop.PropertyType == typeof(Nullable<decimal>)) tbl.Columns.Add(prop.Name, typeof(decimal)); else if (prop.PropertyType == typeof(Nullable<int>)) tbl.Columns.Add(prop.Name, typeof(int)); else if (prop.PropertyType == typeof(Nullable<Int64>)) tbl.Columns.Add(prop.Name, typeof(Int64)); else tbl.Columns.Add(prop.Name, prop.PropertyType); } return tbl; }


using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.Web.UI; using System.Web.UI.WebControls; using System.Data; using System.ComponentModel; public partial class Default3 : System.Web.UI.Page { protected void Page_Load(object sender, EventArgs e) { DataTable dt = new DataTable(); dt = lstEmployee.ConvertToDataTable(); } public static DataTable ConvertToDataTable<T>(IList<T> list) where T : class { try { DataTable table = CreateDataTable<T>(); Type objType = typeof(T); PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(objType); foreach (T item in list) { DataRow row = table.NewRow(); foreach (PropertyDescriptor property in properties) { if (!CanUseType(property.PropertyType)) continue; row[property.Name] = property.GetValue(item) ?? DBNull.Value; } table.Rows.Add(row); } return table; } catch (DataException ex) { return null; } catch (Exception ex) { return null; } } private static DataTable CreateDataTable<T>() where T : class { Type objType = typeof(T); DataTable table = new DataTable(objType.Name); PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(objType); foreach (PropertyDescriptor property in properties) { Type propertyType = property.PropertyType; if (!CanUseType(propertyType)) continue; //nullables must use underlying types if (propertyType.IsGenericType && propertyType.GetGenericTypeDefinition() == typeof(Nullable<>)) propertyType = Nullable.GetUnderlyingType(propertyType); //enums also need special treatment if (propertyType.IsEnum) propertyType = Enum.GetUnderlyingType(propertyType); table.Columns.Add(property.Name, propertyType); } return table; } private static bool CanUseType(Type propertyType) { //only strings and value types if (propertyType.IsArray) return false; if (!propertyType.IsValueType && propertyType != typeof(string)) return false; return true; } }


Dim counties As New List(Of County) Dim dtCounties As DataTable dtCounties = _combinedRefRepository.Get_Counties() If dtCounties.Rows.Count <> 0 Then For Each row As DataRow In dtCounties.Rows Dim county As New County county.CountyId = row.Item(0).ToString() county.CountyName = row.Item(1).ToString().ToUpper() counties.Add(county) Next dtCounties.Dispose() End If


public DataTable ConvertToDataTable<T>(IList<T> data) { PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(typeof(T)); DataTable table = new DataTable(); foreach (PropertyDescriptor prop in properties) table.Columns.Add(prop.Name, Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType); foreach (T item in data) { DataRow row = table.NewRow(); foreach (PropertyDescriptor prop in properties) row[prop.Name] = prop.GetValue(item) ?? DBNull.Value; table.Rows.Add(row); } return table; }