Laden...

ClosedXML => Typen nach C#

Letzter Beitrag vor 8 Tagen 7 Posts 278 Views
ClosedXML => Typen nach C#

Hallo Zusammen,

das ist mein erster Beitrag und ich hoffe, ich bin im Richtigen Forum.

Meine Frage bezieht sich auf ClosedXML und den Part, wo ich Daten aus einer Excel-Datei auslese.

Grundsätzlich kommen die mit dem Typ XLCellValue, damit kann ich in C# aber nichts anfangen, ich bräuchte die realen Typen.

Diese kann ich auslesen z.B. über Worksheet(1).Cell(1, 1).Datatyp

Wie kann ich das denn aber gleich nach C# nehmen?

Ich habe das jetzt mal mit Switch gelöst, sieht aber irgendwie nicht schön aus:

/*****************************************************/
/*                Der Importer selbst                */
/*****************************************************/
private XLTBL ImportXL(IXLWorksheet WS)
{
    int LastRow, LastCol;

    LastRow = WS.LastRowUsed().RowNumber();
    LastCol = WS.LastColumnUsed().ColumnNumber();

    XLTBL Tbl = new XLTBL();                        // XLTBL ist alias für List<List<object>>;

    // Werte aus Excel kopieren
    for (int i = 1; i <= LastRow; i++)              // jede Zeile
    {
        List<object> Row = new List<object>();

        for (int j = 1; j <= LastCol; j++)          // jede Spalte
        {
            switch (WS.Cell(i, j).DataType)
            {
                case XLDataType.Boolean:
                    Row.Add(WS.Cell(i, j).GetBoolean());
                    break;

                case XLDataType.Number:
                    Row.Add(WS.Cell(i, j).GetDouble());
                    break;

                case XLDataType.DateTime:
                    Row.Add(WS.Cell(i, j).GetDateTime());
                    break;

                default:
                    Row.Add(WS.Cell(i, j).GetString());
                    break;
            }
        }

        Tbl.Add(Row);
    }

    return Tbl;
}

Das Problem ist, dass du scheinbar eine generische Methode willst um Daten beliebig auszulesen.
Sieht für mich schon nach dem falschen Ansatz aus.
Eigentlich will man gezielt Daten lesen und schreiben.
Es wäre sauberer wenn du aus dem jeweiligen Datenblatt eine Zeile liest und diese in Form eines spezifischen Klasseninstanz befüllst.
Dann liefert deine Methode am Ende eine Liste von den Instanzen zurück.

object ist hier wegen Boxing/Unboxing schon der falsche Weg.
List<List> ist dann ein Antipattern, was zu sehr falschen Annahmen führen kann.
Wenn z.B. in der gleichen Spalte aber anderen Zeile anstelle eines DateTime ein bool steht, knallt es in deiner nachgelagerten Verarbeitung.

T-Virus

Developer, Developer, Developer, Developer....

99 little bugs in the code, 99 little bugs. Take one down, patch it around, 117 little bugs in the code.

Abgesehen von der Antwort meines Vorredners solltest du darüber nachdenken, LINQ zu verwenden. Das macht den Code lesbarer, sicherer und moderner.

private XLTBL ImportXL(IXLWorksheet WS)
{
   return WS.RangeUsed()?.Rows()
       .Select(row => row.Cells()
           .Select<IXLCell, object>(cell => cell.DataType switch
           {
               XLDataType.Boolean  => cell.GetBoolean(),
               XLDataType.Number   => cell.GetDouble(),
               XLDataType.DateTime => cell.GetDateTime(),
               _                   => cell.GetString()
           })
           .ToList())
       .ToList() ?? new XLTBL();
}

(nicht getestet, aber so sollte es eigentlich gehen)

LG

René

René

Sorry, aber der switch ist hier komplett unnötig, da die einzelnen Zellenelemente einfach als object weitergegeben werden, d.h. die aufrufende Methode müßte genauso wieder einen switch anwenden, um auf die Elemente der XLTBL zuzugreifen.

So, wie T-Virus geschrieben hat, sollte man spezifische Klassen (mit konkreten Datentypen) benutzen, um die Daten auszulesen.

Ich verstehe dein Argument, aber der switch ist hier nicht unnötig. Er sorgt dafür, dass ClosedXML-spezifische Typen direkt in gängige .NET-Typen umgewandelt werden, was den Code robuster macht. Klar, wenn eine stark typisierte Lösung gewünscht ist, wäre eine eigene Klasse besser, aber das hängt vom Anwendungsfall ab. Mein Ziel war es, eine moderne und flexible Variante zu zeigen, die universell funktioniert.

LG

René

René

Hallo Zusammen,

war krank, konnte nicht antworten.

Wie muss ich mir das mit der spezifischen Klasse vorstellen? Meine XLS-Dateien haben mit unter 400 Spalten, ich kann ja nicht per Hand in eine Klasse 400 Properties schreiben. Und wenn ich ein dynamisches ExpandoObject nutze, haben wir das Problem mit den Datentypen ja wieder.

Sorry, wenn die Frage vielleicht ein wenig komisch klingen, aber ich bin recht neu in C#.

List<List<>> sah halt in soweit gut als, als dass ich später über Tbl[i][j] genau auf die Werte zugreifen kann und 2 Indizes habe, die Reihe und Spalte entsprechen.

Grüße

Zitat von Spoiler

Wie muss ich mir das mit der spezifischen Klasse vorstellen? Meine XLS-Dateien haben mit unter 400 Spalten, ich kann ja nicht per Hand in eine Klasse 400 Properties schreiben.

Ja ist Aufwand, aber einen Weg musst halt gehen. Entweder alles manuell machen oder ne sehr unschöne Klasse mit 400 Properties.
Typisierung kommt halt nur mit konkreter Typangabe.

Je nachdem was Du hast kannst Du sowas auch in eine AI werfen und Dir das zB via Copilot erzeugen lassen.


Was auch geht und erstmal umständlich klingt: Excel als Json exportieren und dann via Visual Studio "Paste Special" automatisch zu ner C# Klasse erzeugen lassen und alle Typen überprüfen.