Как преобразовать List <T> в DataSet?

Учитывая список объектов, мне нужно преобразовать его в набор данных, где каждый элемент в списке представлен строкой, и каждое свойство является столбцом в строке. Затем этот DataSet будет передан функции Aspose.Cells, чтобы создать документ Excel в качестве отчета.

Скажем, у меня есть следующее:

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

Учитывая записи в List, как я могу преобразовать его в DataSet следующим образом:

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

В настоящий момент я могу думать только о следующем:

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);
}

Но этот способ не позволяет мне думать, что должен быть лучший способ, поскольку, по крайней мере, если новые свойства добавляются в запись, то они не будут отображаться в DataSet... но в то же время это позволяет мне контролировать порядок каждого свойства добавляется в строку.

Кто-нибудь знает, как лучше это сделать?

Ответ 1

Вы можете сделать это с помощью отражения и генериков, проверяя свойства базового типа.

Рассмотрим этот метод расширения, который я использую:

    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;
    }

Ответ 2

Помимо дополнительного использования Reflection для определения свойств класса Record, чтобы заботиться о добавлении новых свойств, это в значительной степени оно.

Ответ 3

Я написал небольшую библиотеку для выполнения этой задачи. Он использует отражение только в первый раз, когда тип объекта должен быть переведен в datatable. Он испускает метод, который будет выполнять всю работу, переводящую тип объекта.

Его пылают быстро. Вы можете найти его здесь: ModelShredder в GoogleCode

Ответ 4

Я внес некоторые изменения в метод расширения CMS для обработки случая, когда List содержит примитивные или String элементы. В этом случае результирующий DataTable будет иметь только один Column с Row для каждого из значений в списке.

Сначала я думал о включении всех типов значений (не только примитивных типов), но я не хотел включать Структуры (которые являются типами значений).

Это изменение возникло из-за моей потребности в преобразовании List(Of Long) или List<long> в DataTable, чтобы использовать его в качестве параметра Table-Valued в хранимой процедуре MS SQL 2008.

Мне жаль, что мой код находится в VB, хотя этот вопрос отмечен ; мой проект находится в VB (не мой выбор), и не должно быть сложно применить изменения в С#.

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

Ответ 5

Я нашел этот код на форуме Microsoft. Это пока что один из самых простых способов, который легко понять и использовать. Это спасло меня. Я настроил это как метод расширения без каких-либо изменений в фактической реализации. Ниже приведен код. это не требует большого объяснения.

Вы можете использовать две сигнатуры функций с одной и той же реализацией

1) public static DataSet ToDataSetFromObject (этот объект dsCollection)

2) public static DataSet ToDataSetFromArrayOfObject (этот объект [] arrCollection). Я буду использовать этот пример ниже.

// <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;
}

Чтобы использовать это расширение в коде

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