2009-02-07 14 views
14

Biorąc pod uwagę listę obiektów, muszę przekształcić go w zestaw danych, gdzie każdy element na liście jest reprezentowany przez wiersz i każda właściwość jest kolumną w wierszu. Ten zestaw danych zostanie następnie przekazany do funkcji Aspose.Cells w celu utworzenia dokumentu Excel jako raportu.Jak przekształcić listę <T> w zestaw danych?

Say mam następujące:

public class Record 
{ 
    public int ID { get; set; } 
    public bool Status { get; set; } 
    public string Message { get; set; } 
} 

Biorąc pod uwagę zapisy listy, w jaki sposób przekształcić go w zestawie danych w następujący sposób:

ID Status Message 
1 true "message" 
2 false "message2" 
3 true "message3" 
... 

w tej chwili jedyną rzeczą mogę myśleć jest następujący:

DataSet ds = new DataSet 
ds.Tables.Add(); 
ds.Tables[0].Add("ID", typeof(int));  
ds.Tables[0].Add("Status", typeof(bool)); 
ds.Tables[0].Add("Message", typeof(string)); 

foreach(Record record in records) 
{ 
    ds.Tables[0].Rows.Add(record.ID, record.Status, record.Message); 
} 

Ale w ten sposób myślę, że musi być lepszy sposób, ponieważ na przynajmniej jeśli nowe właściwości zostaną dodane do Record, nie pojawią się w DataSet ... ale jednocześnie pozwolą mi kontrolować kolejność każdej właściwości dodawanej do wiersza.

Czy ktoś wie o lepszy sposób to zrobić?

Odpowiedz

27

Możesz to zrobić poprzez odbicie i generics, sprawdzając właściwości danego typu.

Rozważmy tę metodę rozszerzenia, które używam:

public static DataTable ToDataTable<T>(this IEnumerable<T> collection) 
    { 
     DataTable dt = new DataTable("DataTable"); 
     Type t = typeof(T); 
     PropertyInfo[] pia = t.GetProperties(); 

     //Inspect the properties and create the columns in the DataTable 
     foreach (PropertyInfo pi in pia) 
     { 
      Type ColumnType = pi.PropertyType; 
      if ((ColumnType.IsGenericType)) 
      { 
       ColumnType = ColumnType.GetGenericArguments()[0]; 
      } 
      dt.Columns.Add(pi.Name, ColumnType); 
     } 

     //Populate the data table 
     foreach (T item in collection) 
     { 
      DataRow dr = dt.NewRow(); 
      dr.BeginEdit(); 
      foreach (PropertyInfo pi in pia) 
      { 
       if (pi.GetValue(item, null) != null) 
       { 
        dr[pi.Name] = pi.GetValue(item, null); 
       } 
      } 
      dr.EndEdit(); 
      dt.Rows.Add(dr); 
     } 
     return dt; 
    } 
+0

Cóż, społeczność się odezwała, więc zagłosuję na tę odpowiedź, mimo że nie będę mógł jej użyć do moich celów, ponieważ chcę kontrolować kolejność parametrów. Ale na pewno będę mieć to na uwadze ... – mezoid

+2

Hej, właśnie przetestowałem twoje rozszerzenie i stwierdziłem, że jeśli chcesz kontrolować kolejność kolumn pojawiają się w datatable, to musisz zadeklarować je w kolejności, w której chcesz je w obiekcie typu T, który przekazujesz do rozszerzenia. To cudownie! – mezoid

1

Oprócz używania dodatkowo Reflection do określenia właściwości klasy Record, aby zadbać o dodawanie nowych właściwości, to prawie tyle.

+0

Pewnie masz rację ... mimo, że nie to, co chciałem usłyszeć. Sądzę, że albo muszę poprawić swoją wiedzę o DataSets, albo ktoś w firmie Microsoft musi wymyślić lepszy sposób. – mezoid

0

Pisałem mała biblioteka się do wykonania tego zadania. Używa refleksji tylko wtedy, gdy typ obiektu ma zostać przetłumaczony na datatable. Emituje metodę, która wykona całą pracę tłumacząc typ obiektu.

Jego płonący post. Można go znaleźć tutaj: ModelShredder on GoogleCode

0

