我有几个返回不同通用列表的方法.
在.net中存在任何类静态方法或将任何列表转换为数据表的任何东西?我唯一可以想象的是使用Reflection来做到这一点.
如果我有这个:
Listwhatever = new List ();
(下一个代码当然不起作用,但我想有可能:
DataTable dt = (DataTable) whatever;
Marc Gravell.. 310
这是使用NuGet的FastMember进行的 2013年更新:
IEnumerabledata = ... DataTable table = new DataTable(); using(var reader = ObjectReader.Create(data)) { table.Load(reader); }
这使用FastMember的元编程API来获得最佳性能.如果您想将其限制为特定成员(或强制执行订单),那么您也可以这样做:
IEnumerabledata = ... DataTable table = new DataTable(); using(var reader = ObjectReader.Create(data, "Id", "Name", "Description")) { table.Load(reader); }
编辑的Dis/claimer : FastMember是一个Marc Gravell项目.它的黄金和全飞!
是的,这几乎是完全相反的这一个; 反射就足够了 - 或者如果你需要更快,HyperDescriptor
在2.0,或者Expression
在3.5.实际上,HyperDescriptor
应该绰绰有余.
例如:
// remove "this" if not on C# 3.0 / .NET 3.5 public static DataTable ToDataTable(this IList 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; }
现在使用一行,您可以比反射快许多倍(通过启用HyperDescriptor
对象类型T
).
编辑重新性能查询; 这是一个测试台,结果如下:
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(Listdata, 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 foos = new List (); 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 } }
@Ellesedil我努力记住要明确地披露这些事情,但是因为我不是*卖*任何东西(而是在免费提供许多小时的工作)我承认我在这里感觉不到大量的*内疚. . (8认同)
那么"按原样",它将与反射一样快.如果启用HyperDescriptor,它会反射掉反射...我会快速测试......(2分钟) (4认同)
@MarcGravell是的我会对Expression解决方案很感兴趣.对于需要快速+学习效果的东西.谢谢马克! (3认同)
您的方法ToDataTable不支持可为空的字段:其他信息:DataSet不支持System.Nullable <>。 (2认同)
Mary Hamlin.. 222
我不得不修改Mark Gravell的示例代码来处理可空类型和空值.我在下面列出了一个工作版本.谢谢马克.
public static DataTable ToDataTable(this IList 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; }
要实现@Jim Beam,请更改方法签名以接受GroupBy的返回:`public static DataTable ToDataTable <TKey,T>(此IEnumerable <IGrouping <TKey,T >>数据)`然后,在foreach循环:`table.Columns.Add(“ Key”,Nullable.GetUnderlyingType(typeof(TKey))?? typeof(TKey));`然后在数据循环周围添加一个循环,在其中迭代组:foreach(IGrouping数据中的<TKey,T>组){foreach(group.Items中的T项目){有关详细信息,请参见此GIST:https://gist.github.com/rickdailey/8679306 (2认同)
A.Baudouin.. 13
这是解决方案的简单组合.它适用于Nullable类型.
public static DataTable ToDataTable(this IList 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; }
Onur Omer.. 12
对Mark的答案进行了一些小改动,使其适用List
于数据表等值类型:
public static DataTable ListToDataTable(IList 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; }
Jürgen Stein.. 9
MSDN上的这个链接值得一看:如何:实现CopyToDataTable
这会添加一个扩展方法,允许您执行此操作:
// 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();
小智.. 7
public DataTable ConvertToDataTable(IList 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; }
尽管此代码可以回答问题,但提供有关此代码为何和/或如何回答问题的其他上下文,可以提高其长期价值。 (3认同)
kostas ch... 7
上面是另一种方法:
Listlst = getdata(); string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst); DataTable pDt = JsonConvert.DeserializeObject (json);
Sadegh.. 6
尝试这个
public static DataTable ListToDataTable(IList lst) { currentDT = CreateTable (); 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 ) || prop.PropertyType == typeof(Nullable ) || prop.PropertyType == typeof(Nullable )) { 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 () { Type entType = typeof(T); DataTable tbl = new DataTable(DTName); PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType); foreach (PropertyDescriptor prop in properties) { if (prop.PropertyType == typeof(Nullable )) tbl.Columns.Add(prop.Name, typeof(decimal)); else if (prop.PropertyType == typeof(Nullable )) tbl.Columns.Add(prop.Name, typeof(int)); else if (prop.PropertyType == typeof(Nullable )) tbl.Columns.Add(prop.Name, typeof(Int64)); else tbl.Columns.Add(prop.Name, prop.PropertyType); } return tbl; }
Johannes Rud.. 5
我自己写了一个小型图书馆来完成这项任务.它仅在第一次将对象类型转换为数据表时使用反射.它会发出一个方法来完成翻译对象类型的所有工作.
它的速度非常快.你可以在这里找到它: GoogleCode上的ModelShredder
这是使用NuGet的FastMember进行的 2013年更新:
IEnumerabledata = ... DataTable table = new DataTable(); using(var reader = ObjectReader.Create(data)) { table.Load(reader); }
这使用FastMember的元编程API来获得最佳性能.如果您想将其限制为特定成员(或强制执行订单),那么您也可以这样做:
IEnumerabledata = ... DataTable table = new DataTable(); using(var reader = ObjectReader.Create(data, "Id", "Name", "Description")) { table.Load(reader); }
编辑的Dis/claimer : FastMember是一个Marc Gravell项目.它的黄金和全飞!
是的,这几乎是完全相反的这一个; 反射就足够了 - 或者如果你需要更快,HyperDescriptor
在2.0,或者Expression
在3.5.实际上,HyperDescriptor
应该绰绰有余.
例如:
// remove "this" if not on C# 3.0 / .NET 3.5 public static DataTable ToDataTable(this IList 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; }
现在使用一行,您可以比反射快许多倍(通过启用HyperDescriptor
对象类型T
).
编辑重新性能查询; 这是一个测试台,结果如下:
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(Listdata, 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 foos = new List (); 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的示例代码来处理可空类型和空值.我在下面列出了一个工作版本.谢谢马克.
public static DataTable ToDataTable(this IList 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; }
这是解决方案的简单组合.它适用于Nullable类型.
public static DataTable ToDataTable(this IList 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
于数据表等值类型:
public static DataTable ListToDataTable(IList 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; }
MSDN上的这个链接值得一看:如何:实现CopyToDataTable
这会添加一个扩展方法,允许您执行此操作:
// 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();
public DataTable ConvertToDataTable(IList 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; }
上面是另一种方法:
Listlst = getdata(); string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst); DataTable pDt = JsonConvert.DeserializeObject (json);
尝试这个
public static DataTable ListToDataTable(IList lst) { currentDT = CreateTable (); 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 ) || prop.PropertyType == typeof(Nullable ) || prop.PropertyType == typeof(Nullable )) { 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 () { Type entType = typeof(T); DataTable tbl = new DataTable(DTName); PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType); foreach (PropertyDescriptor prop in properties) { if (prop.PropertyType == typeof(Nullable )) tbl.Columns.Add(prop.Name, typeof(decimal)); else if (prop.PropertyType == typeof(Nullable )) tbl.Columns.Add(prop.Name, typeof(int)); else if (prop.PropertyType == typeof(Nullable )) tbl.Columns.Add(prop.Name, typeof(Int64)); else tbl.Columns.Add(prop.Name, prop.PropertyType); } return tbl; }
我自己写了一个小型图书馆来完成这项任务.它仅在第一次将对象类型转换为数据表时使用反射.它会发出一个方法来完成翻译对象类型的所有工作.
它的速度非常快.你可以在这里找到它: GoogleCode上的ModelShredder
它也可以通过XmlSerialization.我们的想法是 - 序列化为XML,然后读取DataSet的readXml方法.
我用这个代码(来自SO的答案,忘了哪里)
public static string SerializeXml(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); }
不确定它如何反对这篇文章的所有其他答案,但它也是一种可能性.
马克·格雷夫(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