Wechsel von “Access Jet Engine” zu OpenXML

Für den lesenden Zugriff auf Exceltabellen habe ich bisher die Access Jet Engine verwendet. Damit ist es relativ einfach gewesen, z.B. eine Tabelle mit einem OleDbReader auszulesen.

Doch zunehmend gab es Probleme mit den 32 und 64 bit Varianten, die zueinander nicht kompatibel sind. Ein installiertes 64 bit Office oder ein 64 bit Sql Server und ich konnte keine 32 bit Jet Engine zum Laufen überreden.

Das Problem existiert schon eine Weile und ist recht verbreitet.

(http://stackoverflow.com/questions/2899201/microsoft-ace-oledb-12-0-64x-sql-server-and-86x-office)

Bisher konnte ich es immer umgehen. Bis jetzt. Eine Alternative ist das OpenXml SDK. Die API fühlt sich etwas gewöhnungsbedürftig an, aber um schnell eine Tabelle auszulesen eignet es sich ganz gut. Nebenbei ist hiermit auch das Schreiben möglich. Im folgenden Beispielcode habe ich das Beispiel aus der MSDN etwas erweitert. Die Methode Read() liefert eine DataTable zurück, welche die erste Tabelle aus der xlsx Datei enthält.

 

public DataTable Read()
{
    var table = new DataTable("MyTable");
    var filePath = GetFilePath();
    if (!File.Exists(filePath))
    {
        throw new FileNotFoundException(string.Format("Excelfile '{0}' not found. ", filePath));
    }
    try
    {
        using (var document = SpreadsheetDocument.Open(filePath, false))
        {
            var wbPart = document.WorkbookPart;
            var sheet = wbPart.Workbook.Descendants<Sheet>().FirstOrDefault();
            var wsPart = wbPart.GetPartById(sheet.Id) as WorksheetPart;
            var rows = wsPart.Worksheet.Descendants<Row>();
            
            
            var spans = rows.ElementAt(0).Spans.Items.FirstOrDefault();//string like "1:18"
            var splits = spans.Value.Split(':');
            
            var columnCount = Convert.ToInt32(splits[1]);
            var columnReference = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
            
            for (int i = 0; i < columnCount; i++)
            {
                var columnName = GetCellValue(rows.ElementAt(0).ChildElements[i] as Cell, wbPart);
                table.Columns.Add(columnName);
            }

            for (int i = 1; i < rows.Count(); i++)
            {
                var newRow = table.NewRow();

                for (int k = 0; k < rows.ElementAt(i).ChildElements.Count; k++)
                {
                    var cell = rows.ElementAt(i).ChildElements[k] as Cell;
                    //cell.CellReference = "A1"
                    var columnIndex = cell.CellReference.Value[0];
                    var cellIndex = columnReference.IndexOf(columnIndex);
                    newRow[cellIndex] = GetCellValue(cell, wbPart);
                }
                table.Rows.Add(newRow);
            }
        }
    }
    catch (Exception exc)
    {
        throw new Exception("Can't read Excelfile!", exc);
    }
    return table;
}


// Retrieve the value of a cell, given a file name, sheet name, 
// and address name.
public string GetCellValue(Cell theCell, WorkbookPart wbPart)
{
    string value = string.Empty;
    // If the cell does not exist, return an empty string.
    if (theCell != null)
    {
        value = theCell.InnerText;

        // If the cell represents an integer number, you are done. 
        // For dates, this code returns the serialized value that 
        // represents the date. The code handles strings and 
        // Booleans individually. For shared strings, the code 
        // looks up the corresponding value in the shared string 
        // table. For Booleans, the code converts the value into 
        // the words TRUE or FALSE.
        if (theCell.DataType != null)
        {
            switch (theCell.DataType.Value)
            {
                case CellValues.SharedString:
                    // For shared strings, look up the value in the
                    // shared strings table.
                    var stringTable = wbPart.GetPartsOfType<SharedStringTablePart>().FirstOrDefault();

                    // If the shared string table is missing, something 
                    // is wrong. Return the index that is in
                    // the cell. Otherwise, look up the correct text in 
                    // the table.
                    if (stringTable != null)
                    {
                        value = stringTable.SharedStringTable.ElementAt(int.Parse(value)).InnerText;
                    }
                    break;

                case CellValues.Boolean:
                    switch (value)
                    {
                        case "0":
                            value = "FALSE";
                            break;
                        default:
                            value = "TRUE";
                            break;
                    }
                    break;
            }
        }
    }
    return value;
}

Vorausetzungen ist einmal die DocumentFormat.OpenXml Library (über nuget verfügbar) und eine Referenz auf WindowsBase aus dem .NET 4.0 Framework.

Es gibt ein SDK Tool mit dem die Xml Bäume der xlsx Datei sichtbar gemacht werden. Richtig weitergeholfen hat es mir zwar nicht, aber zur Sicherheit hier noch der Link.

Ressourcen:

Open Xml SDK 2.5

MSDN Beispielcode

Advertisements

One thought on “Wechsel von “Access Jet Engine” zu OpenXML

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s