Zrobiłem kilka zmian do sposobu rozszerzenia CMS”obsłużyć przypadek, gdy List zawiera prymitywny lub String elementy. W takim przypadku wynikowa wersja DataTable będzie miała tylko jedną wartość Column z wartością dla każdej wartości na liście.

Początkowo myślałem o uwzględnieniu wszystkich typów wartości (nie tylko typów pierwotnych), ale nie chciałem, aby zostały uwzględnione Struktury (które są typami wartości).

Zmiana ta powstała z moim potrzeby konwersji List(Of Long) lub List<long>, w DataTable użyć jako wycenione tabela parametr w MS SQL 2008 procedury przechowywanej.

Przykro mi, że mój kod jest w VB, chociaż to pytanie jest oznaczone jako ; mój projekt jest w VB (NIE mój wybór) i nie powinno być trudno zastosować zmiany w C#.

Imports System.Runtime.CompilerServices 
Imports System.Reflection 

Module Extensions 

    <Extension()> 
    Public Function ToDataTable(Of T)(ByVal collection As IEnumerable(Of T)) As DataTable 
     Dim dt As DataTable = New DataTable("DataTable") 
     Dim type As Type = GetType(T) 
     Dim pia() As PropertyInfo = type.GetProperties() 

     ' For a collection of primitive types create a 1 column DataTable 
     If type.IsPrimitive OrElse type.Equals(GetType(String)) Then 
      dt.Columns.Add("Column", type) 
     Else 
      ' Inspect the properties and create the column in the DataTable 
      For Each pi As PropertyInfo In pia 
       Dim ColumnType As Type = pi.PropertyType 
       If ColumnType.IsGenericType Then 
        ColumnType = ColumnType.GetGenericArguments()(0) 
       End If 
       dt.Columns.Add(pi.Name, ColumnType) 
      Next 

     End If 

     ' Populate the data table 
     For Each item As T In collection 
      Dim dr As DataRow = dt.NewRow() 
      dr.BeginEdit() 
      ' Set item as the value for the lone column on each row 
      If type.IsPrimitive OrElse type.Equals(GetType(String)) Then 
       dr("Column") = item 
      Else 
       For Each pi As PropertyInfo In pia 
        If pi.GetValue(item, Nothing) <> Nothing Then 
         dr(pi.Name) = pi.GetValue(item, Nothing) 
        End If 
       Next 
      End If 
      dr.EndEdit() 
      dt.Rows.Add(dr) 
     Next 
     Return dt 
    End Function 

End Module 
0

Znalazłem ten kod na forum Microsoft. Jest to jak dotąd jedna z najprostszych metod, łatwa do zrozumienia i zastosowania. To zaoszczędziło mi wiele godzin. Dostosowałem to jako metodę rozszerzenia bez żadnych zmian w rzeczywistej implementacji. Poniżej znajduje się kod. nie wymaga wiele wyjaśnień.

można użyć dwóch podpisu funkcji z samej realizacji

1) public static DataSet ToDataSetFromObject (to obiekt dsCollection)

2) public static ToDataSetFromArrayOfObject DataSet (to Object [] arrCollection). Będę używał tego w poniższym przykładzie.

// <summary> 
// Serialize Object to XML and then read it into a DataSet: 
// </summary> 
// <param name="arrCollection">Array of object</param> 
// <returns>dataset</returns> 

public static DataSet ToDataSetFromArrayOfObject(this object[] arrCollection) 
{ 
    DataSet ds = new DataSet(); 
    try { 
     XmlSerializer serializer = new XmlSerializer(arrCollection.GetType); 
     System.IO.StringWriter sw = new System.IO.StringWriter(); 
     serializer.Serialize(sw, dsCollection); 
     System.IO.StringReader reader = new System.IO.StringReader(sw.ToString()); 
     ds.ReadXml(reader); 
    } catch (Exception ex) { 
     throw (new Exception("Error While Converting Array of Object to Dataset.")); 
    } 
    return ds; 
} 

Aby używać tego rozszerzenia w kodzie

Country[] objArrayCountry = null; 
objArrayCountry = ....;// populate your array 
if ((objArrayCountry != null)) { 
    dataset = objArrayCountry.ToDataSetFromArrayOfObject(); 
}