Enterprise Library 数据访问应用程序块的设计

此文档维护在:http://wiki.entlib.net.cn/EntlibHelp31DataAccessApplicationBlock.ashx

数据访问应用程序块包含下列特性:
  • 用不同数据库系统工作的简单而有效的方法(请参见用于简单数据访问的设计)
  • 开发数据库诊断应用程序的方法(请参见用于数据库诊断应用程序的设计)
  • 调整和验证数据库配置设置的简单方法

设计目标

应用程序块被设计以达到下列目标:
  • 封装用于完成绝大多数普通数据访问任务的逻辑。
  • 消除常见代码错误,如错误的关闭连接。
  • 减轻开发人员为常见数据访问任务编写重复代码的需要。
  • 减少定制代码的需要。
  • 为数据访问加入最佳实践,就像在.NET Data Access Architecture Guide中描述的一样。
  • 确保应用程序块的功能尽可能的可以与不同的数据库类型一起工作。
  • 确保为一种数据库编写的应用程序,在数据访问方面,也同样是为另一种数据库类型编写的应用程序。

设计亮点

图 1 说明了数据访问应用程序块中关键类之间的关系。
图片

图 1 数据访问应用程序块中关键类之间的关系


假设客户代码使用的是应用程序块配置信息,它调用了 DatabaseFactory 类的 CreateDatabase 方法创建 Database 对象的实例。DatabaseFactory 使用在配置文件中找到的配置信息来决定要构造的 Database 对象类型,然后返回给应用程序。工厂使用下列标准来决定要构造的 Database 对象类型:

  • 如果客户代码传递在配置文件中的标识连接字符串的数据库实例名称,工厂将使用那个字符串来创建 Database 对象。
注意:.NET Framework 类现在维护了在配置文件中的 connectionStrings 节中的连接字符串。这意味着连接字符串的定义可以被所有访问 connectionStrings 节的应用程序所共享。
  • 如果客户代码不传递数据库实例名称,工厂将由配置文件中的默认实例设置标识连接字符串。在 dataConfiguration 配置节中的 defaultDatabase 属性控件了默认实例。下列来自配置文件的 XML 片段展示了与 Production 连接字符串对应的默认实例。

<dataConfiguration defaultDatabase="Production">

在配置文件中的 <connectionStrings> 节映射逻辑标识(名称)到连接字符串和 DbProviderFactory 类型。DatabaseFactory 从连接字符中获得 ADO.NET 数据提供程序的逻辑名称。DatabaseFactory 对象然后调用 .NET Framework 的 DbProviderFactory 类获得 ADO.NET 数据提供程序的完整类型名称。DatabaseFactory 使用此信息决定构造的 Database 对象的类型。尽管在连接字符串中的 .NET Framework 认为数据库提供程序名称是可选的,但数据访问应用程序块必须需要它们。

应用程序块配置代码包含了默认的映射。映射了 SqlDatabase 对象到 System.Data.SqlClient 数据提供程序,映射了 OracleDatabase 对象到 System.Data.OracleClient 数据提供程序,GenericDatabase 对象到所有其他的数据提供程序。可以使用配置控制台覆写默认的映射。

修改默认的映射或者使用配置控制台添加新的映射会在配置文件中创建一个 providerMappings 节。providerMappings 节映射 DbProviderFactory 类型到 Database 类型。可以通过创建自己的数据库类并映射它到 DbProviderFactory 对象来添加自己的映射。

抽象基类 Database 定义了通用接口并为数据访问方法提供了许多实现。SqlDatabase 类和 OracleDatabase 类派生自 Database 类。它们提供了到各自数据库服务器系统的方法,包括了通用功能的不同数据库的不同实现,以及专用于相应数据库系统的功能。应用程序块还包括了一个 GenericDatabase 类。此类不提供任何数据专用的特性,并可以与任何 ADO.NET 数据提供程序一起进行操作。

应用程序块支持存储过程的参数动态发现。这种发现需要到数据库系统的一次回返。ParameterCache 类允许缓存参数信息,这避免了在同样存储过程的后继调用中的回返。(GenericDatabase 类不支持参数发现)。

5.1 - 用于简化数据访问的设计

开发人员在构建数据访问解决方案时会面临许多实现选择和需求。他们必须以不同的方式访问数据,解决方案必须工作在不同的数据库类型下,并以不同的方式处理数据。结果是,开发人员可能发现它们自己在重复完成通用任务的代码,例如管理连接和给命令中的参数赋值。

另一个挑战是在如何实现数据访问操作中维持一致的方法。可能是在单个项目、多个项目或者企业级解决方案中维持这种一致性。数据访问的制度化的方法使代码更易于理解、更加可以预测并易于维护。

