Ziel:

Ich möchte eine Excel-Datei importieren und die Zeilen bestimmter Spalten lesen. Dafür verwende ich ExcelDataReader. Ich habe eine Low-Level-Klasse namens ExcelData implementiert, die die ExcelDataReader verwendet und beispielsweise herausfindet, ob es sich um eine „.xls“ handelt „of“ .xslx „-Datei (oder etwas völlig Unabhängiges!) usw. Zusätzlich zu dieser Klasse habe ich eine ReadInData -Klasse erstellt, die die gewünschten Spalten aus Excel abruft Blatt.

Hauptanliegen:

  • Die Liste der Fänge in meinem Hauptprogramm
  • Auslösen von Ausnahmen
  • Die Gesamtkonstruktion des Codes und der Codequalität
  • Sollte ich meine ExcelData -Klasse in ReadInData einkapseln oder so lassen, wie sie ist ?
  • Übergabe des Parameters isFirstRowAsColumnNames

Da dies ein Code für eine Firma ist, habe ich die Namen einiger Paare geändert Ich weiß also, dass sie nicht die besten Namen sind.

Der Einstiegspunkt meines Codes:

class Program { static void Main(string[] args) { try { ReadInData readInData = new ReadInData(@"C:\SC.xlsx", "sc_2014"); IEnumerable<Recipient> recipients = readInData.GetData(); } catch (FileNotFoundException ex) { Console.WriteLine(ex.Message); } catch (ArgumentException ex) { Console.WriteLine(ex.Message); } catch (WorksheetDoesNotExistException ex) { Console.WriteLine(ex.Message); } catch (FileToBeProcessedIsNotInTheCorrectFormatException ex) { Console.WriteLine(ex.Message); } Console.ReadLine(); } } 

In diesem Code Ich erstelle eine neue ReadInData -Klasse, zu der ich pas s der Pfad, der Name der Datei und der Name der Excel-Tabelle, die ich lesen möchte.

Bedenken Sie hier: Ist es in Ordnung, diese Dinge in dieser Datei zu übergeben?

Die ReadInData -Klasse:

public class ReadInData { private string path; private string worksheetName; public ReadInData(string path, string worksheetName) { this.path = path; this.worksheetName = worksheetName; } public IEnumerable<Recipient> GetData(bool isFirstRowAsColumnNames = true) { var excelData = new ExcelData(path); var dataRows = excelData.GetData(worksheetName, isFirstRowAsColumnNames); return dataRows.Select(dataRow => new Recipient() { Municipality = dataRow["Municipality"].ToString(), Sexe = dataRow["Sexe"].ToString(), LivingArea = dataRow["LivingArea"].ToString() }).ToList(); } } 

Grundsätzlich dachte ich, ich brauche eine Klasse über der ExcelData -Klasse, da es etwas höher zu sein schien.

Die ExcelData -Klasse:

public class ExcelData { private string path; public ExcelData(string path) { this.path = path; } private IExcelDataReader GetExcelDataReader(bool isFirstRowAsColumnNames) { using (FileStream fileStream = File.Open(path, FileMode.Open, FileAccess.Read)) { IExcelDataReader dataReader; if (path.EndsWith(".xls")) { dataReader = ExcelReaderFactory.CreateBinaryReader(fileStream); } else if (path.EndsWith(".xlsx")) { dataReader = ExcelReaderFactory.CreateOpenXmlReader(fileStream); } else { //Throw exception for things you cannot correct throw new FileToBeProcessedIsNotInTheCorrectFormatException("The file to be processed is not an Excel file"); } dataReader.IsFirstRowAsColumnNames = isFirstRowAsColumnNames; return dataReader; } } private DataSet GetExcelDataAsDataSet(bool isFirstRowAsColumnNames) { return GetExcelDataReader(isFirstRowAsColumnNames).AsDataSet(); } private DataTable GetExcelWorkSheet(string workSheetName, bool isFirstRowAsColumnNames) { DataSet dataSet = GetExcelDataAsDataSet(isFirstRowAsColumnNames); DataTable workSheet = dataSet.Tables[workSheetName]; if (workSheet == null) { throw new WorksheetDoesNotExistException(string.Format("The worksheet {0} does not exist, has an incorrect name, or does not have any data in the worksheet", workSheetName)); } return workSheet; } public IEnumerable<DataRow> GetData(string workSheetName, bool isFirstRowAsColumnNames = true) { DataTable workSheet = GetExcelWorkSheet(workSheetName, isFirstRowAsColumnNames); IEnumerable<DataRow> rows = from DataRow row in workSheet.Rows select row; return rows; } } 

Und schließlich die Klasse Recipient (die nichts Besonderes tut):

namespace ConsoleApplicationForTestingPurposes { public class Recipient { public string Municipality { get; set; } public string Sexe { get; set; } public string LivingArea { get; set; } } } 

Die Ausnahmeklassen erben von Exception und übergeben einfach eine Nachricht an Exception.

Kommentare

