Objectif:
Je souhaite importer un fichier Excel, et lire les lignes de certaines colonnes. Pour cela, jutilise ExcelDataReader
. Jai « implémenté une classe de bas niveau appelée ExcelData
qui utilise le ExcelDataReader
et fait des choses comme déterminer si cest un » .xls « of » .xslx « file (ou peut-être quelque chose de complètement sans rapport!) etc. En plus de cette classe, jai créé une classe ReadInData
, qui obtiendra les colonnes spécifiques que je veux à partir dExcel feuille.
Principales préoccupations:
- La liste des captures dans mon programme principal
- Lancement dexceptions
- La construction globale du code et de la qualité du code
- Dois-je encapsuler ma classe
ExcelData
dansReadInData
, ou la conserver telle quelle ? - Passage du paramètre
isFirstRowAsColumnNames
Parce que cest du code pour une entreprise, jai changé les noms dun couple de classes, donc je sais quils ne sont pas les meilleurs noms.
Le point dentrée de mon code:
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(); } }
Dans ce code Je crée une nouvelle classe ReadInData
, à laquelle je passe s le chemin, le nom du fichier et le nom de la feuille Excel que je veux lire.
Problème ici: est-il correct de passer ces choses dans ce fichier?
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(); } }
En gros, jai pensé que javais besoin dune classe en plus de ExcelData
, car il semblait de niveau un peu plus élevé.
La classe ExcelData
:
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; } }
Et enfin, la classe Recipient
(qui ne fait rien de spécial):
namespace ConsoleApplicationForTestingPurposes { public class Recipient { public string Municipality { get; set; } public string Sexe { get; set; } public string LivingArea { get; set; } } }
Les classes dexception héritent de Exception
, et transmettent simplement un message à Exception.
Commentaires
Réponse
Simplifiez votre chaîne de capture
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); }
Je ne vois aucune raison pour que ReadInData
soit une classe non statique. Vous ne profitez pas du fait que vous vous souvenez du chemin et du nom de la feuille de calcul, et vous ne gardez pas une sorte de connexion ouverte au classeur. Et nhésitez pas à rendre votre code plus complexe en supprimant les variables qui ne sont que utilisé une fois (facultatif). Il ny a aucune raison de ToList()
, car vous renvoyez un IEnumerable<T>
de toute façon.
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() }); } }
Vous pouvez faire la même chose pour votre classe ExcelData
, cest-à-dire … faire ces fonctions au lieu des méthodes sur une classe. Vous n’avez pas à le faire, mais encore une fois, vous n’avez pas beaucoup profité de l’OO, il est donc nécessaire que vous le rendiez OO.
Si vous ne le faites pas. voulez / avez besoin de quelquun qui utilise votre classe ExcelData
, que de lencapsuler. Cependant, je pense que vous voudrez à un moment donné pouvoir réutiliser ce lecteur Excel, auquel cas je déplacerais ces méthodes dans un Static ExcelHelperClass
. Et votre première classe ReadInData
Je ferais simplement une méthode dans votre programme dorigine.
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; }
Comme mentionné dans les commentaires, vous ne tenez pas compte de tous les types de fichiers Excel.
Si ce n’était pas pour le bool isFirstRowAsColumnNames
, je vous suggère d’aller de l’avant avec lensemble de la POO, et que le classeur se charge lorsque vous le construisez, et profitez de la conception OO en ayant en fait des données internes en plus du chemin. Je pense que cest bien que vous leur donniez la possibilité de spécifier isFirstRowAsColumnNames
, cela pourrait être très pratique.
Commentaires
- Tout dabord, merci beaucoup pour votre temps à mettre ensemble ce qui pourrait être amélioré à propos de mon code. Jai cependant quelques questions, la première étant que je ‘ ne suis pas sûr de ce que vous vouliez dire avec » Et votre première classe ReadInData, je ferais juste une méthode dans votre programme dorigine. La deuxième chose est que je ‘ ne suis pas non plus vraiment sûr de ce que vous vouliez dire avec la première phrase de votre dernier paragraphe.Êtes-vous en train de dire que si je navais pas ‘ le paramètre isFirstRowAsColumnNames, je pourrais conserver mon code plus ou moins tel quil était? Si oui, pourquoi en dépendrait-il?
".xlsm"
et".xlsb"
, ainsi que les types de modèles …