Jaki jest dobry wzorzec projektowy do generowania pliku Excel (xlsx) w kodzie?

12

Zobacz moją aktualizację na dole, aby uzyskać więcej.


Czasami mam projekty, w których muszę wyprowadzać niektóre dane w postaci pliku Excela (format xlsx). Proces zwykle przebiega:

  1. Użytkownik klika niektóre przyciski w mojej aplikacji

  2. Mój kod uruchamia zapytanie DB i jakoś przetwarza wyniki

  3. Mój kod generuje plik * .xlsx przy użyciu bibliotek międzyoperacyjnych programu Excel lub biblioteki innej firmy (np. Aspose.Cells)

Mogę łatwo znaleźć przykłady kodu, jak to zrobić online, ale szukam bardziej niezawodnego sposobu, aby to zrobić. Chciałbym, aby mój kod był zgodny z pewnymi zasadami projektowania, aby zapewnić, że mój kod jest łatwy do utrzymania i łatwo zrozumiały.


Oto jak wyglądała moja początkowa próba wygenerowania pliku 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);

Plusy: niewiele. Działa, więc to dobrze.

Cons:

  • Odniesienia do komórek są zakodowane na stałe, więc mam magiczne liczby zaśmiecone w całym kodzie.
  • Trudno jest dodawać lub usuwać kolumny i wiersze bez aktualizacji wielu odniesień do komórek.
  • Muszę nauczyć się biblioteki innej firmy. Niektóre biblioteki są używane podobnie jak inne biblioteki, ale nadal mogą występować problemy. Miałem problem z tym, że biblioteki inter com używają odwoływania się do komórek na podstawie 1, podczas gdy Aspose.Cells używa odwoływania się do komórek na podstawie 0.

Oto jedno rozwiązanie, które dotyczy niektórych wad wymienionych powyżej. Chciałem traktować tabelę danych jako swój własny obiekt, który można przenosić i zmieniać bez zagłębiania się w manipulowanie komórkami i zakłócanie innych odniesień do komórek. Oto pseudokod:

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

W ramach tego rozwiązania będę mieć jakiś obiekt BlockEngine, który pobiera kontener bloków i wykonuje manipulacje komórkowe wymagane do wyprowadzenia danych jako plik * .xlsx. Do obiektu Block można dołączyć formatowanie.

Plusy:

  • To usuwa większość magicznych liczb, które miał mój początkowy kod.
  • Ukrywa to wiele kodów manipulacji komórkami, chociaż manipulowanie komórkami jest nadal wymagane w obiekcie BlockEngine, o którym wspomniałem.
  • Znacznie łatwiej jest dodawać i usuwać wiersze bez wpływu na inne części arkusza kalkulacyjnego.

Cons:

  • Nadal trudno jest dodawać lub usuwać kolumny. Gdybym chciał zamienić pozycję kolumn drugiej i trzeciej, musiałbym bezpośrednio zamienić zawartość komórki. W tym przypadku byłoby to osiem edycji, a więc osiem okazji do popełnienia błędu.
    • Jeśli mam jakieś formatowanie dla tych dwóch kolumn, muszę to również zaktualizować.
  • To rozwiązanie nie obsługuje poziomego umieszczania bloków; Mogę umieścić tylko jeden blok pod drugim. Pewnie, że mógłbym tableRight.PutToRightOf(tableLeft), ale spowodowałoby to problemy, gdyby tableRight i tableLeft miały inną liczbę wierszy. Aby umieścić tabele, silnik musiałby znać każdy inny stolik. Wydaje mi się to niepotrzebnie skomplikowane.
  • Nadal muszę się uczyć kodu innej firmy, chociaż poprzez warstwę abstrakcji poprzez obiekty Block i BlockEngine kod będzie mniej ściśle związany z biblioteką innej firmy niż moja pierwsza próba. Gdybym chciał obsługiwać wiele różnych opcji formatowania w luźny sposób, prawdopodobnie musiałbym napisać dużo kodu; mój BlockEngine byłby ogromnym bałaganem.

Oto rozwiązanie, które obiera inną trasę. Oto proces:

  1. Pobieram dane raportu i generuję plik xml w wybranym przeze mnie formacie.

  2. Następnie używam transformacji xsl do przekonwertowania pliku xml na plik arkusza kalkulacyjnego XML programu Excel 2003.

  3. Stamtąd po prostu przekonwertuję arkusz kalkulacyjny xml do pliku xlsx przy użyciu biblioteki innej firmy.

Znalazłem tę stronę, która opisuje podobny proces i zawiera przykłady kodu.

