Vedi il mio aggiornamento in basso per ulteriori informazioni.
Occasionalmente ho progetti in cui devo produrre alcuni dati come file Excel (formato xlsx). Il processo è di solito:
L'utente fa clic su alcuni pulsanti nella mia applicazione
Il mio codice esegue una query DB ed elabora i risultati in qualche modo
Il mio codice genera un file * .xlsx utilizzando le librerie di interoperabilità di Excel o alcune librerie di terze parti (ad esempio Aspose.Cells)
Posso facilmente trovare esempi di codice su come farlo online, ma sto cercando un modo più efficace per farlo. Vorrei che il mio codice seguisse alcuni principi di progettazione per garantire che il mio codice fosse mantenibile e facilmente comprensibile.
Ecco come appariva il mio tentativo iniziale di generare un file 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);
Pro: non molto. Funziona, quindi va bene.
Contro:
- I riferimenti di cella sono hardcoded, quindi ho numeri magici disseminati nel mio codice.
- È difficile aggiungere o rimuovere colonne e righe senza aggiornare molti riferimenti di cella.
- Devo imparare qualche biblioteca di terze parti. Alcune librerie vengono utilizzate come altre librerie, ma possono ancora esserci problemi. Ho avuto un problema in cui le librerie di interoperabilità com usano il riferimento di cella basato su 1 mentre Aspose.Cells usa il riferimento di cella basato su 0.
Ecco una soluzione che affronta alcuni dei contro che ho elencato sopra. Volevo trattare una tabella di dati come il proprio oggetto che può essere spostato e modificato senza scavare nella manipolazione delle celle e disturbare altri riferimenti di cella. Ecco alcuni pseudocodici:
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);
Come parte di questa soluzione, avrò un oggetto BlockEngine che prende un contenitore di blocchi ed esegue le manipolazioni delle celle richieste per generare i dati come file * .xlsx. A un oggetto Block può essere associata una formattazione.
Professionisti:
- Questo rimuove la maggior parte dei numeri magici che il mio codice iniziale aveva.
- Ciò nasconde molto codice di manipolazione delle celle, sebbene la manipolazione delle celle sia ancora richiesta nell'oggetto BlockEngine che ho citato.
- È molto più semplice aggiungere e rimuovere righe senza influire su altre parti del foglio di calcolo.
Contro:
- È ancora difficile aggiungere o rimuovere colonne. Se volessi scambiare la posizione delle colonne due e tre, dovrei scambiare direttamente il contenuto della cella. In questo caso si tratterebbe di otto modifiche e quindi di otto opportunità per commettere un errore.
- Se ho una formattazione in atto per quelle due colonne, devo aggiornare anche quella.
- Questa soluzione non supporta il posizionamento orizzontale dei blocchi; Posso solo posizionare un blocco sotto l'altro. Certo che avrei potuto
tableRight.PutToRightOf(tableLeft)
, ma ciò causerebbe problemi se tableRight e tableLeft avessero un numero diverso di righe. Per posizionare le tabelle, il motore dovrebbe essere a conoscenza di ogni altra tabella. Questo mi sembra inutilmente complicato. - Devo ancora imparare il codice di terze parti, anche se attraverso un livello di astrazione tramite oggetti Block e BlockEngine il codice sarà meno accoppiato alla libreria di terze parti rispetto al mio tentativo iniziale. Se volessi supportare molte diverse opzioni di formattazione in modo vagamente accoppiato, probabilmente dovrei scrivere molto codice; il mio BlockEngine sarebbe un gran casino.
Ecco una soluzione che prende una strada diversa. Ecco il processo:
Prendo i dati del mio rapporto e genera un file XML in un formato che scelgo.
Quindi uso una trasformazione xsl per convertire il file xml in un file di foglio di calcolo XML di Excel 2003.
Da lì semplicemente converto il foglio di calcolo XML in un file xlsx utilizzando una libreria di terze parti.
Ho trovato questa pagina che descrive un processo simile e include esempi di codice.
Professionisti:
- Questa soluzione non richiede quasi nessuna manipolazione cellulare. Invece usi xsl / xpath per fare le tue manipolazioni. Al fine di scambiare due colonne in una tabella, spostare tutte le colonne nel file xsl a differenza delle altre mie soluzioni che richiederebbero lo scambio di celle.
- Mentre hai ancora bisogno di una libreria di terze parti in grado di convertire un foglio di calcolo XML di Excel 2003 in un file xlsx, questo è tutto ciò di cui avrai bisogno per la libreria. La quantità di codice che devi scrivere che chiamerebbe nella libreria di terze parti è minuscola.
- Penso che questa soluzione sia la più semplice da capire e richieda la minima quantità di codice.
- Il codice che crea i dati nel mio formato XML sarà semplice.
- Il file xsl sarà complicato solo perché il foglio di calcolo XML di Excel 2003 è complicato. Tuttavia è facile controllare l'output del file xsl: basta aprire l'output in Excel e verificare la presenza di messaggi di errore.
- È facile generare file di foglio di calcolo XML di Excel 2003 di esempio: basta creare un foglio di calcolo che assomigli al file xlsx desiderato, quindi salvarlo come foglio di calcolo XML di Excel 2003.
Contro:
- I fogli di calcolo XML di Excel 2003 non supportano determinate funzionalità. Ad esempio, non puoi adattare automaticamente le larghezze di colonna. Non puoi includere immagini in intestazioni o piè di pagina. Se hai intenzione di esportare il file xlsx risultante in pdf, non puoi impostare segnalibri pdf. (Ho messo insieme una correzione per questo usando i commenti delle celle.). Devi farlo usando la tua libreria di terze parti.
- Richiede una libreria che supporta fogli di calcolo XML di Excel 2003.
- Utilizza un formato di file MS Office di 11 anni.
Nota: mi rendo conto che i file xlsx sono in realtà file zip contenenti file xml, ma la formattazione xml sembra troppo complicata per i miei scopi.
Infine, ho esaminato le soluzioni che coinvolgono SSRS, ma sembra troppo gonfio per i miei scopi.
Tornando alla mia domanda iniziale, qual è un buon modello di progettazione per la generazione di file Excel nel codice ?. Posso pensare ad alcune soluzioni, ma nessuna sembra spiccare come ideale. Ognuno ha degli svantaggi.
Aggiornamento: Quindi ho provato sia la mia soluzione BlockEngine che la mia soluzione XML Spreadsheet per generare file XLSX simili. Ecco le mie opinioni su di loro:
La soluzione BlockEngine:
- Ciò richiede semplicemente troppo codice considerando le alternative.
- Ho trovato troppo facile sovrascrivere un blocco con un altro se avevo un offset sbagliato.
- Inizialmente ho affermato che la formattazione potrebbe essere allegata a livello di blocco. Ho trovato che non era molto meglio che fare la formattazione separatamente dal contenuto del blocco. Non riesco a pensare a un buon modo per combinare il contenuto e la formattazione. Né posso trovare un buon modo per tenerli separati. È solo un casino.
La soluzione di foglio di calcolo XML:
- Vado con questa soluzione per ora.
- Vale la pena ripetere che questa soluzione richiede molto meno codice. Sto effettivamente sostituendo BlockEngine con Excel stesso. Ho ancora bisogno di un trucco per funzionalità come segnalibri e interruzioni di pagina.
- Il formato del foglio di calcolo XML è complicato, ma è facile apportare una piccola modifica e confrontare i risultati con un file esistente nel tuo programma Diff preferito. E una volta che hai scoperto un po 'di idiosincrasia, puoi metterlo in atto e dimenticartene da lì.
- Sono ancora preoccupato che questa soluzione si basi su un vecchio formato di file Excel.
- Il file XSLT che ho creato è facile da lavorare. Gestire la formattazione è molto più semplice qui che con la soluzione BlockEngine.