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:
Użytkownik klika niektóre przyciski w mojej aplikacji
Mój kod uruchamia zapytanie DB i jakoś przetwarza wyniki
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:
Pobieram dane raportu i generuję plik xml w wybranym przeze mnie formacie.
Następnie używam transformacji xsl do przekonwertowania pliku xml na plik arkusza kalkulacyjnego XML programu Excel 2003.
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.