Докладніше див. У розділі Моє оновлення внизу.
Іноді у мене є проекти, де мені доводиться виводити деякі дані у вигляді файлу Excel (формат xlsx). Зазвичай процес:
Користувач натискає деякі кнопки в моїй програмі
Мій код виконує запит БД і якось обробляє результати
Мій код генерує файл * .xlsx, використовуючи бібліотеки інтеропів Excel com або якусь сторонній бібліотеку (наприклад, Aspose.Cells)
Я легко можу знайти приклади коду, як це зробити в Інтернеті, але я шукаю більш надійний спосіб зробити це. Я хотів би, щоб мій код дотримувався деяких принципів проектування, щоб мій код був легкодоступним і легко зрозумілим.
Ось як виглядала моя перша спроба генерування файлу xlsx:
var wb = new Workbook();
var ws = wb.Worksheets[0];
ws.Cells[0, 0].Value = "Header";
ws.Cells[1, 0].Value = "Row 1";
ws.Cells[2, 0].Value = "Row 2";
ws.Cells[3, 0].Value = "Row 3";
wb.Save(path);
Плюси: Не так багато. Це працює, так що це добре.
Мінуси:
- Посилання на клітини жорстко кодуються, тому в мене в коді є чарівні цифри.
- Додавати або видаляти стовпці та рядки важко без оновлення багатьох посилань на комірки.
- Мені потрібно вивчити якусь сторонній бібліотеку. Деякі бібліотеки використовуються, як і інші бібліотеки, але проблеми все ще можуть бути. У мене виникла проблема, коли бібліотеки com interop використовують посилання на клітини на основі 1, тоді як Aspose.Cells використовує посилання на клітини на основі 0.
Ось одне рішення, яке стосується деяких мінусів, які я перераховував вище. Я хотів розглядати таблицю даних як власний об'єкт, який можна переміщувати та змінювати, не занурюючись в маніпуляції з клітинками і не порушуючи інших посилань на комірки. Ось псевдокод:
var headers = new Block(new string[] { "Col 1", "Col 2", "Col 3" });
var body = new Block(new string[,]
{
{ "Row 1", "Row 1", "Row 1" },
{ "Row 2", "Row 2", "Row 2" },
{ "Row 3", "Row 3", "Row 3" }
});
body.PutBelow(headers);
У рамках цього рішення у мене буде об’єкт BlockEngine, який бере контейнер Блоків і виконує маніпуляції з комірки, необхідні для виведення даних у вигляді * .xlsx-файлу. Блок-об'єкт може мати до нього форматування.
Плюси:
- Це видаляє більшість магічних чисел, якими був мій початковий код.
- Це приховує багато коду маніпуляції з клітинками, хоча маніпуляція з комірками все ще потрібна в об'єкті BlockEngine, про який я згадував.
- Набагато простіше додавати та видаляти рядки, не впливаючи на інші частини електронної таблиці.
Мінуси:
- Додавання або видалення стовпців все ще важко. Якби я хотів поміняти місцями два та три стовпці, мені доведеться безпосередньо поміняти вміст комірок. У цьому випадку було б вісім правок, а значить, вісім можливостей зробити помилку.
- Якщо у мене є якесь форматування для цих двох стовпців, я також повинен це оновити.
- Це рішення не підтримує розміщення горизонтального блоку; Я можу розмістити лише один блок нижче іншого. Звичайно, я міг би мати це
tableRight.PutToRightOf(tableLeft)
, але це спричинило б проблеми, якби tableRight та tableLeft мали різну кількість рядків. Щоб розмістити таблиці, двигун повинен був знати всі інші таблиці. Мені це здається зайвим. - Мені все-таки потрібно вивчити сторонній код, хоча через шар абстракції через об'єкти Block та BlockEngine код буде менш щільно пов'язаний із сторонніми бібліотеками, ніж моя перша спроба. Якби я хотів підтримати безліч різноманітних варіантів форматування в зв'язаному вигляді, я, мабуть, мав би написати багато коду; мій BlockEngine був би величезним безладом.
Ось рішення, яке займає інший маршрут. Ось процес:
Я беру дані своїх звітів і генерую XML-файл у обраному вами форматі.
Потім я використовую перетворення xsl для перетворення файлу xml у файл таблиці Excel 2003 XML.
Звідти я просто конвертую таблицю xml у файл xlsx, використовуючи сторонні бібліотеки.
Я знайшов цю сторінку, яка описує подібний процес і включає приклади коду.
Плюси:
- Це рішення майже не вимагає маніпуляцій з клітинами. Ви замість цього використовуєте xsl / xpath для маніпуляцій. Для того, щоб поміняти два стовпчики в таблиці, ви переміщуєте цілі стовпці у файлі xsl на відміну від інших моїх рішень, які потребували б заміни комірок.
- Хоча вам ще потрібна стороння бібліотека, яка може конвертувати таблицю XML XML Excel 2003 у файл xlsx, це приблизно все, що вам знадобиться для бібліотеки. Кількість коду, який потрібно написати, який би зателефонував до сторонньої бібліотеки, невеликий.
- Я думаю, що це рішення є найпростішим для розуміння і вимагає найменшої кількості коду.
- Код, який створює дані у власному форматі XML, буде простим.
- Файл xsl буде складним лише тому, що електронна таблиця XML 2003 Excel складна. Однак перевірити вихід файлу xsl легко: просто відкрийте вихід у Excel і перевірте наявність повідомлень про помилки.
- Генерувати зразкові файли електронних таблиць Excel 2003 XML легко: просто створіть електронну таблицю, схожу на потрібний файл xlsx, а потім збережіть її як таблицю XML Excel 2003.
Мінуси:
- Електронні таблиці XML Excel 2003 не підтримують певні функції. Наприклад, не можна автоматично встановити ширину стовпців. Ви не можете включати зображення в колонтитули чи колонтитули. Якщо ви збираєтеся експортувати отриманий файл xlsx у pdf, ви не можете встановити закладки pdf. (Я зламав разом виправлення цього за допомогою коментарів у клітинках.) Ви повинні зробити це за допомогою своєї сторонньої бібліотеки.
- Потрібна бібліотека, яка підтримує таблиці Excel 2003 XML.
- Використовується 11-річний формат файлу MS Office.
Примітка. Я розумію, що файли xlsx - це фактично поштові файли, що містять файли xml, але форматування xml здається занадто складним для моїх цілей.
Нарешті, я розглядав рішення, що стосуються SSRS, але це здається занадто роздутим для моїх цілей.
Назад до мого початкового запитання, що таке хороша схема дизайну для генерації файлів Excel у коді ?. Я можу придумати декілька рішень, але жодне не здається ідеальним. У кожного є недоліки.
Оновлення. Тому я спробував як моє рішення BlockEngine, так і моє рішення XML-електронної таблиці для генерації подібних файлів XLSX. Ось моя думка щодо них:
Рішення BlockEngine:
- Для цього просто потрібно занадто багато коду з урахуванням альтернатив.
- Мені було занадто просто перезаписати один блок на інший, якщо я зсув неправильно.
- Я спочатку заявив, що форматування може бути додане на рівні блоку. Я вважав, що це не набагато краще, ніж робити форматування окремо від вмісту блоку. Я не можу придумати вдалого способу поєднання вмісту та форматування. Я також не можу знайти хороший спосіб тримати їх окремо. Це просто безлад.
Рішення електронної таблиці XML:
- Я зараз іду з цим рішенням.
- Повторюється, що для цього рішення потрібно набагато менше коду. Я ефективно замінюю BlockEngine самим Excel. Мені все одно потрібен злом для таких функцій, як закладки та розриви сторінок.
- Формат електронної таблиці XML - вибагливий, але легко змінити невеликі зміни та порівняти результати з існуючим файлом у вашій улюбленій програмі Diff. І як тільки ви з'ясуєте якусь ідіосинкразію, можете поставити її на місце і забути про неї звідти.
- Я все ще стурбований тим, що це рішення спирається на більш старий формат файлу Excel.
- Створений мною XSLT файл легко працювати. Справа з форматуванням тут набагато простіша, ніж з рішенням BlockEngine.