일반 목록 / 열거 가능을 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);
}
편집자 Dis / 클레임 : 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<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;
}
이제 한 줄을 사용하면 ( HyperDescriptor
object-type 을 활성화하여) 반사보다 여러 배 더 빠르게 만들 수 있습니다 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(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
}
}
nullable 유형과 null 값을 처리하기 위해 Marc Gravell의 샘플 코드를 수정해야했습니다. 아래에 작동 버전을 포함 시켰습니다. 고마워 마크.
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;
}
이것은 솔루션의 간단한 혼합입니다. 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;
}
데이터 테이블 과 같은 값 유형으로 작동 하도록 Marc의 답변 을 약간 변경했습니다 List<string>
.
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;
}
MSDN의이 링크는 방문 할 가치가 있습니다. 방법 : 일반 형식 T가 DataRow가 아닌 CopyToDataTable <T> 구현
이렇게하면 확장 방법이 추가됩니다.
// 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<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;
}
다른 접근법은 위와 같습니다.
List<WhateEver> lst = getdata();
string json = Newtonsoft.Json.JsonConvert.SerializeObject(lst);
DataTable pDt = JsonConvert.DeserializeObject<DataTable>(json);
이 시도
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;
}
이 작업을 수행하기 위해 작은 라이브러리를 직접 작성했습니다. 오브젝트 유형이 데이터 테이블로 처음 변환 될 때만 리플렉션을 사용합니다. 객체 유형을 번역하는 모든 작업을 수행하는 메소드를 생성합니다.
빠른 속도입니다. 여기에서 찾을 수 있습니다 : GoogleCode의 ModelShredder
XmlSerialization을 통해서도 가능합니다. 아이디어는-XML로 직렬화 한 다음 DataSet의 readXml 메소드입니다.
이 코드를 사용합니다 (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);
}
이 게시물의 다른 모든 답변과 어떻게 일치하는지 잘 모르겠지만 가능성도 있습니다.
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
여기에 나열된 옵션 중 어느 것도 내 경우에 효과가 없었기 때문에 대체 솔루션을 생각해 내야했습니다. 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;
}
}
나는 이것이 한동안 폐쇄되었다는 것을 알고있다. 그러나이 특정 문제에 대한 해결책이 있었지만 약간의 왜곡이 필요했습니다. 열과 데이터 테이블은 사전 정의 / 이미 인스턴스화해야했습니다. 그런 다음 단순히 데이터 테이블에 유형을 삽입해야했습니다.
여기 내가 한 일의 예가 있습니다.
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);
}
}
}
VB.NET을 사용하는 경우이 클래스가 작업을 수행합니다.
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
클래스에 속성이있는 경우이 코드 줄은 괜찮습니다 !!
PropertyDescriptorCollection props =
TypeDescriptor.GetProperties(typeof(T));
그러나 모든 공개 필드가 있으면 다음을 사용하십시오.
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;
}
원래 답변은 위에서 왔으며 속성 대신 필드를 사용하도록 편집했습니다.
그리고 이것을 사용하려면
DataTable dt = new DataTable();
dt = ToDataTable(myBriefs);
gridData.DataSource = dt;
gridData.DataBind();
일반 목록을 데이터 테이블로 변환하려면 DataTableGenerator를 사용할 수 있습니다.
이 라이브러리를 사용하면 다음과 같은 여러 기능을 갖춘 목록을 데이터 테이블로 변환 할 수 있습니다
- 데이터 테이블 헤더 번역
- 표시 할 열을 지정하십시오.
이것은 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;
}
}
}
Se probó el método para 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<Whatever> _lobj= new List<Whatever>();
var json = JsonConvert.SerializeObject(_lobj);
DataTable dt = (DataTable)JsonConvert.DeserializeObject(json, (typeof(DataTable)));
.NET Core를 사용하는 경우 2019 답변 -Nuget ToDataTable 라이브러리를 사용하십시오 . 장점 :
- FastMember보다 나은 성능
- SQL Server 테이블 반환 매개 변수 로 사용할 구조적 SqlParameters도 만듭니다.
면책 조항 -저는 ToDataTable의 저자입니다
성능 -일부 Benchmark .Net 테스트를 수행하여 ToDataTable 리포지토리에 포함 시켰습니다 . 결과는 다음과 같습니다.
100,000 행 데이터 테이블 만들기 :
MacOS Windows
Reflection 818.5 ms 818.3 ms
FastMember from 1105.5 ms 976.4 ms
Mark's answer
Improved FastMember 524.6 ms 456.4 ms
ToDataTable 449.0 ms 376.5 ms
Marc의 답변 에서 제안 된 FastMember 방법은 리플렉션을 사용한 Mary의 답변 보다 성능이 좋지 않은 것처럼 보였지만 FastMember를 사용하여 다른 방법 을 굴려서 TypeAccessor
훨씬 더 잘 수행했습니다. 그럼에도 불구하고 ToDataTable 패키지는 성능을 능가했습니다.
참고 URL : https://stackoverflow.com/questions/564366/convert-generic-list-enumerable-to-datatable
'Programing' 카테고리의 다른 글
timedelta를 문자열로 형식화 (0) | 2020.04.10 |
---|---|
누가 1 = 1을 사용하고 왜 (0) | 2020.04.10 |
텍스트 상자에 Enter 키를 누르는 사용자를위한 JQuery 이벤트? (0) | 2020.04.10 |
내 git 저장소에 virtualenv 디렉토리를 두는 것이 좋지 않습니까? (0) | 2020.04.09 |
EC2에서 '중지 된'인스턴스에 대한 요금이 부과됩니까? (0) | 2020.04.09 |