Entity Frameworkを使用してテーブルをレンダリング

データベースからPDF文書にデータをエクスポートする際、最近人気のHTMLからPDFへの変換スキームを使用せずに行う方が便利な場合がいくつかあります。

この記事では、Aspose.PDF for .NETを使用してPDF文書を生成する方法を示します。

Aspose.PDFを使用したPDF生成の基本

Aspose.PDFで最も重要なクラスの1つはDocumentクラスです。このクラスはPDFレンダリングエンジンです。PDF構造を表現するために、Aspose.PDFライブラリはDocument-Pageモデルを使用します。ここで:

  • Document - PDF文書のプロパティを含み、ページコレクションを持ちます。
  • Page - 特定のページのプロパティと、このページに関連するさまざまな要素のコレクションを含みます。

したがって、Aspose.PDFを使用してPDF文書を作成するには、次の手順に従う必要があります。

  1. Documentオブジェクトを作成します。
  2. Documentオブジェクトにページ(Pageオブジェクト)を追加します。
  3. ページに配置されるオブジェクトを作成します(例:テキストフラグメント、テーブルなど)。
  4. 作成したアイテムをページの対応するコレクションに追加します(この場合は段落コレクションになります)。
  5. 文書をPDFファイルとして保存します。

最も一般的な問題は、テーブル形式でデータを出力することです。Tableクラスはテーブルを処理するために使用されます。このクラスは、テーブルを作成し、RowsCellsを使用して文書に配置する能力を提供します。したがって、テーブルを作成するには、必要な数の行を追加し、それらを適切な数のセルで埋める必要があります。

次の例では、4x10のテーブルを作成します。

Tableオブジェクトを初期化する際には、最小限のスキン設定が使用されました:

  • ColumnWidths - 列の幅(デフォルト)。
  • DefaultCellPadding - テーブルセルのデフォルトフィールド。
  • Border - テーブルフレームの属性(スタイル、厚さ、色)。
  • DefaultCellBorder - セルフレームの属性(スタイル、厚さ、色)。

その結果、等幅の列を持つ4x10のテーブルが得られます。

Table 4x10

ADO.NETオブジェクトからのデータのエクスポート

Tableクラスは、ADO.NETデータソースと対話するためのメソッドを提供します - ImportDataTableImportDataView。最初のメソッドはDataTableからデータをインポートし、2番目はDataViewからデータをインポートします。 これらのオブジェクトはMVCテンプレートで作業するにはあまり便利ではないため、簡単な例に制限します。この例では(行50)、ImportDataTableメソッドが呼び出され、DataTableインスタンスとヘッダーフラグ、データ出力の初期位置(行/列)などの追加設定をパラメータとして受け取ります。

Entity Frameworkからのデータのエクスポート

現代の.NETにとってより関連性が高いのは、ORMフレームワークからのデータのインポートです。この場合、単純なリストまたはグループ化されたデータからデータをインポートするための拡張メソッドでTableクラスを拡張するのが良いアイデアです。最も人気のあるORMの1つであるEntity Frameworkの例を示しましょう。

public static class PdfHelper
{
    private static void ImportEntityList<TSource>(this Aspose.Pdf.Table table, IList<TSource> data)
    {
        var headRow = table.Rows.Add();

        var props = typeof(TSource).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (var prop in props)
        {
            headRow.Cells.Add(prop.GetCustomAttribute(typeof(DisplayAttribute)) is DisplayAttribute dd ? dd.Name : prop.Name);
        }

        foreach (var item in data)
        {
            // Add row to table
            var row = table.Rows.Add();
            // Add table cells
            foreach (var t in props)
            {
                var dataItem = t.GetValue(item, null);
                if (t.GetCustomAttribute(typeof(DataTypeAttribute)) is DataTypeAttribute dataType)
                    switch (dataType.DataType)
                    {

                        case DataType.Currency:
                            row.Cells.Add(string.Format("{0:C}", dataItem));
                            break;
                        case DataType.Date:
                            var dateTime = (DateTime)dataItem;
                            if (t.GetCustomAttribute(typeof(DisplayFormatAttribute)) is DisplayFormatAttribute df)
                            {
                                row.Cells.Add(string.IsNullOrEmpty(df.DataFormatString)
                                    ? dateTime.ToShortDateString()
                                    : string.Format(df.DataFormatString, dateTime));
                            }
                            break;
                        default:
                            row.Cells.Add(dataItem.ToString());
                            break;
                    }
                else
                {
                    row.Cells.Add(dataItem.ToString());
                }
            }
        }
    }

