コードでExcel(xlsx)ファイルを生成するための良いデザインパターンは何ですか?


12

詳細については、下部の更新を参照してください。


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

長所:それほど多くはありません。動作するので、それは良いことです。

短所:

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

上に挙げたいくつかの短所に対処する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オブジェクトを用意します。ブロックオブジェクトには、書式を添付できます。

長所:

  • これにより、最初のコードにあったほとんどのマジックナンバーが削除されます。
  • これにより、多くのセル操作コードが非表示になりますが、私が述べた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ファイルを生成するための優れたデザインパターンは何ですか?いくつかの解決策を考えることができますが、理想的なものとして突出しているものはありません。それぞれに欠点があります。


更新:そこで、似たようなXLSXファイルを生成するために、BlockEngineソリューションとXML Spreadsheetソリューションの両方を試しました。私の意見は次のとおりです。

  • BlockEngineソリューション:

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

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

回答:


7

本当にうまく機能するものが本当に必要な場合は、「不必要に複雑」という考え方に慣れることをお勧めします。これがMicrosoft Officeファイル形式の処理の性質です。

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

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


Officeドキュメントはありませ「不必要に複雑」 -彼らはなど、やったりできる操作の巨大な範囲を、書式設定、機能している
ウォーレン

5
私は、ファイル形式自体が不必要に複雑であると主張しているのと同じように、ファイル形式を扱うことはそうだと主張しているのではありません。たとえば、OpenXML SDKを使用するには、要素を追加する魔法の順序を知っている必要があります。たとえば、プレゼンテーションへのスライドレイアウトの追加は機能しません。最初にスライドに追加してから、プレゼンテーションに追加する必要があります。どうして?Microsoftがライブラリをそのようにコーディングしたためです。管理する必要のある奇妙な循環参照もたくさんあります。このフォーマットには複雑さが必要なことは理解していますが、その作業はそれほど苦痛ではないはずです。
mgw854 14

3

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

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

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

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

長所:

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

  • 列に独自の書式設定がある限り、ほとんどの書式設定はテンプレートを操作してExcelで直接実行できます。これにより、WYSIWYGの感覚が得られます。また、Excelで使用可能な任意の書式設定オプションを、コードを記述する必要なく自由に使用できます。

短所:

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

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

  • 同じ列の異なるセルの動的な書式設定が必要な場合は、コードでそれを処理する必要があります

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


0

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

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

最初について:

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

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

2番目について:

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


元の質問では、自分のソリューションで書式設定や印刷オプションなどを制御したいかどうかは明確ではありませんでした。2番目の点に関しては、あなたが言及しているのは、BlockEngineソリューションで説明したものだと思います。オブジェクトをIList<IBusinessObject>取り出して吐き出すことができましたBlock。長所と短所は同じままです。
user2023861 14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.