Plusy:

  • To rozwiązanie prawie nie wymaga manipulacji komórkami. Zamiast tego używasz xsl / xpath do manipulacji. Aby zamienić dwie kolumny w tabeli, przenosisz całe kolumny w pliku xsl, w przeciwieństwie do innych moich rozwiązań, które wymagałyby zamiany komórek.
  • Chociaż nadal potrzebujesz biblioteki innej firmy, która może konwertować arkusz kalkulacyjny XML programu Excel 2003 na plik xlsx, to wszystko, czego potrzebujesz do biblioteki. Ilość kodu, który musisz napisać, aby wywołać bibliotekę innej firmy, jest niewielka.
  • Myślę, że to rozwiązanie jest najłatwiejsze do zrozumienia i wymaga najmniejszej ilości kodu.
    • Kod tworzący dane w moim własnym formacie xml będzie prosty.
    • Plik xsl będzie skomplikowany tylko dlatego, że arkusz kalkulacyjny XML programu Excel 2003 jest skomplikowany. Łatwo jest jednak sprawdzić dane wyjściowe pliku xsl: wystarczy otworzyć dane wyjściowe w programie Excel i sprawdzić komunikaty o błędach.
    • Łatwo jest wygenerować przykładowe pliki arkuszy kalkulacyjnych XML Excel 2003: po prostu utwórz arkusz kalkulacyjny, który wygląda jak żądany plik xlsx, a następnie zapisz go jako arkusz kalkulacyjny XML 2003 Excel.

Cons:

  • Arkusze kalkulacyjne XML programu Excel 2003 nie obsługują niektórych funkcji. Nie można na przykład automatycznie dopasowywać szerokości kolumn. Nie możesz umieszczać obrazów w nagłówkach lub stopkach. Jeśli zamierzasz wyeksportować wynikowy plik xlsx do pdf, nie możesz ustawić zakładek pdf. (Zhackowałem razem poprawkę za pomocą komentarzy do komórki). Musisz to zrobić przy użyciu biblioteki innej firmy.
  • Wymaga biblioteki obsługującej arkusze kalkulacyjne XML programu Excel 2003.
  • Wykorzystuje 11-letni format pliku MS Office.

Uwaga: Zdaję sobie sprawę, że pliki xlsx to tak naprawdę pliki zip zawierające pliki xml, ale formatowanie xml wydaje się zbyt skomplikowane dla moich celów.


W końcu przyjrzałem się rozwiązaniom związanym z SSRS, ale wydaje się to zbyt rozdęte dla moich celów.


Wracając do mojego początkowego pytania, jaki jest dobry wzorzec projektowy do generowania plików Excel w kodzie ?. Mogę wymyślić kilka rozwiązań, ale żadne nie wydaje się być idealne. Każdy ma wady.


Aktualizacja: Więc wypróbowałem zarówno moje rozwiązanie BlockEngine, jak i moje rozwiązanie XML Spreadsheet do generowania podobnych plików XLSX. Oto moje opinie na ich temat:

  • Rozwiązanie BlockEngine:

    • Wymaga to po prostu zbyt dużo kodu, biorąc pod uwagę alternatywy.
    • Stwierdziłem, że zbyt łatwe jest zastąpienie jednego bloku innym blokiem, jeśli miałem nieprawidłowe przesunięcie.
    • Pierwotnie stwierdziłem, że formatowanie można dołączyć na poziomie bloku. Uważam, że nie jest to dużo lepsze niż formatowanie oddzielnie od zawartości bloku. Nie mogę wymyślić dobrego sposobu na połączenie treści i formatowania. Nie mogę też znaleźć dobrego sposobu na rozdzielenie ich. To tylko bałagan.
  • Rozwiązanie arkusza kalkulacyjnego XML:

    • Na razie idę z tym rozwiązaniem.
    • Powtarzam, że to rozwiązanie wymaga znacznie mniej kodu. Skutecznie zastępuję BlockEngine samym Excelem. Nadal potrzebuję włamania do funkcji takich jak zakładki i podziały stron.
    • Format arkusza kalkulacyjnego XML jest drobiazgowy, ale łatwo jest wprowadzić niewielką zmianę i porównać wyniki z plikiem istniejącym w ulubionym programie Diff. A kiedy odkryjesz jakąś osobliwość, możesz ją wprowadzić na miejscu i stamtąd o niej zapomnieć.
    • Nadal obawiam się, że to rozwiązanie opiera się na starszym formacie pliku Excel.
    • Utworzony przeze mnie plik XSLT jest łatwy w obsłudze. Obsługa formatowania jest tutaj o wiele prostsza niż w przypadku rozwiązania BlockEngine.
user2023861
źródło

Odpowiedzi:

7

Jeśli naprawdę chcesz czegoś, co będzie dla ciebie dobre, sugeruję przyzwyczaić się do idei „niepotrzebnie skomplikowanych” ... taka jest natura radzenia sobie z formatami plików Microsoft Office.

Podobał mi się twój pomysł na „bloki” ... Chciałbym, aby podklasowe obiekty blokowe, takie jak Tabela, miały Kolumny i Rzędy niezależne od pojęcia komórek. Następnie użyj silnika bloku, aby przekonwertować je na pliki XSLS.

W przeszłości korzystałem z zestawu OpenXML SDK , ale nie próbuj czytać dokumentacji i zaczynać od zera. Zamiast tego utwórz dokładną kopię tego, czego chcesz, zapisz ją i sprawdź za pomocą dostarczonego narzędzia Document Reflector. Otrzymasz kod C # potrzebny do utworzenia dokumentu, z którego możesz się uczyć i modyfikować.

