Konvertieren von DataTable in Klassenliste

213806
devspider

Ich verwende ExcelDataReader, um eine Excel-Datei in eine Datenmenge zu importieren.

Beispiel Excel-Tabelle:

//ID     Name     Display Order    Active
//1      John          1             1

ID, DisplayOrderUnd Activesind Spalten lesen wie double, so muss ich sie konvertieren long, intund booljeweils Typen. Ich muss eine Typenliste Categoryaus der DataTable des DataSet erstellen .

Wird dieser Code gut funktionieren? Vorschläge für eine schnellere Konvertierung von DataTable in Klassenliste?

var list = result.Tables["Categories"].AsEnumerable()
.Skip(1)
.Select(dr =>
        new Category
            {
                Id = Convert.ToInt64(dr.Field<double>("ID")),
                Name = dr.Field<string>("Name"),
                DisplayOrder = Convert.ToInt32(dr.Field<double>("Display Order")),
                IsActive= dr.Field<double>("Active") == 1 ? true : false
            }
        ).ToList();
Antworten
38
Ich würde die letzte Zeile wahrscheinlich als `IsActive = dr.Field umschreiben("Active")> 0` (Ihr Ternär ist nicht erforderlich, da der Vergleich bereits als "true" oder "false" bewertet wird), weil es zu doppelten Rundungsproblemen und Gleichheitsprüfungen kommt. Jesse C. Slicer vor 7 Jahren 0
Danke Jesse. Noch etwas zu verbessern? devspider vor 7 Jahren 0
Ich muss sagen, das sieht hübsch und prägnant aus. Jesse C. Slicer vor 7 Jahren 2
Sie können auf die DataRow-Felder direkt über den Namen zugreifen: `dr [" ID "]`, `dr [" Name "]` usw. Sie sind vom Typ `object`, aber die Funktionen` Convert.To ____ () `erledigen das. Bobson vor 7 Jahren 0

2 Antworten auf die Frage

34
Gaui

Ich habe eine Erweiterungsmethode erstellt DataTable, um sie in eine zu konvertierenList<T>

public static class Helper
{
    /// <summary>
    /// Converts a DataTable to a list with generic objects
    /// </summary>
    /// <typeparam name="T">Generic object</typeparam>
    /// <param name="table">DataTable</param>
    /// <returns>List with generic objects</returns>
    public static List<T> DataTableToList<T>(this DataTable table) where T : class, new()
    {
        try
        {
            List<T> list = new List<T>();

            foreach (var row in table.AsEnumerable())
            {
                T obj = new T();

                foreach (var prop in obj.GetType().GetProperties())
                {
                    try
                    {
                        PropertyInfo propertyInfo = obj.GetType().GetProperty(prop.Name);
                        propertyInfo.SetValue(obj, Convert.ChangeType(row[prop.Name], propertyInfo.PropertyType), null);
                    }
                    catch
                    {
                        continue;
                    }
                }

                list.Add(obj);
            }

            return list;
        }
        catch
        {
            return null;
        }
    }
}

Beispiel:

