Tavoite:

Haluan tuoda Excel-tiedoston ja lukea tiettyjen sarakkeiden rivit. Tähän käytän ExcelDataReader. Olen toteuttanut matalan tason luokan nimeltä ExcelData, joka käyttää tiedostoa ExcelDataReader ja tekee esimerkiksi selvittääkseen onko se .xls ”of” .xslx ”-tiedosto (tai ehkä jotain, joka ei liity täysin!) jne. Tämän luokan päälle tein luokan ReadInData, joka saa haluamasi sarakkeet Excelistä taulukko.

Suurimmat huolenaiheet:

  • Pääohjelmani saalisluettelo
  • Poikkeusten heittäminen
  • Kokonaisrakenne koodin ja koodin laadun
  • Pitäisikö minun kapseloida luokkani ExcelData ReadInData, vai pitääkö se sellaisenaan ?
  • isFirstRowAsColumnNames -parametrin

välittäminen Koska tämä on yrityksen koodi, muutin parin nimet luokista, joten tiedän, että ne eivät ole parhaita nimiä.

Koodini lähtökohta:

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

Tässä koodi luon uuden ReadInData luokan, johon passin s polku, tiedoston nimi ja Excel-taulukon nimi, jonka haluan lukea.

Huolenaihe: onko hyvä siirtää nämä asiat tiedostoon?

ReadInData -luokka:

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

Pohjimmiltaan ajattelin, että tarvitsin luokan ExcelData -luokka, koska se tuntui hieman korkeammalta tasolta.

ExcelData -luokka:

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

Ja lopuksi Recipient -luokka (joka ei tee mitään erityistä):

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

Poikkeusluokat perivät kohteen Exception ja välittävät vain viestin poikkeukselle.

Kommentit

  • Tämä koodi ei toimi kaikkien Excel-tiedostojen kanssa … Pidä mielessä myös joitain muita EXCEL-File-päätteitä. ".xlsm" ja ".xlsb" sekä mallityypit …
  • @ Vogel612 Kiitos, otan sen huomioon!
  • Tästä oli hyötyä. Meillä oli koodi, joka käytti juuri OpenXml-koodia, joka heitti poikkeuksen ensimmäistä kertaa, kun joku yritti käyttää .xls-tiedostoa (vuosia ensimmäisen käyttöönoton jälkeen). Sitten ensimmäinen yritys sen korjaamiseksi ei toiminut ’, koska se yritti vain binäärilukijaa sen jälkeen, kun ensimmäinen lukija oli virheellinen, mutta se pilasi virran. Laajennuksen tarkistaminen korjasi sen ensin.

Vastaa

Yksinkertaista sahaketjuasi

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

En näe syytä ReadInData olla ei-staattinen luokka. Et hyödynnä sitä, että muistat polun ja laskentataulukon nimen, etkä pidä jonkinlaista avointa yhteyttä työkirjaan. Ja tee aina vapaasti tehdä koodistasi monimutkaisempi poistamalla vain muuttujia käytetään kerran (valinnainen). Ei ole mitään syytä ToList() tähän, koska palautat joka tapauksessa 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() }); } } 

Voit tehdä tämän saman luokan ExcelData luokassa, eli .. tehdä nämä toiminnot menetelmien sijaan luokka. Sinun ei tarvitse, mutta jälleen kerran, et hyödynnä paljon OO: ta, joten sinun on tehtävä siitä OO.


Jos et haluavat / tarvitsevat jonkun ExcelData -luokan käyttäjällä kuin kapseloivat sen. Uskon kuitenkin, että haluat jossain vaiheessa pystyä käyttämään tätä Excel-lukijaa uudelleen, jolloin siirrän nämä menetelmät staattiseen ExcelHelperClass. Ja ensimmäisen luokan ReadInData tein vain menetelmän alkuperäiseen ohjelmaan.

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

Kuten kommenteissa mainittu, et ota huomioon kaikkia Excel-tiedostotyyppejä.


Jos bool isFirstRowAsColumnNames ei olisi ”t”, suosittelen, että jatkat eteenpäin koko OOP-jutun kanssa, ja työkirja on ladattava, kun rakennat sen, ja hyödynnä OO-suunnittelua hyödyntämällä todella sisäisiä tietoja polun lisäksi. Minusta on hienoa, että annat heille mahdollisuuden määrittää isFirstRowAsColumnNames, se voi olla erittäin kätevä.

Kommentit

  • Ensinnäkin, kiitos paljon panoksestasi. Yhdessä siihen, mitä koodissani voitaisiin parantaa. Minulla on kuitenkin pari kysymystä; ensimmäinen on se, että en ’ ole varma, mitä tarkoitit ” Ja ensimmäisen luokan ReadInDatan tekisin vain menetelmän alkuperäisessä ohjelmassa. Toinen asia on, että en ’ ole myöskään varma siitä, mitä tarkoitit viimeisen kappaleen ensimmäisellä virkkeellä.Tarkoitatko, että jos minulla ’ ei olisi isFirstRowAsColumnNames-parametria, voisin pitää koodini enemmän tai vähemmän samalla tavalla kuin se oli? Jos on, niin miksi se riippuu siitä?

Vastaa

Sähköpostiosoitettasi ei julkaista. Pakolliset kentät on merkitty *