数 据访问应用程序块通过封装完成常见数据库操作的逻辑来简化数据访问。这些方法还处理如打开和关闭连接这样的通用内部任务。它们是数据库透明的,这意味着它 们用 SQL Server 和 Oracle 数据库工作时不需要进行修改。为一种数据库编写的应用程序所使用的方法与为另一种数据库所编写的方法是一样的。这意味着应用程序在访问数据的方式方面是一 致的。另外,GenericDatabase 类支持许多通用于 ADO.NET 数据提供程序间的一样的特性。

设计内涵

应用程序块为简化访问数据的任务而设计。因此,它需要下列的设计决定:
  • 仅暴露了少量的开发人员需要理解的方法。
  • 封装了常见的内部任务。
  • 使其易于处理参数。
  • 提供了很好的性能。
  • 简化数据库对象的创建。

下面的节描述了这些决定。

有限的操作集

应用程序块支持少量的简化最常见数据访问任务的操作。它提供了抽象的基类:Database,Database 类定义了应用程序块支持的方法集合。这些方法如下:
  • ExecuteDataSet
  • LoadDataSet
  • ExecuteReader
  • ExecuteScalar
  • ExecuteNonQuery
  • UpdateDataSet

每个方法都有多个重载。重载通过每个方法传递的信息进行不同程度的控制,以方便不同的程序类型。重载类之一允许传递一个 ADO.NET 的类型 DbCommand 的对象,如下面示例所示:

C#
Database db = DatabaseFactory.CreateDatabase();

DbCommand dbCommand = db.GetStoredProcCommand("GetProductList");

DataSet productsDataSet = db.ExecuteDataSet(dbCommand);

Visual Basic
Dim db As Database = DatabaseFactory.CreateDatabase()

Dim dbCommand As DbCommand = db.GetStoredProcCommand("GetProductList")

Dim productsDataSet As DataSet = db.ExecuteDataSet(dbCommand)

Database 类上的每个可用方法,有二个接受 DbCommand 对象的重载版本:一个重载用于在事务外执行,另一个用于在事务内执行。例如,下列代码示例是用于 ExecuteDataSet 方法的二个重载,第一个没有事务,而第二个有事务。

C#
public virtual DataSet ExecuteDataSet(DbCommand command) 

public virtual DataSet ExecuteDataSet(DbCommand command, IDbTransaction transaction)

Visual Basic
Public Overridable Function ExecuteDataSet(ByRef command As DbCommand) As DataSet

Public MustOverride Function ExecuteDataSet(ByRef command As DbCommand, ByRef transaction As IDbTransaction) As DataSet

注意:数据访问应用程序块的事务是 ADO.NET 2.0 DbTransaction 基类的实例,应用程序块不使用属于 ADO.NET 2.0 System.Transaction 命名空间中的事务。

对于喜欢简单的传递所有信息到 Database 类的方法的开发人员,每个方法的重载都允许在单一调用中提供要求的信息。例如,ExecuteDataSet 包含了下列的重载,这些重载允许开发人员传递一个存储过程的名称和所使用的参数的集合。

C#
public virtual DataSet ExecuteDataSet(string storedProcedureName, params object[] parameterValues)

Visual Basic
Public Overridable Function ExecuteDataSet(ByRef storedProcedureName As String, ByRef parameterValues As Object()) As DataSet

连接生命周期的封装

开发人员必须考虑的最常见的任务之一是如何管理到数据库的连接。在任何可能的时候,应用程序块处理连接的管理。应用程序块在返回前打开连接并关闭。这减少了需要的客户代码的数量和遗漏而使连接打开的可能性。

ExecuteDataReader 方法的情况中,DataReader 对象使用 CommandBehavior.CloseConnection 方法来执行,这在 DataReader 关闭时会自动关闭连接。

C#
Database db = DatabaseFactory.CreateDatabase();

DbCommand dbCommand = db.GetSqlStringCommand("Select Name, Address From Customers");

using (IDataReader dataReader = db.ExecuteReader(dbCommand))
{
// Process results
}

Visual Basic
Dim db As Database = DatabaseFactory.CreateDatabase()

Dim dbCommand As DbCommand = db.GetSqlStringCommand("Select Name, Address From Customers")

Using dataReader As IDataReader = db.ExecuteReader(dbCommand)

' Process results

End Using

方便的参数处理

应用程序块使编写数据访问代码和处理存储过程的参数更加容易。开发人员可以显式的创建参数或者使用支持参数发现的方法重载。

显式参数创建

Database 类包含有将参数与 DbCommand 对象关联的方法。开发人员必须在代码里传递 DbCommand 对象、参数名称和参数的类型。关于这些方法和代码示例的信息,请参见在用数据访问应用程序块开发应用程序中的“处理参数”。

参数发现

GetStoredProcCommand 的调用在特定存储可能被调用时允许开发人员指定用于做为参数的值。Database 类在如果有参数值时将使用动态参数信息发现。因此,客户代码不需要指定每个参数的类型,就像展示在下列示例中的一样。

C#
Database db = DatabaseFactory.CreateDatabase();

