业务实体具有以下特点:
- 业务实体提供对业务数据及相关功能(在某些设计中)的状态编程访问。
- 业务实体可以使用具有复杂架构的数据来构建。这种数据通常来自数据库中的多个相关表。
- 业务实体数据可以作为业务过程的部分 I/O 参数传递。
- 业务实体可以是可序列化的,以保持它们的当前状态。例如,应用程序可能需要在本地磁盘、桌面数据库(如果应用程序脱机工作)或消息队列消息中存储实体数据。
- 业务实体不直接访问数据库。全部数据库访问都是由相关联的数据访问逻辑组件提供的。
- 业务实体不启动任何类型的事务处理。事务处理由使用业务实体的应用程序或业务过程来启动。
如本文前面所述,在您的应用程序中表示业务实体的方法有很多(从以数据为中心的模型到更加面向对象的表示法):
- XML
- 通用 DataSet
- 有类型的 DataSet
- 自定义业务实体组件
- 带有 CRUD 行为的自定义业务实体组件
以下各节将介绍如何使用这些格式来表示业务实体。为帮助您确定特定环境中最适宜的业务实体表示,以下各节将介绍如何为各业务实体格式执行以下任务:
- 组织业务实体集合
- 将业务实体数据绑定到用户界面控件
- 序列化业务实体数据
- 在层间传递业务实体数据
以下各节还针对非功能性要求(包括性能、效率、可缩放性和可扩展性)考虑了每种业务实体表示的适用性。
将业务实体表示为 XML以下示例显示了如何将一个简单的业务实体表示为 XML。该业务实体包含一个产品。
<?xml version="1.0"?> <Product xmlns="urn:aUniqueNamespace"> <ProductID>1</ProductID> <ProductName>Chai</ProductName> <QuantityPerUnit>10 boxes x 20 bags</QuantityPerUnit> <UnitPrice>18.00</UnitPrice> <UnitsInStock>39</UnitsInStock> <UnitsOnOrder>0</UnitsOnOrder> <ReorderLevel>10</ReorderLevel> </Product> |
当使用 XML 表示业务实体数据时,请考虑以下原则:
- 确定 XML 文档是包含单个业务实体还是包含一个业务实体集合。前面的示例表示的是单个 Product 业务实体。
- 使用一个命名空间唯一标识该 XML 文档,以避免与其他 XML 文档的内容发生命名冲突。前面的示例使用名为 urn:aUniqueNamespace 的默认命名空间。
- 为元素和属性选择合适的名称。前面的示例使用 Product 表的列名称,但并不要求一定这样。可以选择对您的应用程序有意义的名称。
- 使用以下方法之一以 XML 格式检索您的业务实体:
- 如果您使用的是 SQL Server 2000,则可以在查询或存储过程中使用 FOR XML 子句。在性能测试中,使用 FOR XML 只比返回 DataSet 稍微快一点。
- 检索 DataSet 并将其转换为 XML 流或以 XML 流的格式写出。这种方法会带来创建 DataSet 的系统开销和额外的转换开销(如果执行转换)。
- 使用输出参数或数据读取器构建一个 XML 文档。数据读取器是从数据库检索多个行的最快方法,但与构建 XML 相关联的过程可能会减弱这种性能优势。
将业务实体表示为 XML 的优点如下:
- 标准支持。XML 是 World Wide Web Consortium (W3C) 的标准数据表示格式。
- 灵活性。XML 能够表示信息的层次结构和集合。
- 互操作性。在所有平台上,XML 都是与外部各方及贸易伙伴交换信息的理想选择。如果 XML 数据将由 ASP.NET 应用程序或 Windows 窗体应用程序使用,则还可以把这些 XML 数据装载到一个 DataSet 中,以利用 DataSet 提供的数据绑定支持。
将业务实体表示为 XML 的缺点如下:
- 类型保真。XML 不支持类型保真。然而,对于简单的数据分类可以使用 XSD 架构。
- 验证 XML。要验证 XML,可以手动分析代码,或者使用 XSD 架构。但这两种方法都比较慢。
- 显示 XML。您不能将 XML 数据自动显示在用户界面上。可以编写一个 XSLT 样式表将数据转换为 DataSet;但样式表的编写比较麻烦。另一种方法是通过样式表将 XML 转换为 HTML 等可显示格式。
- 分析 XML。要分析 XML,可以使用文档对象模型 (DOM) 或 Microsoft .NET Framework 类库提供的 XmlReader 类。XmlReader 提供对 XML 数据的快速只读的、仅向前的访问,而 DOM 可以提供随机读/写访问,因此更灵活。然而,使用 DOM 分析 XML 文档的速度较慢;您必须创建一个 XmlDocument 实例(或另一个 XML 分析器类)并把整个 XML 文件装载到内存中。
- 排序 XML。您不能自动排序 XML 数据,而应使用以下技术之一:
- 按预先排好的顺序提供数据。这种方法不支持在调用应用程序中动态重新排序数据。
- 应用 XSLT 样式表动态排序数据。如果需要,可以使用 DOM 在运行时改变 XSLT 样式表中的排序条件。
- 将 XML 数据转换为 DataSet,并使用 DataView 对象排序和搜索数据元素。
- 使用专用字段。您不能选择隐藏信息。
通用 DataSet 是 DataSet 类的实例,它是在 ADO.NET 的 System.Data 命名空间中定义的。DataSet 对象包含一个或多个 DataTable 对象,用以表示数据访问逻辑组件从数据库检索到的信息。
图 7 所示为用于 Product 业务实体的通用 DataSet 对象。该 DataSet 对象具有一个 DataTable,用于保存产品信息。该 DataTable 具有一个 UniqueConstraint 对象,用于将 ProductID 列标记为主键。DataTable 和 UniqueConstraint 对象是在数据访问逻辑组件中创建该 DataSet 时创建的。
图 7:用于 Product 业务实体的通用 DataSet
图 8 所示为用于 Order 业务实体的通用 DataSet 对象。此 DataSet 对象具有两个 DataTable 对象,分别保存订单信息和订单详细信息。每个 DataTable 具有一个对应的 UniqueConstraint 对象,用于标识表中的主键。此外,该 DataSet 还有一个 Relation 对象,用于将订单详细信息与订单相关联。
图 8:用于 Order 业务实体的通用 DataSet
以下代码显示了如何从数据访问逻辑组件检索通用 DataSet ,然后将该 DataSet 绑定到 DataGrid 控件,再将该 DataSet 传递到数据访问逻辑组件以保存对数据所做的更改:
// 创建 ProductDALC 对象 ProductDALC dalcProduct = new ProductDALC(); // 对 ProductDALC 调用一个方法以获取一个包含全部产品信息的 DataSet // 在客户端中使用 DataSet。 例如,把该 DataSet 绑定到用户界面控件 // 然后,把更新后的 DataSet 传递给 ProductDALC,将更改 |
您还可以在运行时查询和修改 DataSet 中的表、约束及关系。
将业务实体表示为通用 DataSet 的优点如下:
- 灵活性。DataSet 可以包含数据的集合,能够表示复杂的数据关系。
- 序列化。在层间传递时,DataSet 本身支持序列化。
- 数据绑定。可以把 DataSet 绑定到 ASP.NET 应用程序和 Windows 窗体应用程序的任意用户界面控件。
- 排序与过滤。可以使用 DataView 对象排序和过滤 DataSet。应用程序可以为同一个 DataSet 创建多个 DataView 对象,以便用不同方式查看数据。
- 与 XML 的互换性。可以用 XML 格式读写 DataSet。这种方法在远程和脱机应用程序中很有用,它可以用 XML 格式接收 DataSet,然后在本地重新创建该 DataSet 对象。应用程序在与数据库断开连接后,还可以将 DataSet 保持为 XML 格式。
- 元数据的可用性。可以用 XSD 架构的形式为 DataSet 提供完整的元数据。还可以使用 DataSet、DataTable、DataColumn、Constraint 和 Relation 类中的方法以编程方式为 DataSet 获取元数据。
- 开放式并发。在更新数据时,可以配合使用数据适配器与 DataSet 以方便地执行开放式并发检查。
- 可扩展性。如果修改了数据库架构,则适当情况下数据访问逻辑组件中的方法可以创建包含修改后的 DataTable 和 DataRelation 对象的 DataSet。数据访问逻辑组件方法签名并不改变。可以将调用应用程序修改为使用该 DataSet 中的这些新元素。
将业务实体表示为通用 DataSet 的缺点如下:
- 客户端代码必须通过 DataSet 中的集合访问数据。要访问 DataSet 中的表,客户端代码必须使用整数索引生成器或字符串索引生成器来索引 DataTable 集合。要访问特定列,必须使用列编号或列名称索引 DataColumn 集合。以下示例显示了如何访问 Products 表中第一行的 ProductName 列:
// 获取所调用的名为 dsProducts 的 DataSet 的第一行的
// 产品名称。 注意,该集合是基于零的。
String str = (String)dsProducts.Tables["Products"].Rows[0]["ProductName"];
...
注意:这里没有这些索引生成器的编译时检查。如果指定一个无效的表名称、列名称或列类型,会在运行时捕获该错误。使用通用 DataSet 时不支持 IntelliSense。
- 实例化和封送处理的成本很高。DataSet 需要创建多个子对象(DataTable、DataRow 和 DataColumn),这意味着在实例化和封送处理时,DataSet 会比 XML 字符串或自定义实体组件花费更长的时间。随着数据量的增大,创建 DataSet 内部结构的系统开销将明显少于将数据填充到 DataSet 中所需的开销,因此 DataSet 的相对性能会随之提高。
- 专用字段。您不能选择隐藏信息。
有类型的 DataSet 是包含具有严格类型的方法、属性和类型定义以公开 DataSet 中的数据和元数据的类。
下面列出了有类型的 DataSet 与通用 DataSet 相比的优缺点。注意,有类型的 DataSet 的实例化和封送处理性能与通用 DataSet 基本相同。
将业务实体表示为有类型的 DataSet 的优点如下:
- 代码易读。要访问有类型的 DataSet 中的表和列,可以使用有类型的方法和属性,如以下代码所示:
...
// 获取所调用的名为 dsProducts 的有类型的 DataSet 的第一行的
// 产品名称。注意,该集合是基于零的。
String str = dsProducts.Products[0].ProductName;
...
在本示例中,dsProducts 是有类型的 DataSet 的一个实例。该 DataSet 有一个 DataTable,它由一个命名为 Products 的属性公开。该 DataTable 中的列由 ProductName 等属性公开,后者返回列的相应数据类型(而不仅仅返回对象)。
有类型的方法和属性的提供使得使用有类型的 DataSet 比使用通用 DataSet 更方便。使用有类型的 DataSet 时,IntelliSense 将可用。
- 编译时类型检查。无效的表名称和列名称将在编译时而不是在运行时检测。
将业务实体表示为有类型的 DataSet 的缺点如下:
- 部署。必须将包含有类型的 DataSet 类的程序集部署到使用业务实体的所有层。
- 支持企业服务 (COM+) 调用程序。如果有类型的 DataSet 将由 COM+ 客户端使用,则必须为包含该有类型的 DataSet 类的程序集提供一个严格名称,并且必须在客户端计算机上注册。通常,该程序集安装在全局程序集缓存中。这些也是自定义实体类所要求的步骤,如本文后面所述。
- 可扩展性问题。如果修改了数据库架构,则可能需要重新生成有类型的 DataSet 类以支持新架构。重新生成过程将不会保留在有类型的 DataSet 类中实现的任何自定义代码。必须将包含有类型的 DataSet 类的程序集重新部署到所有客户端应用程序中。
- 实例化。您不能使用 new 运算符来实例化类型。
- 继承。有类型的 DataSet 必须从 DataSet 类继承,这会禁止使用任何其他基本类。