使用实体框架渲染表格
有许多任务在某些情况下更方便将数据从数据库导出到 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 文档,您应遵循以下步骤:
创建 Document 对象。
为 Document 对象添加页面(Page 对象)。
创建放置在页面上的对象(例如文本片段、表格等)。
将创建的项目添加到页面上的相应集合中(在我们的例子中将是段落集合)。
将文档保存为 PDF 文件。
.NET Core 3.1
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void AddTable ()
{
// Create PDF document
using ( var document = new Aspose . Pdf . Document
{
PageInfo = new Aspose . Pdf . PageInfo { Margin = new Aspose . Pdf . MarginInfo ( 28 , 28 , 28 , 42 ) }
})
{
// Add page
var page = document . Pages . Add ();
var textFragment = new Aspose . Pdf . Text . TextFragment ( reportTitle );
var table = new Aspose . Pdf . Table
{
// .................................
};
page . Paragraphs . Add ( textFragment );
page . Paragraphs . Add ( table );
using ( var streamOut = new MemoryStream ())
{
// Save PDF document
document . Save ( streamOut );
return new FileContentResult ( streamOut . ToArray (), "application/pdf" )
{
FileDownloadName = "tenants.pdf"
};
}
}
}
.NET 8
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void AddTable ()
{
// Create PDF document
using var document = new Aspose . Pdf . Document
{
PageInfo = new Aspose . Pdf . PageInfo { Margin = new Aspose . Pdf . MarginInfo ( 28 , 28 , 28 , 42 ) }
};
// Add page
var page = document . Pages . Add ();
var textFragment = new Aspose . Pdf . Text . TextFragment ( reportTitle );
var table = new Aspose . Pdf . Table
{
// .................................
};
page . Paragraphs . Add ( textFragment );
page . Paragraphs . Add ( table );
using var streamOut = new MemoryStream ();
// Save PDF document
document . Save ( streamOut );
return new FileContentResult ( streamOut . ToArray (), "application/pdf" )
{
FileDownloadName = "tenants.pdf"
};
}
最常见的问题是以表格格式输出数据。 Table class 用于处理表格。该类使我们能够创建表格并将其放置在文档中,使用 Rows 和 Cells 。因此,要创建表格,您需要添加所需数量的行并用适当数量的单元格填充它们。
以下示例创建了 4x10 的表格。
.NET Core 3.1
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void AddTable ()
{
// The path to the documents directory
var dataDir = RunExamples . GetDataDir_AsposePdf_WorkingDocuments ();
// Create PDF document
using ( var document = new Aspose . Pdf . Document ())
{
// Add page
var page = document . Pages . Add ();
var table = new Aspose . Pdf . Table
{
// Set column auto widths of the table
ColumnWidths = "25% 25% 25% 25%" ,
// Set cell padding
DefaultCellPadding = new Aspose . Pdf . MarginInfo ( 10 , 5 , 10 , 5 ), // Left Bottom Right Top
// Set the table border color as Green
Border = new Aspose . Pdf . BorderInfo ( Aspose . Pdf . BorderSide . All , . 5f , Aspose . Pdf . Color . Green ),
// Set the border for table cells as Black
DefaultCellBorder = new Aspose . Pdf . BorderInfo ( Aspose . Pdf . BorderSide . All , . 2f , Aspose . Pdf . Color . Green ),
};
for ( var rowCount = 0 ; rowCount < 10 ; rowCount ++)
{
// Add row to table
var row = table . Rows . Add ();
// Add table cells
for ( int i = 0 ; i < 4 ; i ++)
{
row . Cells . Add ( $"Cell ({i + 1}, {rowCount + 1})" );
}
}
// Add table object to first page of input document
page . Paragraphs . Add ( table );
// Save PDF document
document . Save ( dataDir + "AddTable_out.pdf" );
}
}
.NET 8
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void AddTable ()
{
// The path to the documents directory
var dataDir = RunExamples . GetDataDir_AsposePdf_WorkingDocuments ();
// Create PDF document
using var document = new Aspose . Pdf . Document ();
// Add page
var page = document . Pages . Add ();
var table = new Aspose . Pdf . Table
{
// Set column auto widths of the table
ColumnWidths = "25% 25% 25% 25%" ,
// Set cell padding
DefaultCellPadding = new Aspose . Pdf . MarginInfo ( 10 , 5 , 10 , 5 ), // Left Bottom Right Top
// Set the table border color as Green
Border = new Aspose . Pdf . BorderInfo ( Aspose . Pdf . BorderSide . All , . 5f , Aspose . Pdf . Color . Green ),
// Set the border for table cells as Black
DefaultCellBorder = new Aspose . Pdf . BorderInfo ( Aspose . Pdf . BorderSide . All , . 2f , Aspose . Pdf . Color . Green ),
};
for ( var rowCount = 0 ; rowCount < 10 ; rowCount ++)
{
// Add row to table
var row = table . Rows . Add ();
// Add table cells
for ( int i = 0 ; i < 4 ; i ++)
{
row . Cells . Add ( $"Cell ({i + 1}, {rowCount + 1})" );
}
}
// Add table object to first page of input document
page . Paragraphs . Add ( table );
// Save PDF document
document . Save ( dataDir + "AddTable_out.pdf" );
}
在初始化 Table 对象时,使用了最小的皮肤设置:
结果,我们得到了宽度相等的 4x10 表格。
从 ADO.NET 对象导出数据
Table 类提供了与 ADO.NET 数据源交互的方法 - ImportDataTable 和 ImportDataView 。第一个方法从 DataTable 导入数据,第二个从 DataView 导入数据。
假设这些对象在 MVC 模板中不太方便使用,我们将限制在一个简短的示例中。在此示例中(第 50 行),调用 ImportDataTable 方法,并接收 DataTable 实例和其他设置(如标题标志和数据输出的初始位置(行/列))作为参数。
.NET Core 3.1
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void AddTable ()
{
// Create PDF document
using ( var document = new Aspose . Pdf . Document
{
PageInfo = new Aspose . Pdf . PageInfo { Margin = new Aspose . Pdf . MarginInfo ( 28 , 28 , 28 , 42 ) }
})
{
var table = new Aspose . Pdf . Table
{
// Set column widths of the table
ColumnWidths = "25% 25% 25% 25%" ,
// Set cell padding
DefaultCellPadding = new Aspose . Pdf . MarginInfo ( 10 , 5 , 10 , 5 ), // Left Bottom Right Top
// Set the table border color as Green
Border = new Aspose . Pdf . BorderInfo ( Aspose . Pdf . BorderSide . All , . 5f , Aspose . Pdf . Color . Green ),
// Set the border for table cells as Black
DefaultCellBorder = new Aspose . Pdf . BorderInfo ( Aspose . Pdf . BorderSide . All , . 2f , Aspose . Pdf . Color . Green ),
};
var configuration = new ConfigurationBuilder ()
. SetBasePath ( Directory . GetCurrentDirectory ())
. AddJsonFile ( "config.json" , false )
. Build ();
var connectionString = configuration . GetSection ( "connectionString" ). Value ;
if ( string . IsNullOrEmpty ( connectionString ))
{
throw new ArgumentException ( "No connection string in config.json" );
}
var resultTable = new DataTable ();
using ( var conn = new SqlConnection ( connectionString ))
{
const string sql = "SELECT * FROM Tennats" ;
using ( var cmd = new SqlCommand ( sql , conn ))
{
using ( var adapter = new SqlDataAdapter ( cmd ))
{
adapter . Fill ( resultTable );
}
}
}
table . ImportDataTable ( resultTable , true , 1 , 1 );
// Add table object to first page of input document
document . Pages [ 1 ]. Paragraphs . Add ( table );
using ( var streamOut = new MemoryStream ())
{
// Save PDF document
document . Save ( streamOut );
return new FileContentResult ( streamOut . ToArray (), "application/pdf" )
{
FileDownloadName = "demotable2.pdf"
};
}
}
}
.NET 8
// For complete examples and data files, visit https://github.com/aspose-pdf/Aspose.PDF-for-.NET
private static void AddTable ()
{
// Create PDF document
using var document = new Aspose . Pdf . Document
{
PageInfo = new Aspose . Pdf . PageInfo { Margin = new Aspose . Pdf . MarginInfo ( 28 , 28 , 28 , 42 ) }
};
var table = new Aspose . Pdf . Table
{
// Set column widths of the table
ColumnWidths = "25% 25% 25% 25%" ,
// Set cell padding
DefaultCellPadding = new Aspose . Pdf . MarginInfo ( 10 , 5 , 10 , 5 ), // Left Bottom Right Top
// Set the table border color as Green
Border = new Aspose . Pdf . BorderInfo ( Aspose . Pdf . BorderSide . All , . 5f , Aspose . Pdf . Color . Green ),
// Set the border for table cells as Black
DefaultCellBorder = new Aspose . Pdf . BorderInfo ( Aspose . Pdf . BorderSide . All , . 2f , Aspose . Pdf . Color . Green ),
};
var configuration = new ConfigurationBuilder ()
. SetBasePath ( Directory . GetCurrentDirectory ())
. AddJsonFile ( "config.json" , false )
. Build ();
var connectionString = configuration . GetSection ( "connectionString" ). Value ;
if ( string . IsNullOrEmpty ( connectionString ))
{
throw new ArgumentException ( "No connection string in config.json" );
}
var resultTable = new DataTable ();
using var conn = new SqlConnection ( connectionString );
const string sql = "SELECT * FROM Tennats" ;
using var cmd = new SqlCommand ( sql , conn );
using var adapter = new SqlDataAdapter ( cmd );
adapter . Fill ( resultTable );
table . ImportDataTable ( resultTable , true , 1 , 1 );
// Add table object to first page of input document
document . Pages [ 1 ]. Paragraphs . Add ( table );
using var streamOut = new MemoryStream ();
// Save PDF document
document . Save ( streamOut );
return new FileContentResult ( streamOut . ToArray (), "application/pdf" )
{
PageInfo = new Aspose . Pdf . PageInfo { Margin = new Aspose . Pdf . MarginInfo ( 28 , 28 , 28 , 42 ) }
};
}
从实体框架导出数据
对于现代 .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 行),然后是该组的行。