DbCommand dbCommand = db.GetStoredProcCommand("GetProductsByCategory", 2);
DataSet productsDataSet = db.ExecuteDataSet(dbCommand);

Visual Basic
Dim db As Database = DatabaseFactory.CreateDatabase()

Dim dbCommand As DbCommand = db.GetStoredProcCommand("GetProductsByCategory", 2)
Dim productsDataSet As DataSet = db.ExecuteDataSet(dbCommand)

动态发现是为了开发人员的方便,因为他们可以简单的传递值而不用查找信息,如值的名称和类型。(参数的动态发现不由 GenericDatabase 类的支持。)

性能考虑

开发人员必须在编写访问数据的代码时考虑性能,数据访问应用程序块的设计在多个方面对其有所影响:

  • 用于每个数据库类型的特定类已存在。这些类表示了开发人员将要添加的代码。它们在应用程序中没有强行加入附加的代码层。从通用抽象基类 Database 中继承方法,确保与通用接口相一致。通用实现保持在单一的位置,没有牺牲性能。
  • ParameterCache 类提供为每次过程调用保存参数信息的缓存。因为参数的动态发现需要到数据库的一次回返,使用缓存意味着对同样过程的后继调用在获取到参数后不再产生另外的回返。


5.2 - 用于数据库透明应用程序的设计

数据访问应用程序块为支持多种关系数据库提供了一个扩展框架,应用程序可以使用可移植到不同数据库系统的应用程序块。

在 此有许多通用的数据访问工具,如开放数据库连接(ODBC)或者 OLE DB,它们可以支持对各种数据源的访问。这些工具的一个缺点就是如何使用它们依赖于目标数据库。这意味着程序员需要理解访问不同数据库类型的各种程序模 型。迁移应用程序到另一种数据库时将需要大量重新编码。

ODBC 或者 OLE DB 方法的另一个缺点是性能可能降低。通用数据提供程序比那些为特定数据源优化过的方法要慢。数据访问应用程序块提供了具有可移植性和优化的性能的基于工厂的实现。

设计内涵

这些特性隐含了关于应用程序块设计的多个方面:
  • 数据库系统的抽象
  • 专用于 Database 基类的特性的分离

下面的节将描述这些内涵。

数据库系统的抽象

数据访问应用程序块构建在由 ADO.NET 2.0 提供的功能之上以创建数据库透明的提供程序模型。它提供的某些特性如下:
  • 它标准化了参数名称。例如,它支持用于 SQL 参数名称的 “@”。
  • 配置了用于 Oracle 数据库的存储过程包名映射。
  • 使用用于 SQL Server 和 Oracle 的 ADO.NET 静态方法以支持透明的参数发现。
  • 添加了用于由 Oracle 存储过程返回的结果的游标参数。

多数数据访问方法是通过抽象 Database 类来使用的。客户代码可以在它们的代码中引用这些方法而不用管所使用的实际派生自 Database 的对象。例如,下列代码展示了如何使用 ExecuteDataSet 方法。

C#
Database db;
...
db.ExecuteDataSet(dbCommand);

Visual Basic
Dim db As Database
...
db.ExecuteDataSet(dbCommand)

DatabaseFactory 工厂创建了特定的派生 Database 对象。它返回一个 Database 类型的对象,这允许客户代码保持对于返回的实际 database 类型的通用性。例如,下列代码片段创建了默认的 database 对象。

C#
Database db = DatabaseFactory.CreateDatabase();

Visual Basic
Dim db As Database = DatabaseFactory.CreateDatabase()

Database 类的可用方法需要关于执行的命令及任何相关的参数的信息。不同的数据库系统以不同的方式处理命令和参数。派生的 Database 类提供了接受参数信息的方法,特定数据库系统提供了它们自己的处理参数的派生实现。

特定数据库特性的分离

专用于特定数据库系统的功能已加入到适当的 Database 派生类中。例如,ExecuteXmlReader 是一个仅用于 SqlDatabase 类的方法。因为 DatabaseFactory 返回一个 Database 类型的对象,客户代码必须转换到正确的类型才能使用专用于特定数据库的方法。例如,下列的代码创建了一个 SQL Servr 数据库对象,它是可以使用 ExecuteXmlReader 方法的 database 对象。然后,它返回一个 XmlReader 对象。

C#
SqlDatabase dbSQL = (SqlDatabase) DatabaseFactory.CreateDatabase("EntLibQuickStartsSql");
...
XmlReader xmlResults = dbSQL.ExecuteXmlReader(dbCommand);

Visual Basic
Dim dbSQL As SqlDatabase = DirectCast(DatabaseFactory.CreateDatabase("EntLibQuickStartsSql"), SqlDatabase)
...
Dim xmlResults As XmlReader = dbSQL.ExecuteXmlReader(dbCommand)


posted @ 2007-11-25 21:54  Dorian Deng  阅读(912)  评论(0编辑  收藏  举报