使用实体框架渲染表格

有许多任务在某些情况下更方便将数据从数据库导出到 PDF 文档,而不使用最近流行的 HTML 到 PDF 转换方案。

本文将向您展示如何使用 Aspose.PDF for .NET 生成 PDF 文档。

使用 Aspose.PDF 生成 PDF 的基础知识

Aspose.PDF 中最重要的类之一是 Document class。该类是 PDF 渲染引擎。为了呈现 PDF 结构,Aspose.PDF 库使用文档-页面模型,其中:

  • Document - 包含 PDF 文档的属性,包括页面集合。
  • Page - 包含特定页面的属性和与该页面相关的各种元素集合。

因此,要使用 Aspose.PDF 创建 PDF 文档,您应遵循以下步骤:

  1. 创建 Document 对象。
  2. 为 Document 对象添加页面(Page 对象)。
  3. 创建放置在页面上的对象(例如文本片段、表格等)。
  4. 将创建的项目添加到页面上的相应集合中(在我们的例子中将是段落集合)。
  5. 将文档保存为 PDF 文件。

最常见的问题是以表格格式输出数据。 Table class 用于处理表格。该类使我们能够创建表格并将其放置在文档中,使用 RowsCells。因此,要创建表格,您需要添加所需数量的行并用适当数量的单元格填充它们。

以下示例创建了 4x10 的表格。

在初始化 Table 对象时,使用了最小的皮肤设置:

结果,我们得到了宽度相等的 4x10 表格。

Table 4x10

从 ADO.NET 对象导出数据

Table 类提供了与 ADO.NET 数据源交互的方法 - ImportDataTableImportDataView。第一个方法从 DataTable 导入数据,第二个从 DataView 导入数据。 假设这些对象在 MVC 模板中不太方便使用,我们将限制在一个简短的示例中。在此示例中(第 50 行),调用 ImportDataTable 方法,并接收 DataTable 实例和其他设置(如标题标志和数据输出的初始位置(行/列))作为参数。

从实体框架导出数据

对于现代 .NET,更相关的是从 ORM 框架导入数据。在这种情况下,扩展 Table 类以添加从简单列表或分组数据导入数据的扩展方法是个好主意。让我们以最流行的 ORM 之一 - 实体框架为例。

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

数据注释属性通常用于描述模型,并帮助我们创建表格。因此,为 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 行),然后是该组的行。