Mål:

Jeg vil importere en Excel-fil og læse rækkerne i bestemte kolonner. Til dette bruger jeg ExcelDataReader. Jeg har implementeret en klasse på lavt niveau kaldet ExcelData som bruger ExcelDataReader og gør ting som at finde ud af om det er et “.xls “af” .xslx “-fil (eller måske noget helt uafhængigt!) osv. Oven på den klasse lavede jeg en ReadInData -klasse, som får de specifikke kolonner, jeg vil have fra Excel ark.

Største bekymringer:

  • Fangslisten i mit hovedprogram
  • Kaste af undtagelser
  • Den samlede konstruktion af koden og kodekvaliteten
  • Skal jeg indkapsle min ExcelData klasse inden for ReadInData, eller beholde den som den er ?
  • Videregivelse af isFirstRowAsColumnNames parameter

Fordi dette er kode for en virksomhed, ændrede jeg navnene på et par af klasser, så jeg ved, at de ikke er de bedste navne.

Indgangsstedet for min kode:

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(); } } 

I dette kode Jeg opretter en ny ReadInData klasse, som jeg passerer s stien, navnet på filen og navnet på Excel-arket, som jeg vil læse.

Bekymring her: er det okay at videregive disse ting inden for den fil?

ReadInData -klassen:

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(); } } 

Grundlæggende regnede jeg med, at jeg havde brug for en klasse oven på ExcelData klasse, fordi det syntes noget højere niveau.

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; } } 

Og til sidst Recipient klasse (som ikke gør noget særligt):

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

Undtagelsesklasser arver fra Exception, og send bare en meddelelse til Undtagelse.

Kommentarer

  • Denne kode fungerer ikke med alle Excel-filer … Husk at der også er nogle andre EXCEL-fil slutninger. ".xlsm" og ".xlsb" samt skabelontyperne …
  • @ Vogel612 Tak, jeg tager det med i betragtning!
  • Dette var nyttigt. Vi havde kode, der lige brugte OpenXml-en, hvilket kastede en undtagelse første gang nogen forsøgte at bruge .xls (år efter første implementering). Derefter fungerede det første forsøg på at rette det ikke ‘, fordi det bare prøvede den binære læser, efter at den første læser var ugyldig, men det ødelagde strømmen. Kontrol af udvidelsen fik først ordnet det.

Svar

Forenkl din fangstkæde

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); } 

Jeg ser ingen grund til at have ReadInData til at være en ikke-statisk klasse. Du udnytter ikke det faktum, at du husker stien og regnearknavnet, og du holder ikke en slags åben forbindelse til projektmappen. Og lad altid din kode se mere kompleks ud ved at fjerne variabler, der kun er bruges en gang (valgfrit). Der er ingen grund til at ToList() dette, fordi du alligevel returnerer en IEnumerable<T>.

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() }); } } 

Du kan gøre den samme ting til din ExcelData klasse, dvs. at lave disse funktioner i stedet for metoder på en klasse. Det behøver du ikke, men igen udnytter du ikke meget OO, så der er behov for, at du gør det til OO.


Hvis du ikke ønsker / har brug for nogen, der bruger din ExcelData klasse, end at indkapsle den. Jeg føler dog, at du på et eller andet tidspunkt vil være i stand til at genbruge denne excel-læser, i hvilket tilfælde jeg vil flytte disse metoder til en statisk ExcelHelperClass. Og din første klasse ReadInData Jeg ville bare lave en metode i dit originale program.

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; } 

Som nævnt i kommentarerne, regner du ikke med alle Excel-filtyper.


Hvis det ikke var “t for bool isFirstRowAsColumnNames Jeg vil foreslå, at du rent faktisk går fremad med hele OOP-tingen, og lad arbejdsbogen indlæses, når du konstruerer den, og drage fordel af OO-design ved faktisk at have interne data udover bare stien. Jeg synes, det er fint, at du giver dem mulighed for at specificere isFirstRowAsColumnNames, det kunne være meget praktisk.

Kommentarer

  • Først og fremmest tak for din tid med at sætte sammen hvad der kunne forbedres med min kode. Jeg har dog et par spørgsmål; den første er, at jeg ‘ ikke er sikker på, hvad du mente med ” Og din første klasse ReadInData Jeg ville bare lave en metode i dit originale program. Den anden ting er, at jeg ‘ også ikke rigtig er sikker på, hvad du mente med første sætning i dit sidste afsnit.Siger du, at hvis jeg ikke ville ‘ ikke have isFirstRowAsColumnNames-parameteren, kunne jeg beholde min kode mere eller mindre som den var? Hvis ja, hvorfor ville det afhænge af det?

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *