Qual è un buon modello di progettazione per generare un file Excel (xlsx) nel codice?


12

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:

  1. L'utente fa clic su alcuni pulsanti nella mia applicazione

  2. Il mio codice esegue una query DB ed elabora i risultati in qualche modo

  3. 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:

  1. Prendo i dati del mio rapporto e genera un file XML in un formato che scelgo.

  2. Quindi uso una trasformazione xsl per convertire il file xml in un file di foglio di calcolo XML di Excel 2003.

  3. 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.

Risposte:


7

Se vuoi davvero qualcosa che funzioni bene per te, allora ti suggerisco di abituarti all'idea di "inutilmente complesso" ... questa è la natura della gestione dei formati di file di Microsoft Office.

Mi piace (un po ') la tua idea di "blocchi" ... Renderei oggetti a blocchi sotto-classificati, come Tabella, con colonne e righe indipendenti dalla nozione di celle. Quindi utilizzare il motore del blocco per convertirli in file XSLS.

Ho usato l' SDK OpenXML in passato, ma non provo a leggere la documentazione e ricominciare da capo. Invece, crea una copia esatta in Excel di ciò che desideri, salvalo e ispezionalo utilizzando lo strumento Document Reflector in dotazione. Ti fornirà il codice C # di cui hai bisogno per creare il documento, dal quale potrai quindi imparare e modificare.


I documenti di Office NON sono "inutilmente complessi" - stanno eseguendo o consentendo una vasta gamma di operazioni, formattazione, funzionalità, ecc.
Warren

5
Non sto sostenendo che i formati di file stessi siano inutilmente complessi tanto quanto sto sostenendo che lavorare con loro lo è. L'uso dell'SDK OpenXML, ad esempio, richiede di conoscere l'ordine magico in cui aggiungere elementi ... l'aggiunta di un layout di diapositiva a una presentazione, ad esempio, non funziona. Devi prima aggiungerlo alla diapositiva, quindi alla presentazione. Perché? Perché Microsoft ha codificato le librerie in quel modo. Ci sono anche molti strani riferimenti circolari da gestire. Capisco che il formato richiede complessità, ma lavorare con esso non dovrebbe essere così doloroso.
mgw854,

3

Ecco una soluzione che ho usato spesso in passato:

  • creare un normale documento Excel (in genere in formato xlsx) come modello, contenente tutte le intestazioni di colonna, incluso il titolo e una formattazione predefinita per le colonne e forse la formattazione per le celle del titolo.

  • incorpora quel modello nelle risorse del tuo programma. In fase di esecuzione, il primo passo è quello di estrarre il modello come nuovo file e posizionarlo nella cartella di destinazione

  • utilizzare Interop o una libreria di terze parti per riempire i dati nella xlsx appena creata. Non fare riferimento ai numeri di colonna codificati, ma utilizzare alcuni metadati (ad esempio le intestazioni di colonna) per identificare le colonne corrette.

Professionisti:

  • qualcosa come l'approccio Block ora funziona meglio. Ad esempio, scambio di colonne: non è necessario modificare nulla nel codice di blocco, poiché le colonne corrette sono identificate dalle intestazioni

  • fintanto che le colonne hanno una formattazione unica, la maggior parte della formattazione può essere eseguita direttamente in Excel, manipolando il modello. Questo ti dà la sensazione di WYSIWYG, insieme alla libertà di usare qualsiasi opzione di formattazione disponibile in Excel senza la necessità di scrivere codice per esso

Contro:

  • hai ancora bisogno di utilizzare una libreria di terze parti o Interop. Ho già detto che l'interoperabilità è lenta?

  • quando le intestazioni di colonna cambiano nel tuo modello, devi adattare anche il tuo codice (ma questo può essere facilmente rilevato avendo una routine di validazione che segnala se mancano le colonne previste)

  • quando hai bisogno di una formattazione dinamica di celle diverse nella stessa colonna, devi comunque occupartene nel codice

Come suggerimento generale, qualunque approccio scegliate: presenta vantaggi nel separare il layout dal contenuto e nell'utilizzare soluzioni dichiarative.


0

Ci sono due cose da considerare:

  • Complessità nella creazione di un file in un determinato formato
  • Suscettibilità del codice alla rottura quando la struttura del contenuto del file deve cambiare.

Per quanto riguarda il primo:

Se i fogli di calcolo che devi generare non contengono alcuna formattazione o formula , allora è piuttosto semplice generare un file CSV o delimitato da tabulazioni anziché un XLSX reale. Excel apre questi file, spesso per impostazione predefinita su molti PC. Questo non ti aiuterà con la codifica rigida attorno a colonne e righe ma ti risparmierà il lavoro extra di manipolazione del modello di oggetti di Excel.

Se hai bisogno di formattazione o formule, lavorare con il modello a oggetti di Excel è un modo ragionevole di procedere, specialmente se crei un foglio di calcolo che non è troppo "codificato". In altre parole, se il tuo foglio di calcolo utilizza formule relative e nomi di intervalli in modo appropriato, può andare insieme con una codifica meno rigida dei numeri magici.

Per quanto riguarda il secondo:

È possibile lavorare cella per cella con riferimenti di riga e colonna codificati, oppure è possibile lavorare con matrici / raccolte di elenchi e forcicli per generalizzare la popolazione di celle.


Non ero chiaro nella mia domanda originale che volevo controllare le opzioni di formattazione e stampa e simili nella mia soluzione. Per quanto riguarda il secondo punto, penso che ciò a cui ti riferisci sia quello che ho descritto nella mia BlockEnginesoluzione. Potrei prendere uno IList<IBusinessObject>e sputare un Blockoggetto. I pro e i contro sarebbero comunque gli stessi.
user2023861,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.