Смотрите мое обновление внизу для получения дополнительной информации.
Иногда у меня есть проекты, в которых я должен выводить некоторые данные в виде файла Excel (формат xlsx). Процесс обычно:
Пользователь нажимает некоторые кнопки в моем приложении
Мой код выполняет запрос к БД и как-то обрабатывает результаты
Мой код генерирует файл * .xlsx, используя библиотеки взаимодействия Excel или какую-либо стороннюю библиотеку (например, 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-взаимодействия используют ссылки на ячейки на основе 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, который принимает контейнер Blocks и выполняет манипуляции с ячейками, необходимые для вывода данных в виде файла * .xlsx. К объекту Block может быть прикреплено форматирование.
Плюсы:
- Это удаляет большинство магических чисел, которые были в моем исходном коде.
- Это скрывает много кода манипулирования ячейкой, хотя манипулирование ячейкой все еще требуется в объекте BlockEngine, о котором я упоминал.
- Гораздо проще добавлять и удалять строки, не затрагивая другие части электронной таблицы.
Минусы:
- По-прежнему сложно добавлять или удалять столбцы. Если бы я хотел поменять местами столбцы два и три, мне пришлось бы поменять местами содержимое ячейки. В этом случае это будет восемь правок и, следовательно, восемь возможностей ошибиться.
- Если у меня есть какое-либо форматирование для этих двух столбцов, я должен также обновить его.
- Это решение не поддерживает горизонтальное размещение блоков; Я могу разместить только один блок под другим. Конечно, мог
tableRight.PutToRightOf(tableLeft)
, но это вызвало бы проблемы, если бы tableRight и tableLeft имели разное количество строк. Чтобы разместить таблицы, движок должен знать о каждой другой таблице. Это кажется излишне сложным для меня. - Мне все еще нужно изучать сторонний код, хотя через слой абстракции через объекты Block и BlockEngine код будет менее тесно связан со сторонней библиотекой, чем моя первоначальная попытка. Если бы я хотел поддерживать множество различных вариантов форматирования в слабосвязанной форме, мне, вероятно, пришлось бы писать много кода; мой BlockEngine был бы огромным беспорядком.
Вот решение, которое идет другим путем. Вот процесс:
Я беру данные своего отчета и генерирую XML-файл в каком-то выбранном формате.
Затем я использую преобразование xsl для преобразования файла xml в файл электронной таблицы Excel 2003 XML.
Оттуда я просто преобразовываю электронную таблицу xml в файл xlsx, используя стороннюю библиотеку.
Я нашел эту страницу, которая описывает аналогичный процесс и содержит примеры кода.
Плюсы:
- Это решение практически не требует клеточных манипуляций. Вместо этого вы используете xsl / xpath для выполнения ваших манипуляций. Чтобы поменять местами два столбца в таблице, вы перемещаете целые столбцы в файле xsl, в отличие от других моих решений, которые требуют замены ячеек.
- Хотя вам по-прежнему нужна сторонняя библиотека, которая может конвертировать XML-таблицу Excel 2003 в файл xlsx, это почти все, что вам понадобится для этой библиотеки. Количество кода, которое вам нужно написать, чтобы вызвать стороннюю библиотеку, ничтожно мало.
- Я думаю, что это решение является самым простым для понимания и требует наименьшего количества кода.
- Код, который создает данные в моем собственном формате xml, будет простым.
- Файл xsl будет сложным только потому, что электронная таблица Excel 2003 сложна. Однако легко проверить вывод файла xsl: просто откройте вывод в Excel и проверьте сообщения об ошибках.
- Сгенерировать образцы файлов Excel 2003 XML Spreadsheet очень просто: просто создайте электронную таблицу, которая будет выглядеть как нужный вам файл xlsx, а затем сохраните ее как Excel 2003 XML Spreadsheet.
Минусы:
- XML-таблицы Excel 2003 не поддерживают определенные функции. Например, вы не можете автоматически подбирать ширину столбцов. Вы не можете включать изображения в верхние или нижние колонтитулы. Если вы собираетесь экспортировать полученный xlsx-файл в формат pdf, вы не можете установить закладки в формате pdf. (Я взломал исправление для этого, используя комментарии клетки.). Вы должны сделать это с помощью сторонней библиотеки.
- Требуется библиотека, поддерживающая электронные таблицы Excel 2003 XML.
- Использует 11-летний формат файла MS Office.
Примечание. Я понимаю, что xlsx-файлы на самом деле являются zip-файлами, содержащими xml-файлы, но форматирование xml кажется слишком сложным для моих целей.
Наконец, я рассмотрел решения, включающие SSRS, но он кажется слишком раздутым для моих целей.
Возвращаясь к моему первоначальному вопросу, что такое хороший шаблон проектирования для генерации файлов Excel в коде? Я могу придумать несколько решений, но ни одно из них не кажется идеальным. У каждого есть недостатки.
Обновление: поэтому я попробовал и мое решение BlockEngine, и свое решение для электронных таблиц XML для создания похожих файлов XLSX. Вот мои мнения о них:
Решение BlockEngine:
- Это просто требует слишком много кода, учитывая альтернативы.
- Мне было слишком легко переписать один блок другим, если у меня было неправильное смещение.
- Я изначально заявил, что форматирование может быть прикреплено на уровне блоков. Я обнаружил, что это не намного лучше, чем делать форматирование отдельно от содержимого блока. Я не могу придумать хороший способ объединить контент и форматирование. Я также не могу найти хороший способ держать их отдельно. Это просто беспорядок.
Решение электронной таблицы XML:
- Я собираюсь с этим решением сейчас.
- Следует повторить, что это решение требует гораздо меньше кода. Я эффективно заменяю BlockEngine на сам Excel. Мне все еще нужен хак для таких функций, как закладки и разрывы страниц.
- Формат XML Spreadsheet очень придирчив, но легко внести небольшие изменения и сравнить результаты с существующим файлом в вашей любимой программе Diff. И как только вы поймете какую-то особенность, вы можете поставить ее на место и забыть об этом оттуда.
- Я по-прежнему обеспокоен тем, что это решение опирается на старый формат файла Excel.
- Созданный мной файл XSLT прост в работе. Работа с форматированием здесь намного проще, чем с решением BlockEngine.