DataTable dtTable = GetEmployeeDataTable();
List<Employee> employeeList = dtTable.DataTableToList<Employee>();
Du bist ein Lebensretter! Ich hatte keine Ahnung, dass Convert.ChangeType bis heute überhaupt existierte. Dies ist viel einfacher als bei dem Versuch, TryParse für den Zieltyp aufzurufen. Und dies sorgt auch für die DBNull-Werte in der DataTable. 10/10! Krummelz vor 5 Jahren 2
Interessanter Code, ich frage mich nur, ob dies im Gegensatz zu nicht generischem Code funktioniert. Nap vor 5 Jahren 0
Ein Update, das Sie für Ihre großartige Antwort in Betracht ziehen sollten, sind nur bearbeitbare Eigenschaften, um alle Ausnahmen zu reduzieren, die ausgelöst werden. Folgendes würde Ihren Code entsprechend aktualisieren: `foreach (var prop in obj.GetType (). GetProperties () **. Where (p => p.CanWrite) **)` GregTank vor 5 Jahren 3
@Nap Die Leistung dieses Codes ist aufgrund von Reflektionen äußerst langsam. Elegante Lösung mit schrecklicher Leistung. Zer0 vor 5 Jahren 4
Nur eine Randnotiz: Ich empfehle, die Methode in `ToList` anstelle von` DataTableToList` umzubenennen, da sie bereits eine Erweiterungsmethode von DataTable ist. Und es entspricht auch der Standardkonvention von ToString, ToChar usw. BornToCode vor 4 Jahren 1
Ich werde berichten, dass das, was Zer0 gesagt hat, wirklich stimmt. Es ist auf meinem Server passiert. :( toha vor 3 Jahren 0
2
Jesse C. Slicer

Sie können etwas von der Reflexionsschwäche in Gauis Antwort mit etwas Refactoring und etwas Caching als solchen verlieren :

public static class Helper
{
    private static readonly IDictionary<Type, ICollection<PropertyInfo>> _Properties =
        new Dictionary<Type, ICollection<PropertyInfo>>();

    /// <summary>
    /// Converts a DataTable to a list with generic objects
    /// </summary>
    /// <typeparam name="T">Generic object</typeparam>
    /// <param name="table">DataTable</param>
    /// <returns>List with generic objects</returns>
    public static IEnumerable<T> DataTableToList<T>(this DataTable table) where T : class, new()
    {
        try
        {
            var objType = typeof(T);
            ICollection<PropertyInfo> properties;

            lock (_Properties)
            {
                if (!_Properties.TryGetValue(objType, out properties))
                {
                    properties = objType.GetProperties().Where(property => property.CanWrite).ToList();
                    _Properties.Add(objType, properties);
                }
            }

            var list = new List<T>(table.Rows.Count);

            foreach (var row in table.AsEnumerable().Skip(1))
            {
                var obj = new T();

                foreach (var prop in properties)
                {
                    try
                    {
                        var propType = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
                        var safeValue = row[prop.Name] == null ? null : Convert.ChangeType(row[prop.Name], propType);

                        prop.SetValue(obj, safeValue, null);
                    }
                    catch
                    {
                        // ignored
                    }
                }

                list.Add(obj);
            }

            return list;
        }
        catch
        {
            return Enumerable.Empty<T>();
        }
    }
}
Möglicherweise möchten Sie nach einer Set-Methode für die Eigenschaft suchen oder die Spalte in row.Table.Columns so durchlaufen, dass schreibgeschützte Eigenschaften unterstützt werden moarboilerplate vor 5 Jahren 0
@moarboilerplate leicht hinzugefügt. Jesse C. Slicer vor 5 Jahren 0
Ich mag Ihre Lösung wirklich, sie funktioniert gut, aber ich bekomme eine Fehlermeldung, weil ich DateTime? - und `int? -Eigenschaften in meiner Klasse habe. Ich erhalte die Fehlermeldung, dass ich nicht von System.DateTIme zu System.Nullable wechseln kann. Kann das behoben werden? Misiu vor 4 Jahren 0
Ich habe hier eine einfache Lösung gefunden: http://stackoverflow.com/a/3531824/965722. Vielleicht könnten Sie dies Ihrem Code hinzufügen. Dies würde anderen Findern wie mir helfen. Misiu vor 4 Jahren 0
@ JesseC.Slicer Ich verwende dies mit großen Datenmengen, die aus einer anderen Bibliothek stammen (ich kann es nicht ändern). Ihre Lösung verwendet Reflektion. Ich habe viele Artikel über Ausdrucksbäume und Lambdas gefunden, daher frage ich mich, dass es möglich sein könnte, Ihren Code so zu ändern, dass er anstelle von Reflektionen verwendet wird. Misiu vor 4 Jahren 0
Noch eine Frage: Warum haben Sie "Überspringen (1)" in Ihrem Code? Ich musste das entfernen, denn wenn meine Datentabelle einzeilig ist, habe ich keine Ergebnisse erhalten, wobei diese erste Datenzeile übersprungen wird. Ich sehe das nicht in Gauis Antwort. Ist das wirklich nötig? Misiu vor 4 Jahren 0
@Misiu das ursprüngliche Poster zeigte seine Daten (und wie sein Code mit dem Skip funktionierte) hatte eine erste Zeile, die die Spaltenüberschriften waren. Lesen Sie die Kommentare zu der Antwort von IharS. Jesse C. Slicer vor 4 Jahren 0
Sorry, ich habe diesen Kommentar nicht bemerkt. Misiu vor 4 Jahren 0
@Misiu bezüglich deines Reflexionskommentars - ich würde dich herausfordern, die Leistung zu messen und herauszufinden, wo der Engpass liegt. Ich wette, das Bit, das die Datentabelle tatsächlich ausfüllt, ist um Größenordnungen langsamer (aufgrund von Netzwerk oder E / A) als jedes der Reflexionsbits. Jesse C. Slicer vor 4 Jahren 0