  • Dieser Code funktioniert nicht mit allen Excel-Dateien … Beachten Sie, dass es auch einige andere EXCEL-File-Endungen gibt. ".xlsm" und ".xlsb" sowie die Vorlagentypen …
  • @ Vogel612 Danke, ich werde es nehmen berücksichtigt!
  • Dies war hilfreich. Wir hatten Code, der nur den OpenXml-Code verwendete, der eine Ausnahme auslöste, als jemand zum ersten Mal versuchte, .xls zu verwenden (Jahre nach der ersten Implementierung). Dann funktionierte der erste Versuch, das Problem zu beheben, nicht ‚, da nur der Binärleser ausprobiert wurde, nachdem der erste Leser ungültig war, aber dies den Stream ruinierte. Durch Überprüfen der Erweiterung wurde diese zuerst behoben.

Antwort

Vereinfachen Sie Ihre Fangkette

static void Main(string[] args) { try { ReadInData readInData = new ReadInData(@"C:\SC.xlsx", "sc_2014"); IEnumerable<Recipient> recipients = readInData.GetData(); } catch (Exception ex) { if(!(ex is FileNotFoundException || ex is ArgumentException || ex is FileToBeProcessedIsNotInTheCorrectFormatException)) throw; Console.WriteLine(ex.Message); } Console.Write(Press any key to continue...); Console.ReadKey(true); } 

Ich sehe keinen Grund, ReadInData eine nicht statische Klasse zu sein. Sie nutzen nicht die Tatsache aus, dass Sie sich den Pfad und den Arbeitsblattnamen merken, und Sie halten keine offene Verbindung zur Arbeitsmappe aufrecht. Sie können Ihren Code auch immer komplexer gestalten, indem Sie nur Variablen entfernen einmal verwendet (optional). Es gibt keinen Grund, dies zu ToList(), da Sie ohnehin eine IEnumerable<T> zurückgeben.

public static class ReadInData { public static IEnumerable<Recipient> GetData(string path, string worksheetName, bool isFirstRowAsColumnNames = true) { return new ExcelData(path).GetData(worksheetName, isFirstRowAsColumnNames) .Select(dataRow => new Recipient() { Municipality = dataRow["Municipality"].ToString(), Sexe = dataRow["Sexe"].ToString(), LivingArea = dataRow["LivingArea"].ToString() }); } } 

Sie können dasselbe für Ihre ExcelData -Klasse tun, dh diese Funktionen anstelle von Methoden aktivieren eine Klasse. Sie müssen nicht, aber auch hier nutzen Sie die OO nicht sehr aus, sodass Sie sie zur OO machen müssen.


Wenn Sie dies nicht tun Ich möchte / brauche jemanden, der Ihre ExcelData -Klasse verwendet, als sie zu kapseln. Ich bin jedoch der Meinung, dass Sie diesen Excel-Reader irgendwann wieder verwenden möchten. In diesem Fall würde ich diese Methoden in eine statische ExcelHelperClass verschieben. Und Ihre erste Klasse ReadInData Ich würde nur eine Methode in Ihrem ursprünglichen Programm erstellen.

private static IExcelDataReader GetExcelDataReader(string path, bool isFirstRowAsColumnNames) { using (FileStream fileStream = File.Open(path, FileMode.Open, FileAccess.Read)) { IExcelDataReader dataReader; if (path.EndsWith(".xls")) dataReader = ExcelReaderFactory.CreateBinaryReader(fileStream); else if (path.EndsWith(".xlsx")) dataReader = ExcelReaderFactory.CreateOpenXmlReader(fileStream); else throw new FileToBeProcessedIsNotInTheCorrectFormatException("The file to be processed is not an Excel file"); dataReader.IsFirstRowAsColumnNames = isFirstRowAsColumnNames; return dataReader; } } private static DataSet GetExcelDataAsDataSet(string path, bool isFirstRowAsColumnNames) { return GetExcelDataReader(path, isFirstRowAsColumnNames).AsDataSet(); } private static DataTable GetExcelWorkSheet(string path, string workSheetName, bool isFirstRowAsColumnNames) { DataTable workSheet = GetExcelDataAsDataSet(path, isFirstRowAsColumnNames).Tables[workSheetName]; if (workSheet == null) throw new WorksheetDoesNotExistException(string.Format("The worksheet {0} does not exist, has an incorrect name, or does not have any data in the worksheet", workSheetName)); return workSheet; } private static IEnumerable<DataRow> GetData(string path, string workSheetName, bool isFirstRowAsColumnNames = true) { return from DataRow row in GetExcelWorkSheet(path, workSheetName, isFirstRowAsColumnNames).Rows select row; } 

As In den Kommentaren erwähnt, berücksichtigen Sie nicht alle Excel-Dateitypen.


Wenn dies nicht für bool isFirstRowAsColumnNames wäre, würde ich vorschlagen, dass Sie tatsächlich fortfahren mit der gesamten OOP-Sache, und lassen Sie die Arbeitsmappe laden, wenn Sie sie erstellen, und nutzen Sie das OO-Design, indem Sie neben dem Pfad auch interne Daten haben. Ich denke, es ist in Ordnung, dass Sie ihnen die Option geben, isFirstRowAsColumnNames, das könnte sehr praktisch sein.

Kommentare

  • Zunächst einmal vielen Dank für Ihre Zeit beim Putten zusammen, was an meinem Code verbessert werden könnte. Ich habe jedoch ein paar Fragen: Die erste ist, dass ich ‚ nicht sicher bin, was Sie mit “ Und Ihre erste Klasse ReadInData Ich würde nur eine Methode in Ihrem ursprünglichen Programm erstellen. Das zweite ist, dass ich ‚ auch nicht wirklich sicher bin, was Sie mit dem ersten Satz Ihres letzten Absatzes gemeint haben.Wollen Sie damit sagen, dass ich meinen Code mehr oder weniger so behalten könnte, wie er war, wenn ich ‚ den Parameter isFirstRowAsColumnNames nicht hätte? Wenn ja, warum sollte es davon abhängen?

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.