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 dans ReadInData, 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

  • Ce code ne fonctionne pas avec tous les fichiers Excel … Gardez à lesprit quil existe également dautres fins de fichier EXCEL. ".xlsm" et ".xlsb", ainsi que les types de modèles …
  • @ Vogel612 Merci, je vais le prendre en compte!
  • Cela a été utile. Nous avions du code qui utilisait juste celui dOpenXml, qui a jeté une exception la première fois que quelquun a essayé dutiliser .xls (des années après la première implémentation). Ensuite, la première tentative de correction na pas ‘ t fonctionner, car elle a juste essayé le lecteur binaire après que le premier lecteur était invalide, mais cela a ruiné le flux. La vérification de lextension a tout dabord résolu le problème.

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?

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *