将genericsList / Enumerable转换为DataTable?
我有几个方法返回不同的通用列表。
.net中存在任何类的静态方法或任何将任何列表转换为数据表? 唯一可以想象的是使用reflection来做到这一点。
如果我有这个:
List<Whatever> whatever = new List<Whatever>();
(这个下一个代码当然不行,但是我想有这个可能性:
DataTable dt = (DataTable) whatever;
以下是使用NuGet的FastMember进行的 2013年更新:
IEnumerable<SomeType> data = ... DataTable table = new DataTable(); using(var reader = ObjectReader.Create(data)) { table.Load(reader); }
这使用FastMember的元编程API来获得最佳性能。 如果你想限制它到特定的成员(或强制执行),那么你也可以这样做:
IEnumerable<SomeType> data = ... DataTable table = new DataTable(); using(var reader = ObjectReader.Create(data, "Id", "Name", "Description")) { table.Load(reader); }
Editor's Dis / claimer : FastMember是一个Marc Gravell项目。 它的黄金和全面飞行!
是的,这个和这个完全相反。 reflection就足够了 – 或者如果你需要更快的HyperDescriptor
,2.0中的HyperDescriptor
,或者3.5中的Expression
。 实际上, HyperDescriptor
应该足够了。
例如:
// 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; }
现在用一条线可以比reflection快得多(通过为对象typesT
启用HyperDescriptor
)。
编辑重新性能查询; 这里有一个结果testing平台:
Vanilla 27179 Hyper 6997
我怀疑瓶颈已经从成员访问转移到DataTable
性能…我怀疑你会提高很多…
码:
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 } }
我不得不修改Mark Gravell的示例代码来处理可为空的types和空值。 我已经在下面包含了一个工作版本。 谢谢马克。
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; }
这是解决scheme的简单组合。 它与Nullabletypes一起工作。
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; }
对Mark的答案进行一些小修改,使它能够像List<string>
到数据表一样使用值types:
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; }
也可以通过XmlSerialization。 这个想法是 – 序列化为XML,然后读取DataSet的XML方法。
我使用这个代码(从SO的答案,忘了在哪里)
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(); } }
那么就像下面这样简单:
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); }
不知道如何反对这篇文章的所有其他答案,但也是一种可能性。
MSDN上的此链接值得一访: 如何:实现CopyToDataTable <T>其中genericstypesT不是DataRow
这增加了一个扩展方法,可以让你这样做:
// 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();
我自己写了一个小库来完成这个任务。 它仅在第一次将对象types转换为数据表时使用reflection。 它发出一个方法来完成翻译对象types的所有工作。
它的燃烧速度很快。 你可以在这里find它: ModelShredder on GoogleCode
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; }
尝试这个
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; }
Marc Gravell的答案,但在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
我也不得不提出一个替代解决scheme,因为这里列出的所有选项都不适用于我的情况。 我正在使用IEnumerable返回一个IEnumerable和属性不能被枚举。 这个诀窍:
// 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; }
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; } }
我意识到这已经closures了一段时间; 然而,我有一个解决这个特定的问题,但需要一个轻微的扭曲:列和数据表需要预定义/已经实例化。 然后,我需要将这些types插入到数据表中。
所以这是我做的一个例子:
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); } } }
这是将List转换为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; } } }
Seprobóelmétodopara que acepte campos con null。
// 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; }
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
以上是另一种方法:
List<WhateEver> lst = getdata(); string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst); DataTable pDt = JsonConvert.DeserializeObject<DataTable>(json);