配置数据访问层的连接级和命令级设置71
简介
在本系列教程中,我们一直使用强类型DataSet 来实现分层架构中的数据访问层以及业务对象。我们曾在第一篇教程 中讨论过,强类型 DataSet 的DataTable 用作存储数据的仓库,而 TableAdapter 作为包装与数据库通信,以便检索和修改基础数据。TableAdapter 封装了操作数据库的复杂过程,从而使我们不必编写代码就可以连接数据库、发布命令或将结果填充到DataTable 中。
但有时,我们可能需要深入探索 TableAdapter ,并编写代码直接处理ADO.NET 对象。例如,在教程在事务中封装数据库修改 中,我们向 TableAdapter 添加了用于启动、提交和回滚ADO.NET 事务的方法。这些方法使用一个手动创建的内部SqlTransaction 对象,该对象被分配给TableAdapter 的SqlCommand 对象。
在本教程中,我们将讨论如何在 TableAdapter 中访问数据库的连接级和命令级设置。具体来说,我们将向ProductsTableAdapter 添加功能,从而启用对基础连接字符串和命令超时设置的访问。
使用ADO.NET 处理数据
Microsoft .NET Framework 中包含大量用于处理数据的类。这些类位于 System.Data 命名空间 中,称为 ADO.NET 类。ADO.NET 名下的某些类依赖特定的数据提供程序。我们可以将数据提供程序想象成一个通信渠道,该渠道允许信息在ADO.NET 类和基础数据存储之间流动。这些提供程序中既包含OleDb 和 ODBC 等通用提供程序,也包含专为某个特定数据库系统设计的提供程序。例如,尽管使用OleDb 提供程序可以连接Microsoft SQL Server 数据库,但使用 SqlClient 提供程序的效率更高,因为SqlClient 是专为 SQL Server 设计和优化的。
通过编码访问数据时,通常使用以下模式:
- 建立一个数据库连接;
- 发布一条命令;
- 针对 SELECT 查询处理结果记录。
上述每个步骤都由一个单独的 ADO.NET 类来执行。例如,通过SqlClient 提供程序连接数据库时使用 SqlConnection 类 。而向数据库发布 INSERT 、UPDATE 、DELETE 或 SELECT 命令则使用 SqlCommand 类 。
TableAdapter 自动生成的代码已经包含了用于连接数据库、发布命令、检索数据和向DataTable 填充数据的功能。因此,除了在事务中封装数据库修改教程之外,我们不需要自己编写任何底层ADO.NET 代码。但有时,我们也可能需要自定义这些底层设置。在下面的步骤中,我们将探讨如何设置TableAdapter 内部使用的ADO.NET 对象。
步骤 1:检查 Connection 属性
每个TableAdapter 类都有一个Connection 属性,用于指定数据库连接信息。此属性的数据类型及ConnectionString 的值是由在 TableAdapter Configuration 向导中进行的选择确定的。回顾一下,当我们第一次向强类型DataSet 添加TableAdapter 时,此向导要求我们指定数据库源(参见图1 )。本步骤的下拉列表中包含了配置文件中指定的数据库,以及Server Explorer 的 Data Connections 中的其它数据库。如果我们要使用的数据库未包含在下拉列表中,可以通过单击New Connection 按钮并提供所需的连接信息来指定一个新的数据库连接。
图1 :TableAdapter Configuration Wizard 的第一步
让我们花些时间来检查 TableAdapter 的 Connection 属性的代码。我们在创建数据访问层教程中提到过,要查看自动创建的 TableAdapter 代码,我们可以打开Class View 窗口,进入相应的类,然后双击成员名称。
打开View 菜单并选择 Class View (或按下Ctrl+Shift+C ),导航到 Class View 窗口。从Class View 窗口的上半部分深入到 NorthwindTableAdapters 命名空间,从中选择 ProductsTableAdapter 类。这将在 Class View 的下半部分显示 ProductsTableAdapter 的成员,如图 2 所示。双击 Connection 属性来查看它的代码。
图2 :在 Class View 中双击Connection 属性,查看为该属性自动生成的代码
TableAdapter 的 Connection 属性及其它与连接相关的代码如下所示:
private System.Data.SqlClient.SqlConnection _connection;
private void InitConnection() {
this._connection = new System.Data.SqlClient.SqlConnection();
this._connection.ConnectionString =
ConfigurationManager.ConnectionStrings["NORTHWNDConnectionString"].ConnectionString;
}
internal System.Data.SqlClient.SqlConnection Connection {
get {
if ((this._connection == null)) {
this.InitConnection();
}
return this._connection;
}
set {
this._connection = value;
if ((this.Adapter.InsertCommand != null)) {
this.Adapter.InsertCommand.Connection = value;
}
if ((this.Adapter.DeleteCommand != null)) {
this.Adapter.DeleteCommand.Connection = value;
}
if ((this.Adapter.UpdateCommand != null)) {
this.Adapter.UpdateCommand.Connection = value;
}
for (int i = 0; (i < this.CommandCollection.Length); i = (i + 1)) {
if ((this.CommandCollection[i] != null)) {
((System.Data.SqlClient.SqlCommand)
(this.CommandCollection[i])).Connection = value;
}
}
}
}
当对TableAdapter 类进行实例化时,成员变量 _connection 等于空值。访问 Connection 属性时,首先检查_connection 成员变量是否已被实例化。如果没有,则调用InitConnection 方法,将 _connection 实例化并将其ConnectionString 属性设为在 TableAdapter Configuration 向导的第一步中指定的连接字符串值。
Connection 属性也可分配给SqlConnection 对象,这会将新增 SqlConnection 对象与TableAdapter 的每个SqlCommand 对象关联起来。
步骤2:显示连接级设置
连接信息应封装在 TableAdapter 内,不能被应用程序架构的其它层访问。但有时查询、ASP.NET 页面或用户需要访问或自定义TableAdapter 的连接级信息。
我们将扩展Northwind 数据集中的 ProductsTableAdapter ,使其包含一个 ConnectionString 属性,业务逻辑层可使用该属性读取或更改TableAdapter 使用的连接字符串。
注意 :连接字符串用于指定数据库连接信息 ,如使用的提供程序、数据库的位置、验证凭据及其它数据库相关设置。对于不同数据存储和提供程序使用的连接字符串模式列表,请参考 ConnectionStrings.com 。
正如我们在创建数据访问层 教程中讨论的那样,强类型 DataSet 自动生成的类可通过使用部分类进行扩展。首先,在项目中的~/App_Code/DAL 文件夹下新建一个名为 ConnectionAndCommandSettings 的子文件夹。
图3 :添加一个名为 ConnectionAndCommandSettings 的子文件夹
新增一个名为 ProductsTableAdapter.ConnectionAndCommandSettings.cs 的类文件,并输入以下代码:
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
namespace NorthwindTableAdapters
{
public partial class ProductsTableAdapter
{
public string ConnectionString
{
get
{
return this.Connection.ConnectionString;
}
set
{
this.Connection.ConnectionString = value;
}
}
}
}
部分类向ProductsTableAdapter 类添加一个名为 ConnectionString 的 Public 属性,允许任何层读取或更新 TableAdapter 基础连接的连接字符串。
创建(并保存)此部分类后,打开 ProductsBLL 类。进入一个现有的方法并键入 Adapter ,然后按下句点键打开 IntelliSense 。现在IntelliSense 中会显示新的ConnectionString 属性。这意味着,您可以从 BLL 中通过编码读取或调整此值。
显示整个连接对象
部分类只显示基础连接对象的一个属性,即ConnectionString 。如果想突破TableAdapter 的限制从而访问整个连接对象,我们可以选择更改Connection 属性的保护级别。从步骤1 中的自动生成代码可以看出,TableAdapter 的 Connection 属性标记为internal,即它只能被同一个程序集中的类访问。但我们可以通过TableAdapter 的 ConnectionModifier 属性对此进行更改。
打开Northwind 数据集,在设计器中单击 ProductsTableAdatper ,导航到Properties 窗口。从此窗口中可以看到,ConnectionModifier 设为默认值 Assembly 。要想从强类型 DataSet 程序集的外部访问 Connection 属性,应将ConnectionModifier 属性更改为 Public 。
图4 :Connection 属性的访问级别可通过ConnectionModifier 属性配置
保存数据集,然后返回ProductsBLL 类。与前面的操作一样,进入一个现有的方法并键入Adapter ,然后按下句点键打开IntelliSense 。列表中应包含Connection 属性,即现在可以从 BLL 中通过编码读取或指定连接级的设置了。
步骤3:检查命令相关属性
每个TableAdapter 包含一个主查询。主查询默认包含自动生成的INSERT 、UPDATE 和 DELETE 语句。在TableAdapter 代码中,主查询的INSERT 、UPDATE 和DELETE 语句是通过Adapter 属性作为 ADO.NET 数据适配器对象实现的。与 Connection 属性一样,Adapter 属性的数据类型由使用的数据提供程序确定。由于我们的教程使用SqlClient 提供程序,Adapter 属性为SqlDataAdapter 类型。
TableAdapter 的 Adapter 属性使用如下三个SqlCommand 类型的属性来发布 INSERT 、UPDATE 和 DELETE 语句:
- InsertCommand
- UpdateCommand
- DeleteCommand
SqlCommand 对象负责向数据库发送特定的查询,其属性如下: CommandText ,其中包含要执行的 ad-hoc SQL 语句或存储过程;以及 Parameters ,即 SqlParameter 对象集合。正如我们在创建数据访问层教程中讨论的那样,用户可以通过 Properties 窗口自定义这些命令对象。
除了主查询外,TableAdapter 还可以包含多个方法,这些方法在调用时将向数据库发出具体的命令。主查询的命令对象和所有其它方法的命令对象都存储在TableAdapter 的 CommandCollection 属性中。
让我们花些时间来查看一下 Northwind 数据集中的 ProductsTableAdapter 为上述两个属性及其支持成员变量和 helper 方法生成的代码:
private System.Data.SqlClient.SqlDataAdapter _adapter;
private void InitAdapter() {
this._adapter = new System.Data.SqlClient.SqlDataAdapter();
... Code that creates the InsertCommand, UpdateCommand, ...
... and DeleteCommand instances - omitted for brevity ...
}
private System.Data.SqlClient.SqlDataAdapter Adapter {
get {
if ((this._adapter == null)) {
this.InitAdapter();
}
return this._adapter;
}
}
private System.Data.SqlClient.SqlCommand[] _commandCollection;
private void InitCommandCollection() {
this._commandCollection = new System.Data.SqlClient.SqlCommand[9];
... Code that creates the command objects for the main query and the ...
... ProductsTableAdapter�s other eight methods - omitted for brevity ...
}
protected System.Data.SqlClient.SqlCommand[] CommandCollection {
get {
if ((this._commandCollection == null)) {
this.InitCommandCollection();
}
return this._commandCollection;
}
}
Adapter 和CommandCollection 属性的代码与 Connection 属性的代码非常相似。有一些成员变量中包含属性使用的对象。属性的Get 访问器启动时检查相应成员变量是否为空值。如果是,则调用一个初始化方法,创建成员变量的一个实例,并为核心的命令相关属性赋值。
步骤4:显示命令级设置
在理想情况下,命令级信息应封装在数据访问层中。但如果架构中的其它层需要使用这些信息,则可通过部分类来显示,如同显示连接级设置一样。
由于TableAdapter 只有一个Connection 属性,用于显示连接级设置的代码非常简单。但更改命令级设置则较为复杂。因为TableAdapter 可能有多个命令对象(InsertCommand 、UpdateCommand 和 DeleteCommand )以及 CommandCollection 属性包含的数量不定的命令对象。更新命令级设置时,需要将这些设置传送给所有命令对象。
例如,假设 TableAdapter 中某些查询的执行相当耗时,当使用 TableAdapter 执行其中某个查询时,我们可能希望增大命令对象的CommandTimeout 属性 的值。此属性指定该命令执行时的等待时间,默认值为30 秒。
要允许通过 BLL 更改 CommandTimeout 属性,使用步骤 2 中创建的部分类文件 (ProductsTableAdapter.ConnectionAndCommandSettings.cs) 向 ProductsDataTable 添加以下 Public 方法即可 :
public void SetCommandTimeout(int timeout)
{
if (this.Adapter.InsertCommand != null)
this.Adapter.InsertCommand.CommandTimeout = timeout;
if (this.Adapter.DeleteCommand != null)
this.Adapter.DeleteCommand.CommandTimeout = timeout;
if (this.Adapter.UpdateCommand != null)
this.Adapter.UpdateCommand.CommandTimeout = timeout;
for (int i = 0; i < this.CommandCollection.Length; i++)
if (this.CommandCollection[i] != null)
this.CommandCollection[i].CommandTimeout = timeout;
}
此方法可从 BLL 或表示层调用,用于设置 TableAdapter 实例发布的所有命令的超时时间。
注意 :Adapter 和 CommandCollection 属性标记为 Private ,这表示它们只能从 TableAdapter 中的代码访问。与 Connection 属性不同,这些访问修饰符是不可配置的。因此,如果需要向架构中的其它层显示命令级属性,必须使用前面提到的部分类方法提供一个Public 方法或属性,利用该方法或属性对 Private 命令对象执行读取或写入操作。
小结
强类型DataSet 中的 TableAdapter 用于封装数据访问的详细信息和复杂性。通过TableAdapter ,我们无需编写ADO.NET 代码便可连接数据库、发布命令或使用结果填充DataTable 。这些都是自动完成的。
但有时,我们需要自定义底层 ADO.NET 的细节,如更改连接字符串、默认连接或命令超时设置。TableAdapter 自动生成 Connection 、Adapter 和 CommandCollection 属性。但这些属性都默认为 internal 或private 。要显示内部信息,我们可以使用部分类扩展TableAdapter ,使其包含 public 方法或属性即可。TableAdapter 的 Connection 属性访问修饰符也可通过TableAdapter 的ConnectionModifier 属性配置。
快乐编程!