配置数据访问层的连接和命令级别的设置 (C#)

作者 :斯科特·米切尔

下载 PDF

类型化 DataSet 中的 TableAdapters 会自动负责连接到数据库、执行命令,并用结果填充 DataTable。 但是,有时我们想要自行处理这些详细信息,本教程介绍如何访问 TableAdapter 中的数据库连接和命令级设置。

介绍

在整个教程系列中,我们使用了类型化数据集来实现分层体系结构的数据访问层和业务对象。 如 第一个教程中所述,类型化数据集的 DataTable 充当数据的存储库,而 TableAdapters 充当包装器来与数据库通信以检索和修改基础数据。 TableAdapters 封装了处理数据库所涉及的复杂性,从而省却了我们编写代码来连接数据库、执行命令或将结果填充到 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 提供程序的效率要高得多,因为它专为 SQL Server 设计和优化。

以编程方式访问数据时,通常使用以下模式:

  • 建立与数据库的连接。
  • 发出命令。
  • 对于 SELECT 查询,处理生成的记录。

有单独的 ADO.NET 类用于执行上述每个步骤。 例如,若要使用 SqlClient 提供程序连接到数据库,请使用SqlConnection。 若要向数据库发出INSERTDELETEUPDATESELECT命令,请使用SqlCommand

除了事务教程 中的包装数据库修改 之外,我们不必自行编写任何低级别 ADO.NET 代码,因为 TableAdapters 自动生成的代码包括连接到数据库、发出命令、检索数据并将这些数据填充到 DataTable 中所需的功能。 但是,有时可能需要自定义这些低级别设置。 在接下来的几个步骤中,我们将了解如何利用 TableAdapters 内部使用的 ADO.NET 对象。

步骤 1:使用连接属性进行检查

每个 TableAdapter 类都有一个 Connection 指定数据库连接信息的属性。 此属性的数据类型和 ConnectionString 值由 TableAdapter 配置向导中所做的选择确定。 回想一下,当我们第一次向类型化数据集添加 TableAdapter 时,此向导会要求我们获取数据库源(请参阅图 1)。 第一步中的下拉列表包括配置文件中指定的数据库以及服务器资源管理器数据连接中的其他任何数据库。 如果要使用的数据库不存在于下拉列表中,可以通过单击“新建连接”按钮并提供所需的连接信息来指定新的数据库连接。

TableAdapter 配置向导的第一步

图 1:TableAdapter 配置向导的第一步(单击以查看全尺寸图像

让我们花点时间检查 TableAdapter 属性的代码 Connection 。 如 “创建数据访问层” 教程中所述,可以通过转到“类视图”窗口,向下钻取到相应的类,然后双击成员名称来查看自动生成的 TableAdapter 代码。

转到“视图”菜单并选择“类视图”(或键入 Ctrl+Shift+C),导航到“类视图”窗口。 从“类视图”窗口的上半部分向下钻取到 NorthwindTableAdapters 命名空间并选择该 ProductsTableAdapter 类。 这将在类视图的下半部分显示 ProductsTableAdapter s 成员,如图 2 所示。 双击该 Connection 属性以查看其代码。

Double-Click 类视图中的连接属性以查看其自动生成的代码

图 2:Double-Click 类视图中的连接属性以查看其自动生成的代码

TableAdapter s 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 等于 null。 当访问Connection属性时,首先会检查_connection成员变量是否已实例化。 如果没有, 则调用 InitConnection 方法,实例化 _connection 并设置其 ConnectionString 属性为从 TableAdapter 配置向导的第一步中指定的连接字符串值。

还可以将 Connection 属性分配给对象 SqlConnection 。 这样做会将新 SqlConnection 对象与每个 TableAdapter 对象 SqlCommand 相关联。

步骤 2:公开 Connection-Level 设置

连接信息应保留在 TableAdapter 中,并且应用程序体系结构中的其他层不可访问。 但是,在某些情况下,可能需要访问或自定义 TableAdapter 的连接级别信息,以便查询、用户或 ASP.NET 页。

让我们扩展 ProductsTableAdapter 数据集,使其包括一个 ConnectionString 属性,业务逻辑层可以使用该属性来读取或更改 TableAdapter 使用的连接字符串。

注释

连接字符串是一个字符串,用于指定数据库连接信息,例如要使用的提供程序、数据库的位置、身份验证凭据和其他与数据库相关的设置。 有关各种数据存储和提供程序使用的连接字符串模式的列表,请参阅 ConnectionStrings.com

“创建数据访问层” 教程中所述,可以通过使用分部类扩展类型化数据集自动生成的类。 首先,在~/App_Code/DAL文件夹下创建一个名为ConnectionAndCommandSettings的新子文件夹。

添加名为 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类添加一个名为public的属性,该属性允许任何层读取或更新 TableAdapter 的底层连接的连接字符串。

创建并保存此分部类后,打开该 ProductsBLL 类。 转到现有方法之一并键入 Adapter ,然后按句点键打开 IntelliSense。 你应该会看到 IntelliSense 中可用的新 ConnectionString 属性,这意味着你可以以编程方式读取或调整 BLL 中的此值。

公开整个连接对象

此分部类仅公开基础连接对象的一个属性: ConnectionString 如果要使整个连接对象在 TableAdapter 的限制之外可用,或者可以更改 Connection 属性的保护级别。 我们在步骤 1 中检查的自动生成的代码显示 TableAdapter 的属性 Connection 被标记为 internal“,这意味着它只能由同一程序集中的类访问。 但是,可以通过 TableAdapter s ConnectionModifier 属性更改此值。

打开Northwind数据集,在ProductsTableAdapter设计器中单击“属性”窗口,然后导航到属性窗口。 在那里,你将看到ConnectionModifier被设置为其默认值Assembly。 若要使 Connection 属性在 Typed DataSet 程序集之外可用,请将 ConnectionModifier 属性更改为 Public

可以通过 ConnectionModifier 属性配置连接属性的可访问性级别

图 4Connection 属性的辅助功能级别可通过 ConnectionModifier 属性进行配置(单击以查看全尺寸图像

保存 DataSet,然后返回到 ProductsBLL 类。 和之前一样,转到现有方法之一并键入 Adapter ,然后按句点键打开 IntelliSense。 该列表应包含一个 Connection 属性,这意味着现在可以以编程方式读取或分配 BLL 中的任何连接级别设置。

TableAdapter 由默认自动生成INSERTUPDATEDELETE语句的主查询组成。 主要查询INSERTUPDATEDELETE 语句通过 Adapter 属性在 TableAdapter 代码中作为 ADO.NET 数据适配器对象实现。 与属性 Connection 一样, Adapter 属性的数据类型由所使用的数据提供程序确定。 由于这些教程使用 SqlClient 提供程序,因此该 Adapter 属性的类型 SqlDataAdapter为 。

TableAdapter的Adapter属性具有三个类型为SqlCommand的属性,用于发出INSERTUPDATEDELETE语句。

  • InsertCommand
  • UpdateCommand
  • DeleteCommand

对象 SqlCommand 负责向数据库发送特定查询,并具有如下属性: CommandText,其中包含要执行的即席 SQL 语句或存储过程; Parameters以及,它是对象的集合 SqlParameter 。 正如我们在 “创建数据访问层” 教程中看到的那样,可以通过“属性”窗口自定义这些命令对象。

除了主查询之外,TableAdapter 还可以包含一个可变数目的方法,在调用时,将指定的命令调度到数据库。 主查询的命令对象和所有其他方法的命令对象存储在 TableAdapter s CommandCollection 属性中。

让我们花些时间看看在ProductsTableAdapter数据集中由Northwind为这两个属性及其支持的成员变量和辅助方法生成的代码:

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

AdapterCommandCollection属性的代码与Connection属性的代码非常相似。 有一些成员变量保存属性使用的对象。 属性 get 访问器首先检查相应的成员变量是否为 null。 如果是这样,将调用初始化方法,该方法将创建成员变量的实例并分配与核心命令相关的属性。

步骤 4:公开 Command-Level 设置

理想情况下,命令级信息应保留在数据访问层中。 但是,如果体系结构的其他层需要此信息,可以通过分部类公开此信息,就像使用连接级设置一样。

由于 TableAdapter 只有一 Connection 个属性,因此用于公开连接级别设置的代码非常简单。 修改命令级设置时,情况稍微复杂一些,因为 TableAdapter 可以具有多个命令对象-一个 InsertCommand命令对象, UpdateCommand以及 DeleteCommand属性中的 CommandCollection 命令对象数量可变。 更新命令级设置时,需要将这些设置传播到所有命令对象。

例如,假设 TableAdapter 中有一些查询需要很长时间才能执行。 使用 TableAdapter 执行其中一个查询时,我们可能需要增加命令对象的 CommandTimeout 属性。 此属性指定等待命令执行的秒数,默认值为 30。

为了让 BLL 能够调整 CommandTimeout 属性,请将以下 public 方法添加到步骤 2 中创建的分部类文件 ProductsDataTableProductsTableAdapter.ConnectionAndCommandSettings.cs)中:

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 实例的所有命令问题设置命令超时。

注释

AdapterCommandCollection 属性被标记为 private,这意味着它们只能从 TableAdapter 中的代码访问。 与属性 Connection 不同,这些访问修饰符不可配置。 因此,如果需要向体系结构中的其他层公开命令级属性,则必须使用上述分部类方法提供 public 读取或写入 private 命令对象的方法或属性。

概要

Typed DataSet 中的 TableAdapters 用于封装数据访问的细节和复杂性。 使用 TableAdapters 时,我们不必担心编写 ADO.NET 代码以连接到数据库、发出命令或将结果填充到 DataTable 中。 这一切都会自动处理。

但是,有时可能需要自定义低级别 ADO.NET 细节,例如更改连接字符串或默认连接或命令超时值。 TableAdapter 自动生成了ConnectionAdapterCommandCollection属性,但这些属性默认是internalprivate。 可以通过使用分部类扩展 TableAdapter 以包括 public 方法或属性来公开此内部信息。 或者,可以通过 TableAdapter 的 ConnectionModifier 属性来配置 TableAdapter 的 Connection 属性访问修饰符。

快乐编程!

关于作者

斯科特·米切尔,七本 ASP/ASP.NET 书籍的作者和 4GuysFromRolla.com 的创始人,自1998年以来一直在与Microsoft Web 技术合作。 斯科特担任独立顾问、教练和作家。 他的最新书是 《Sams Teach Yourself ASP.NET 2.0 in 24 Hours》。 可以通过 mitchell@4GuysFromRolla.com 联系到他。

特别致谢

本教程系列由许多有用的审阅者审阅。 本教程的主要审阅者是伯纳黛特·利伊、瑟伦·雅各布·劳里森、特蕾莎·墨菲和希尔顿·吉塞诺。 有兴趣查看即将发布的 MSDN 文章? 如果是这样,请给我写信。mitchell@4GuysFromRolla.com