mgw854
źródło
Dokumenty biurowe NIE są „niepotrzebnie skomplikowane” - wykonują lub zezwalają na ogromny zakres operacji, formatowania, funkcjonalności itp.
warren,
5
Nie twierdzę, że same formaty plików są niepotrzebnie złożone, tak jak twierdzę, że praca z nimi jest. Na przykład użycie zestawu OpenXML SDK wymaga znajomości magicznej kolejności dodawania elementów ... na przykład dodanie układu slajdów do prezentacji nie działa. Najpierw musisz dodać go do slajdu, a następnie do prezentacji. Dlaczego? Ponieważ Microsoft kodował biblioteki w ten sposób. Istnieje również wiele dziwnych okrągłych odniesień do zarządzania. Rozumiem, że format wymaga złożoności, ale praca z nim nie powinna być tak bolesna.
mgw854
3

Oto rozwiązanie, z którego często korzystałem:

  • utwórz zwykły dokument Excela (zwykle w formacie xlsx) jako szablon, zawierający wszystkie nagłówki kolumn, w tym ich tytuł i domyślne formatowanie kolumn, a być może formatowanie komórek tytułowych.

  • osadzić ten szablon w zasobach swojego programu. W czasie wykonywania pierwszym krokiem jest wyodrębnienie szablonu jako nowego pliku i umieszczenie go w folderze docelowym

  • użyj Interop lub biblioteki innej firmy do wypełnienia danych w nowo utworzonym xlsx. Nie należy odwoływać się do numerów kolumn zakodowanych na stałe, zamiast tego należy użyć niektórych metadanych (na przykład nagłówków kolumn), aby zidentyfikować prawidłowe kolumny.

Plusy:

  • coś w rodzaju podejścia blokowego działa teraz lepiej. Na przykład zamiana kolumn: nie trzeba nic zmieniać w kodzie bloku, ponieważ prawidłowe kolumny są identyfikowane przez ich nagłówki

  • tak długo, jak kolumny mają unikalne formatowanie, większość formatowania można wykonać bezpośrednio w programie Excel, manipulując szablonem. To daje poczucie WYSIWYG, wraz ze swobodą korzystania z dowolnej opcji formatowania dostępnej w Excelu bez potrzeby pisania dla niego kodu

Cons:

  • nadal musisz skorzystać z biblioteki innej firmy lub Interop. Czy wspominałem, że Interop działa powoli?

  • gdy nagłówki kolumn zmieniają się w szablonie, musisz także dostosować swój kod (ale można to łatwo wykryć dzięki procedurze sprawdzania poprawności, która sygnalizuje, że brakuje oczekiwanych kolumn)

  • kiedy potrzebujesz dynamicznego formatowania różnych komórek w tej samej kolumnie, nadal musisz sobie z tym poradzić w kodzie

Jako ogólna wskazówka, bez względu na to, jakie podejście wybierzesz: ma zalety oddzielania układu od treści i korzystania z deklaratywnych rozwiązań.

Doktor Brown
źródło
0

Należy wziąć pod uwagę dwie rzeczy:

  • Złożoność tworzenia pliku w danym formacie
  • Podatność kodu na uszkodzenie, gdy struktura zawartości pliku wymaga zmiany.

W odniesieniu do pierwszego:

Jeśli arkusze kalkulacyjne, które musisz wygenerować , nie zawierają żadnego formatowania ani formuł , możesz wygenerować plik CSV lub plik rozdzielany tabulatorami zamiast rzeczywistego XLSX. Excel otwiera te pliki, często domyślnie na wielu komputerach. To nie pomoże ci w twardym kodowaniu wokół kolumn i wierszy, ale pozwoli ci zaoszczędzić dodatkowej pracy związanej z manipulowaniem modelem obiektowym Excel.

Jeśli potrzebujesz formatowania lub formuł, dobrym pomysłem jest praca z modelem obiektowym Excel, zwłaszcza jeśli tworzysz arkusz kalkulacyjny, który sam nie jest zbyt „zakodowany na stałe”. Innymi słowy, jeśli arkusz kalkulacyjny używa odpowiednio formuł względnych i nazw zakresów, może iść w parze z mniej trudnym kodowaniem liczb magicznych.

W odniesieniu do drugiego:

Możesz pracować między komórkami za pomocą zakodowanych odwołań do wierszy i kolumn lub możesz pracować z tablicami / kolekcjami List i forpętlami, aby uogólnić populację komórek.

Joel Brown
źródło
W moim pierwotnym pytaniu nie było jasne, że chcę kontrolować opcje formatowania i drukowania itp. W moim rozwiązaniu. Jeśli chodzi o drugą kwestię, myślę, że to, o czym mówisz, opisałem w moim BlockEnginerozwiązaniu. Mogę wziąć IList<IBusinessObject>i wypluć Blockprzedmiot. Plusy i minusy pozostałyby takie same.
user2023861