Obiettivo:
Voglio importare un file Excel e leggere le righe di determinate colonne. Per questo, utilizzo ExcelDataReader
. Ho implementato una classe di basso livello chiamata ExcelData
che utilizza ExcelDataReader
e fa cose come capire se si tratta di un “.xls “di” .xslx “(o forse qualcosa di completamente non correlato!) ecc. In cima a quella classe ho creato una classe ReadInData
, che otterrà le colonne specifiche che desidero da Excel foglio.
Principali preoccupazioni:
- Lelenco delle catture nel mio programma principale
- Lancio di eccezioni
- La costruzione complessiva del codice e della qualità del codice
- Devo incapsulare la mia classe
ExcelData
allinterno diReadInData
o mantenerla comè ? - Passaggio del
isFirstRowAsColumnNames
parametro
Poiché questo è il codice per unazienda, ho cambiato i nomi di una coppia di classi, quindi so che non sono il migliore dei nomi.
Il punto di ingresso del mio codice:
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 questo codice creo una nuova classe ReadInData
, alla quale passo è il percorso, il nome del file e il nome del foglio Excel che voglio leggere.
Riguarda qui: va bene passare queste cose allinterno di quel file?
La classe ReadInData
:
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(); } }
Fondamentalmente, ho pensato che avevo bisogno di una classe in cima a ExcelData
classe, perché sembrava di livello leggermente superiore.
La ExcelData
classe:
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; } }
E infine, la classe Recipient
(che non fa niente di speciale):
namespace ConsoleApplicationForTestingPurposes { public class Recipient { public string Municipality { get; set; } public string Sexe { get; set; } public string LivingArea { get; set; } } }
Le classi di eccezione ereditano da Exception
e passano semplicemente un messaggio a Exception.
Commenti
Risposta
Semplifica il tuo catch-chain
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); }
Non vedo alcun motivo per cui ReadInData
sia una classe non statica. Non stai approfittando del fatto che stai ricordando il percorso e il nome del foglio di lavoro e non stai mantenendo una sorta di connessione aperta alla cartella di lavoro. Sentiti sempre libero di rendere il tuo codice più complesso rimuovendo le variabili che sono solo utilizzato una sola volta (opzionale). Non cè motivo di ToList()
, perché stai restituendo un IEnumerable<T>
comunque.
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() }); } }
Puoi fare la stessa cosa per la tua classe ExcelData
, cioè .. rendendo queste funzioni invece dei metodi su una lezione. Non è necessario, ma ancora una volta, non stai sfruttando molto lOO, quindi è necessario che tu lo faccia OO.
Se non lo fai vuoi / hai bisogno di qualcuno che usi la tua classe ExcelData
, piuttosto che incapsularla. Tuttavia, credo che a un certo punto vorrai essere in grado di riutilizzare questo lettore Excel, nel qual caso sposterò questi metodi in un ExcelHelperClass
statico. E la tua prima classe ReadInData
creerei solo un metodo nel tuo programma originale.
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 menzionato nei commenti, non stai tenendo conto di tutti i tipi di file Excel.
Se non fosse “t per bool isFirstRowAsColumnNames
ti suggerirei di andare avanti con lintera faccenda dellOOP, e carica la cartella di lavoro quando la costruisci e sfrutta il design OO avendo effettivamente dati interni oltre al solo percorso. Penso che sia giusto che tu dia loro la possibilità di specificare isFirstRowAsColumnNames
, potrebbe essere molto utile.
Commenti
- Prima di tutto, grazie mille per il tempo che ci hai dedicato insieme cosa si potrebbe migliorare del mio codice. Tuttavia, ho un paio di domande; la prima è che ‘ non sono sicuro di cosa intendevi con ” E la tua prima classe ReadInData vorrei solo creare un metodo nel tuo programma originale. La seconda cosa è che ‘ non sono nemmeno sicuro di cosa intendevi con la prima frase del tuo ultimo paragrafo.Stai dicendo che se ‘ non avessi il parametro isFirstRowAsColumnNames, potrei mantenere il mio codice più o meno comera? In tal caso, perché dovrebbe dipendere da questo?
".xlsm"
e".xlsb"
, nonché i tipi di modello …