web-dev-qa-db-ja.com

コードでExcel(xlsx)ファイルを生成するための優れた設計パターンは何ですか?

詳細については、下部にある私のアップデートを参照してください。


一部のデータをExcelファイル(xlsx形式)として出力しなければならないプロジェクトがあります。プロセスは通常:

  1. ユーザーがアプリケーションの一部のボタンをクリックした

  2. 私のコードはDBクエリを実行し、結果を何らかの形で処理します

  3. 私のコードは、Excel com相互運用ライブラリまたはいくつかのサードパーティライブラリ(Aspose.Cellsなど)を使用して* .xlsxファイルを生成します

オンラインでこれを行う方法のコード例を簡単に見つけることができますが、これを行うためのより堅牢な方法を探しています。コードをいくつかの設計原則に従って、コードが保守可能で理解しやすいものになっていることを確認します。


以下は、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);
_

長所:それほどではありません。それは機能するので、それは良いことです。

短所:

  • セル参照はハードコードされているため、コード全体にマジックナンバーが散らばっています。
  • 多くのセル参照を更新せずに列や行を追加または削除することは困難です。
  • いくつかのサードパーティのライブラリを学ぶ必要があります。一部のライブラリは他のライブラリと同じように使用されますが、それでも問題が発生する可能性があります。 Aspose.Cellsが0ベースのセル参照を使用するのに対し、com interopライブラリは1ベースのセル参照を使用するという問題がありました。

上記の短所の一部に対処する1つのソリューションを次に示します。データのテーブルを独自のオブジェクトとして扱い、セルの操作を掘り下げたり他のセル参照を妨害したりすることなく、移動したり変更したりできるようにしました。ここにいくつかの疑似コードがあります:

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

このソリューションの一部として、ブロックのコンテナーを取得し、*。xlsxファイルとしてデータを出力するために必要なセル操作を実行するいくつかのBlockEngineオブジェクトを用意します。 Blockオブジェクトには、フォーマットを関連付けることができます。

長所:

  • これにより、最初のコードにあったマジックナンバーのほとんどが削除されます。
  • これにより、多くのセル操作コードが非表示になりますが、前述のBlockEngineオブジェクトではセル操作が必要です。
  • スプレッドシートの他の部分に影響を与えることなく、行を追加および削除する方がはるかに簡単です。

短所:

  • 列を追加または削除することは依然として困難です。列2と3の位置を入れ替えたい場合は、セルの内容を直接入れ替える必要があります。この場合、それは8つの編集であり、したがって、ミスをする8つの機会となります。
    • これらの2つの列の書式を設定している場合は、それも更新する必要があります。
  • このソリューションは水平ブロック配置をサポートしていません。 1つのブロックを別のブロックの下にのみ配置できます。確かにtableRight.PutToRightOf(tableLeft)を使用できますが、tableRightとtableLeftの行数が異なる場合は問題が発生します。テーブルを配置するには、エンジンは他のすべてのテーブルを認識する必要があります。これは不必要に複雑に思えます。
  • 私はまだサードパーティのコードを学ぶ必要がありますが、BlockオブジェクトとBlockEngineを介した抽象化のレイヤーを介して、コードは私の最初の試みよりもサードパーティのライブラリに密結合されていません。疎結合の方法で多くの異なるフォーマットオプションをサポートしたい場合は、おそらく多くのコードを記述する必要があります。私のBlockEngineは非常に混乱します。

これは別のルートを取るソリューションです。ここにプロセスがあります:

  1. レポートデータを取得して、選択した形式でxmlファイルを生成します。

  2. 次に、xsl変換を使用して、xmlファイルをExcel 2003 XMLスプレッドシートファイルに変換します。

  3. そこから、サードパーティのライブラリを使用して、xmlスプレッドシートをxlsxファイルに変換するだけです。

同様のプロセスを説明し、コード例を含む このページ を見つけました。

長所:

  • このソリューションは、ほとんど細胞操作を必要としません。代わりに、xsl/xpathを使用して操作を行います。テーブル内の2つの列を交換するには、セル交換を必要とする他のソリューションとは異なり、xslファイル内の列全体を移動します。
  • Excel 2003 XMLスプレッドシートをxlsxファイルに変換できるサードパーティライブラリがまだ必要ですが、それだけでライブラリが必要になります。サードパーティのライブラリを呼び出すために必要なコードの量はごくわずかです。
  • この解決策が最も理解しやすく、必要なコードも最小限であると思います。
    • 私自身のxml形式でデータを作成するコードは単純です。
    • Excel 2003 XMLスプレッドシートが複雑なため、xslファイルは複雑になります。ただし、xslファイルの出力を確認するのは簡単です。Excelで出力を開き、エラーメッセージを確認するだけです。
    • サンプルのExcel 2003 XMLスプレッドシートファイルを生成するのは簡単です。目的のxlsxファイルのようなスプレッドシートを作成し、それをExcel 2003 XMLスプレッドシートとして保存するだけです。

短所:

  • Excel 2003 XMLスプレッドシートは、特定の機能をサポートしていません。たとえば、列幅を自動調整することはできません。ヘッダーやフッターに画像を含めることはできません。結果のxlsxファイルをpdfにエクスポートする場合、pdfブックマークを設定することはできません。 (私はセルのコメントを使用して、これに対する修正をハッキングしました。)サードパーティのライブラリを使用してこれを行う必要があります。
  • Excel 2003 XMLスプレッドシートをサポートするライブラリが必要です。
  • 11年前のMS Officeファイル形式を使用します。

注:xlsxファイルは実際にはxmlファイルを含むZipファイルですが、xmlのフォーマットは目的に対して複雑すぎるようです。