    private static void ImportGroupedData<TKey, TValue>(this Aspose.Pdf.Table table, IEnumerable<Models.GroupViewModel<TKey, TValue>> groupedData)
    {
        var headRow = table.Rows.Add();
        var props = typeof(TValue).GetProperties(BindingFlags.Public | BindingFlags.Instance);
        foreach (var prop in props)
        {
            headRow.Cells.Add(prop.GetCustomAttribute(typeof(DisplayAttribute)) is DisplayAttribute dd ? dd.Name : prop.Name);
        }

        foreach (var group in groupedData)
        {
            // Add group row to table
            var row = table.Rows.Add();
            var cell = row.Cells.Add(group.Key.ToString());
            cell.ColSpan = props.Length;
            cell.BackgroundColor = Aspose.Pdf.Color.DarkGray;
            cell.DefaultCellTextState.ForegroundColor = Aspose.Pdf.Color.White;

            foreach (var item in group.Values)
            {
                // Add data row to table
                var dataRow = table.Rows.Add();
                // Add cells
                foreach (var t in props)
                {
                    var dataItem = t.GetValue(item, null);

                    if (t.GetCustomAttribute(typeof(DataTypeAttribute)) is DataTypeAttribute dataType)
                        switch (dataType.DataType)
                        {
                            case DataType.Currency:
                                dataRow.Cells.Add(string.Format("{0:C}", dataItem));
                                break;
                            case DataType.Date:
                                var dateTime = (DateTime)dataItem;
                                if (t.GetCustomAttribute(typeof(DisplayFormatAttribute)) is DisplayFormatAttribute df)
                                {
                                    dataRow.Cells.Add(string.IsNullOrEmpty(df.DataFormatString)
                                        ? dateTime.ToShortDateString()
                                        : string.Format(df.DataFormatString, dateTime));
                                }
                                break;
                            default:
                                dataRow.Cells.Add(dataItem.ToString());
                                break;
                        }
                    else
                    {
                        dataRow.Cells.Add(dataItem.ToString());
                    }
                }
            }
        }
    }
}

Data Annotations属性は、モデルを記述するためによく使用され、テーブルを作成するのに役立ちます。したがって、ImportEntityListのために次のテーブル生成アルゴリズムが選択されました:

  • 行12-18:ヘッダ行を構築し、「DisplayAttributeが存在する場合はその値を取得し、そうでなければプロパティ名を取得する」というルールに従ってヘッダーセルを追加します。
  • 行50-53:データ行を構築し、「DataTypeAttributeが定義されている場合は、追加のデザイン設定が必要かどうかを確認し、そうでなければデータを文字列に変換してセルに追加する」というルールに従って行セルを追加します。

この例では、DataType.Currency(行32-34)およびDataType.Date(行35-43)に対して追加のカスタマイズが行われましたが、必要に応じて他のカスタマイズを追加できます。 ImportGroupedDataメソッドのアルゴリズムは、前のものとほぼ同じです。グループ化されたデータを保存するために、追加のGroupViewModelクラスが使用されます。

using System.Collections.Generic;
public class GroupViewModel<K,T>
{
    public K Key;
    public IEnumerable<T> Values;
}

グループを処理するため、最初にキー値の行を生成し(行66-71)、その後にこのグループの行を生成します。