最後に、SSRSに関連するソリューションを調べましたが、私の目的には膨らんでいるようです。


最初の質問に戻りますが、コードでExcelファイルを生成するための優れたデザインパターンは何ですか。私はいくつかの解決策を考えることができますが、どれも理想的なものとしてはみ出していないようです。それぞれに欠点があります。


更新:そこで、BlockEngineソリューションとXMLスプレッドシートソリューションの両方を試して、同様のXLSXファイルを生成しました。ここにそれらの私の意見があります:

  • BlockEngineソリューション:

    • これは単に、代替案を考慮すると多すぎるコードを必要とします。
    • オフセットが間違っていると、あるブロックを別のブロックで上書きするのは簡単すぎると思いました。
    • 元々、ブロックレベルで書式を設定できると述べました。これは、ブロックのコンテンツとは別にフォーマットを実行するよりもはるかに優れていることがわかりました。コンテンツとフォーマットを組み合わせる良い方法は考えられません。それらを分離しておく良い方法も見つけられません。それはただの混乱です。
  • XMLスプレッドシートソリューション:

    • とりあえず、このソリューションを使用します。
    • このソリューションが必要とするコードがはるかに少ないことは繰り返します。私は効果的にBlockEngineをExcel自体に置き換えています。ブックマークや改ページなどの機能のハックはまだ必要です。
    • XMLスプレッドシート形式は細心の注意が必要ですが、小さな変更を加えて、お気に入りのDiffプログラムの既存のファイルと結果を比較するのは簡単です。そして、いくつかの特異性を理解したら、それを所定の位置に配置して、そこからそれを忘れることができます。
    • この解決策が古いExcelファイル形式に依存していることは、まだ心配です。
    • 私が作成したXSLTファイルは操作が簡単です。ここでは、フォーマットの処理は、BlockEngineソリューションよりもはるかに簡単です。
12
user2023861

あなたが本当にうまくいくものを本当に望んでいるなら、「不必要に複雑」という考えに慣れることをお勧めします...それがMicrosoft Officeファイルフォーマットを扱う性質です。

私は「ブロック」のあなたのアイデアが(ちょっと)好きです...私は、テーブルのようなサブクラス化されたブロックオブジェクトを、セルの概念から独立した列と行で作成します。次に、ブロックエンジンを使用して、これらをXSLSファイルに変換します。

私は過去に OpenXML SDK を使用して成功しましたが、ドキュメントを読んでゼロから始めようとしないでください。代わりに、必要なものの正確なコピーをExcelで作成して保存し、提供されているドキュメントリフレクターツールを使用して検査します。これにより、ドキュメントの作成に必要なC#コードが提供され、そこから学習および変更できます。

7
mgw854

これは私が過去に頻繁に使用したソリューションです:

  • テンプレートとして、通常のExcelドキュメント(通常はxlsx形式)を作成します。これには、タイトル、列のデフォルトのフォーマット、タイトルセルのフォーマットなど、すべての列ヘッダーが含まれます。

  • そのテンプレートをプログラムのリソースに埋め込みます。実行時の最初のステップは、テンプレートを新しいファイルとして抽出し、それを宛先フォルダーに配置することです

  • interopまたはサードパーティのライブラリを使用して、新しく作成したxlsxにデータを入力します。ハードコードされた列番号を参照しないでください。代わりに、いくつかのメタデータ(列ヘッダーなど)を使用して正しい列を識別してください。

長所:

  • ブロックアプローチのようなものがよりよく機能するようになりました。たとえば、列スワッピング:正しい列はヘッダーによって識別されるため、ブロックコードで何も変更する必要はありません。

  • 列に一意のフォーマットが設定されている限り、テンプレートを操作することにより、ほとんどのフォーマットをExcelで直接行うことができます。これにより、WYSIWYGの感覚が得られ、コードを記述する必要なく、Excelで使用できるフォーマットオプションを自由に使用できます。

短所:

  • サードパーティのlibまたはInteropを使用する必要があります。 Interopが遅いと言ったことはありますか?

  • テンプレートで列ヘッダーが変更されると、コードも調整する必要があります(ただし、予期される列が欠落している場合に通知する検証ルーチンを使用することで簡単に検出できます)

  • 同じ列の異なるセルの動的なフォーマットが必要な場合でも、コードでそれを処理する必要があります

一般的なヒントとして、どのアプローチを選択しても、レイアウトをコンテンツから分離し、宣言型ソリューションを利用することには利点があります。

3
Doc Brown

考慮すべき点が2つあります。

  • 特定の形式でファイルを作成する複雑さ
  • ファイルのコンテンツの構造を変更する必要がある場合のコードの破損に対する脆弱性。

最初について:

生成する必要があるスプレッドシートフォーマットや数式を含めないの場合、実際のXLSXの代わりにCSVまたはタブ区切りファイルを生成するのは非常に簡単です。 Excelはこれらのファイルを開きます。多くの場合、デフォルトでは多くのPCで開かれています。これは列と行の周りのハードコーディングには役立ちませんが、Excelオブジェクトモデルを操作する余分な作業を省きます。

書式設定または数式が必要な場合、特にそれ自体が「ハードコード」されていないスプレッドシートを作成する場合は、Excelオブジェクトモデルでの作業が合理的な方法です。言い換えると、スプレッドシートが相対数式と範囲名を適切に使用している場合は、マジックナンバーのハードコーディングを減らすことができます。

2番目について:

ハードコードされた行と列の参照を使用してセルごとに作業するか、配列/リストコレクションおよびforループを使用してセルの母集団を一般化できます。

0
Joel Brown