在ASP.NET 2.0中操作数据之七十:配置数据库连接和命令等级设置

导言:

  在本系列我们用类型化的DataSets来构建数据访问层。就像在第一章探讨的那样,类型化DataSets的DataTables用作存储数据的“仓库”,而TableAdapters作为连接数据库的通道,以检索、修改数据.TableAdapters 将处理数据库的很多复杂的细节进行了封装,将我们解脱出来,免去了写代码连接数据库、发出命名、向DataTable填充数据的痛苦.

  不过在某些时候我们需要深入的探究TableAdapter,直接写代码处理ADO.NET对象.在第61章《在事务里对数据库修改进行封装》里我们向TableAdapter添加了多个方法以开启、提交、回滚ADO.NET事务.这些方法都使用内在的、手动创建的SqlTransaction对象来对TableAdapter的SqlCommand对象进行赋值.

  在本文,我们将考察如何访问TableAdapter的“数据库连接”和“数据库命令”级的设置.具体来说,我们将向ProductsTableAdapter添加函数,以访问“连接字符串”(connection string)和“命令过期时间”(command timeout)设置.

用ADO.NET处理数据

  微软.NET Framework包含了很多处理数据的特殊用途的类。这些类用System.Data namespace来进行创建,其中就包括ADO.NET classe类,一些ADO.NET名下的类需要依赖某个特定的data provider才能工作.你可以想象在ADO.NET classes类和某个数据存储(data store)之间,有一个data provider充当连接通道(communication channel)以供传递信息.data provider包括OleDb 、ODBC, 以及其它一些专门设计来连接某种特定数据库系统的data provider.举个例子,我们不能用OleDb来连接一个Microsoft SQL Server数据库.而SqlClient就可以,因为它是经过优化的专门设计来连接SQL Server的.

当编程访问数据时,通常使用下面的模式:

1.新建数据库连接
2.发出命令
3.用SELECT查询来返回记录

  以上3步每步都有单独的ADO.NET classes类来执行.比如连接数据库用SqlConnection class类;要发出INSERT, UPDATE, DELETE,或SELECT命令,用SqlCommand class类.

  除了第61章《在事务里对数据库修改进行封装》外,我们都没有自己写任何ADO.NET代码,因为TableAdapters自动生成的代码包含了一些必要的功能:连接数据库、发出命令、检索数据、填充DataTables.但是有时我们要自己定制这些设置.在接下来的几步我们将探究TableAdapters内部使用的ADO.NET对象.

第一步:考察Connection属性

  每个TableAdapter class类都有一个Connection属性,用于指定数据库连接信息.该属性的数据类型以及ConnectionString的值根据TableAdapter设置向导所做的配置而定.我们还记得,当向类型化的DataSet添加一个TableAdapter时,向导要我们指定数据源(见图1).在下拉列表里列出了web.config文件指定连接的数据库,以及服务器资源管理器的Data Connections里的数据库.如果我们要连接的数据库没有出现在下拉列表里,点“New Connection”按钮,以提供必需的连接信息.


图1:TableAdapter设置向导的第一步

  我们化点时间来查看TableAdapter的Connection属性的代码,就像在第一章《创建一个数据访问层》里探讨的一样,我们可以在Class View窗口里查看自动生成的TableAdapter代码,转到相应的类,再双击成员名(member name)即可.

  打开“View”菜单,选中“Class View”(或按住Ctrl+Shift+C).在Class View窗口的上半部分里,选中NorthwindTableAdapters命名空间,再选中ProductsTableAdapter class类.这将在下半部分显示出ProductsTableAdapter的成员,如图2所示.双击Connection属性以查看代码。


图2:双击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 class类进行“实例化”(instantiated)时,成员变量_connection的值等同为null.当访问Connection属性时,首先检查是否已经对成员变量_connection实例化,如果没有的话就调用InitConnection方法,该方法对_connection进行实例化,然后用TableAdapter设置向导指定的连接字符串对其赋值.

  同样可以用Connection属性对一个SqlConnection对象赋值,这样的话就可以将这个新的SqlConnection对象与TableAdapter的SqlCommand对象联系起来.

第二步:访问“数据库连接”级的设置

  数据库连接信息封装在TableAdapter内部,很难从应用程序的其它层对其进行访问.然而,在某些时候,某人查询、用户或ASP.NET页面需要对TableAdapter的连接信息进行访问或定制.

  让我们对Northwind数据集的ProductsTableAdapter进行扩展,以包含一个ConnectionString属性,以便于在业务逻辑层对TableAdapter用到的连接字符串进行读取和更改.

  注意:一个连接字符串是这样的一个字符串,它指定了数据库连接信息.比如,提供者provider、数据库的位置、身份验证,以及其它与数据库相关的设置.更多详情请参考网站ConnectionStrings.com

  就像在第一章《创建一个数据访问层》里讨论过的一样,类型化的DataSet自动生成的类可以通过使用部分类(partial classes)来进行扩充.首先,在~/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 class类添加了一个public类型的,名为ConnectionString的属性.该属性允许在任何层对TableAdapter用到的连接字符串进行读取和更改.

  当创建并保存该局部类后,打开ProductsBLL class类。打开其中的一个方法,键入Adapter,再输入其范围内的一个关键字以打开智能感知,你应该可以看到这个新添加的的ConnectionString属性出现在智能感知里,这就表明了我们可以在BLL层通过编程来读取或更改其值.

访问整个Connection对象

  该局部类扩展的只是connection对象众多属性中的一个:ConnectionString.如果你想在TableAdapter范围外访问整个connection对象的话,你可以改变Connection属性的保护等级.就像我们在第一步考察的那样,TableAdapter的Connection属性标明为internal,这就是说,只有在同级的类里才可以对其进行访问.不过我们可以通过TableAdapter的 ConnectionModifier属性来改变其访问范围.

  打开Northwind数据集,在ProductsTableAdatper上右键单击,打开属性窗口,你将会看到ConnectionModifier设置为默认的Assembly. 为了在数据集范围以外访问Connection属性,我们将其改为Public.


图4:可以通过ConnectionModifier属性修改Connection属性的访问范围

  保存后在返回到ProductsBLL class类,就向前面一样在某个现有的方法内键入Adapter,再输入其范围内的一个关键字以打开智能感知,你应该也可看到一个Connection属性,这意味着我们可以在业务逻辑层通过编程对连接设置进行读取或赋值操作.

第三步:考察与Command相关的属性

  一个TableAdapter有一个主查询(main query),主查询默认情况下会自动生成INSERT, UPDATE,以及DELETE statements.该主查询的INSERT, UPDATE,DELETE statements在TableAdapter的代码里通过Adapter属性以一个ADO.NET data adapter object(ADO.NET数据适配器对象)的形式来执行.
由于我们的教程使用的SqlClient provider,因此Adapter属性为SqlDataAdapter类型.

TableAdapter的Adapter属性有3个SqlCommand类型的属性,分别用来发出INSERT, UPDATE,DELETE statements:

.InsertCommand
.UpdateCommand
.DeleteCommand

  一个SqlCommand对象负责向数据库发出某个具体的查询,并有相应的属性,比如:CommandText属性包含了要执行的ad-hoc SQL statement或存储过程;Parameters属性是一个SqlParameter对象集。就像我们在第一章《 创建一个数据访问层》探讨的一样,这些command对象可以通过属性窗口进行用户定制.

  除了主查询外,TableAdapter还包含一系列的方法,当调用这些方法时,就向数据库发出具体的命令.主查询的command对象以及其它所有方法使用的command对象都保存在TableAdapter的CommandCollection属性里.

我们花点时间看看Northwind数据集里的ProductsTableAdapter生成的有关这2个属性及其支持的成员变量、方法:

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 ...
 ... ProductsTableAdapters 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模块一开始检测相应的成员变量是否为null,如果是,那么调用一个方法,以创建这个成员变量的一个实例,然后对与command相关的属性进行赋值.

第四步:访问与Command相关的设置

  理想状态下,命令级(command-level)的信息应该封装在数据访问层。当然,我们也可以通过一个部分类来对其进行扩展,就像数据库连接级(connection-level)的设置一样.

  由于TableAdapter只有一个单一的Connection属性,所以那些用于扩展数据库连接级设置的代码是非常直观易懂的.而如果要修改命令级的设置的话要复杂一些,这是因为TableAdapter包含了多个command对象——InsertCommand, UpdateCommand,DeleteCommand, 以及CommandCollection 属性包含的数量不等的command对象.当更新命令级的设置时,这些设置需要告知所有的这些command对象.

  比如,假如在一个TableAdapter里某些查询需要花很长的时间才能执行.当使用该TableAdapter来执行其中的一个查询时,我们可能希望增大command对象的CommandTimeout属性的值.该属性指定了等待命令执行的时间,默认为30秒.

  想要从业务逻辑层来调整CommandTimeout属性的话,可以用我们在第二步里创建的部分类向ProductsDataTable添加一个public类型的方法,如下:(在ProductsTableAdapter.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实例发出的所有命令的command timeout值.

  注意:Adapter 和 CommandCollection属性被标记为private,这就意味着只能在TableAdapter内部对其访问.于Connection属性不同,我们不能改变其访问权限配置.因此,如果你想对其进行扩展以便从体系的其它层对其进行访问的话,像上面讨论的那样,我们必须使用部分类来提供一个public类型的方法或属性,对这些标记为private的命令对象进行读写操作.

结语:

  TableAdapters将数据访问等细节进行的封装,因此我们使用TableAdapters的时候,不用关心手写ADO.NET代码以连接数据库、发出命令、用检索的数据填充DataTable等.因为它已经自动生成了这些代码.

  但是在某些时候,我们需要定制这些ADO.NET细节,比如改变连接字符串或默认的command timeout和connection timeout的值.TableAdapter自动生成了Connection, Adapter,以及CommandCollection属性,但是默认情况下这些属性要么标记为internal要么为private.我们可以对这些内部信息进行扩展,方法就是使用部分类,在部分类里使用标记为public的方法或属性.另外,对TableAdapter的Connection属性,我们可以通过TableAdapter的ConnectionModifier属性来改变其访问权限.

  祝编程快乐!

作者简介

  本系列教程作者 Scott Mitchell,著有六本ASP/ASP.NET方面的书,是4GuysFromRolla.com的创始人,自1998年以来一直应用 微软Web技术。大家可以点击查看全部教程《[翻译]Scott Mitchell 的ASP.NET 2.0数据教程》,希望对大家的学习ASP.NET有所帮助。

(0)

相关推荐

  • 在ASP.NET 2.0中操作数据之七十一:保护连接字符串及其它设置信息

    导言: ASP.NET应用程序的设置信息通常都存储在一个名为Web.config的XML文件里.在教程的前面部分我们已经好几次修改过Web.config文件了.比如在第一章,我们创建名为Northwind的数据集时,数据库连接字符串信息自动的添加到Web.config文件的<connectionStrings>节点.再后来,在第3章里,我们手动更新了Web.config文件,添加了一个<pages>元素,对所有的ASP.NET页面运用DataWebControls主题. 由于Web

  • 在ASP.NET 2.0中操作数据之七十二:调试存储过程

    导言: Visual Studio有很多的调试功能.我们只需要稍稍点点鼠标,敲敲键盘就可以使用断点(breakpoints)来打断某个程序的执行,并查看其状态.除了调试代码以外,Visual Studio也支持对SQL Server里面的存储过程进行调试.就像可以在ASP.NET页面的后台代码类或Business Logic Layer class类里设置断点一样,我们也可以在存储过程里设置断点. 本文我们将考察如何在Visual Studio的Server Explorer里进入存储过程并设置

  • 在ASP.NET 2.0中操作数据之六十九:处理Computed Columns列

    导言: Microsoft SQL Server里有一种computed columns列.这种列的值是通过一个表达式来计算,而表达式引用的是同一张表的其它列的值.打个比方,有一张ServiceLog表,其包含了ServicePerformed, EmployeeID, Rate, Duration等列. 虽然我们可以在一个web页面或其它什么界面里计算每笔服务的费用(也就是 比率 rate乘以时间段duration),不过我们也可以手动向ServiceLog表添加一个 AmountDue列以反

  • C# 动态编译、动态执行、动态调试

    在此基础上我做了一些封装,为使调用更加简单,并增加了对动态代码调试的支持,相同代码只编译一次的支持,代码改动自动重新编译,代码引用文件的自动加载和手工加载等功能. 如上图,我封装的类CSharpProvider很简单,下面说明一下一些公共成员的用法. 公共属性 AssemblyFileName:这个属性指定动态编译后生成的配件名称. CompilerParameters:这个属性指定编译的参数 References:这个属性指定被编译代码中的引用.调用者只要调用References.Add("x

  • 调试ASP.NET2005/2008时,端口不正确的解决三套方案

    在VS2008里点"调试"后.ASP.NET Development Server的端口比调试时打开的浏览器上的端口要大2,造成每次需要调试都要手动改浏览器的端口号.如下图: 解决方法一:为 ASP.NET Development Server 指定端口 1.在解决方案资源管理器中,单击应用程序的名称. 2.在"属性"窗格中,单击"使用动态端口"旁的下箭头,然后从下拉列表选择"False".    这会允许对"端口号

  • Asp.net调试的一些问题小结

    昨天到今天搞了一整天,把人都要差点搞崩了! ,以后再也不能乱关机了,因为这次的大意,几乎所有的Net安装,调试问题都被我碰到了,还好,我一个个把他记录下来了,同时,在CSDN找了一些相关的解决方法,不敢独享,供大家相互学习交流之用. 起因是这样的,机子运行IE时假死,我用着不爽,就二话不说热启动,结果就出事了. 重新打开正在Vs2003.Net里面正在作的项目时,老半天没反应,机子变得异常慢,当然,出于职业的敏感,马上来了个Ctrl+Alt+Del,这下不好,CPU占用100%,一直高居不下,运

  • C#中一些你可能没用过的调试窗口的方法

    首先说明:如果没有进入调试模式的话,默认的调试窗口如下: 开始前的准备: 新建控制台程序DebugWindowDemo: 修改Program.cs 的代码为: 复制代码 代码如下: using System;using System.Threading;using System.Threading.Tasks;using System.Diagnostics; class S    {       public static void Main()        {            pco

  • 在ASP.NET 2.0中操作数据之六十八:为DataTable添加额外的列

    导言: 当向类型化的数据集(Typed DataSet)添加一个TableAdapter时,相应的DataTable的构架已经由TableAdapter的主查询定义好了.比如,如果主查询返回A, B,C这3个域,那么 DataTable将有对应的3个列A, B,和C.除了主查询以外,TableAdapter还可以包含其他的查询,可能是返回基于某些参数的数据.比如,ProductsTableAdapter的主查询返回所有产品的信息,此外,ProductsTableAdapter还包含诸如GetPr

  • ASP.NET笔记之页面跳转、调试、form表单、viewstate、cookie的使用说明

    1.页面跳转: (1 服务器传输 server.Transer(http://www.jb51.net);在定向到新页面后,还是会显示原来的URL,浏览器返回不会退回到原页面,历史记录也不会记录. 应用于完整的控制传输,例如安装向导. (2 超链接 (3 浏览器重定向 Redirect ,速度快,没有发送到服务器 (4 跨页发送 PostBackUrl="http://www.jb51.net" /> IsCrossPagePostBack用来判断是否为跨页提交 IsPostBa

  • asp.net程序编译调试时偶尔出现访问被拒绝的错误的解决方法

    问题描述: 编写asp.net程序,当编译调试比较频繁的时候,很容易经常地出现访问被拒绝.形如: 分析器错误信息: 访问被拒绝:"Microsoft.Web.UI.WebControls".源错误: 行 197: <add assembly="System.Web.Mobile, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>行 198: <add as

  • 调试ASP.NET应用程序的方法和技巧

    以前使用ASP开发过Web应用程序的人员肯定知道调试Web应用程序是多么麻烦.在ASP中,调试是痛苦的,通常包括了使用Response.Write()方法输出变量的值.那么请问一下自己:你有多少次忘记了在布署应用程序之前删除了调试语句? 随着.NET框架组件的出现这种情况发生了彻底的改变.在.NET中,你可以使用Visual Studio .NET中的调试程序跟踪整个Web应用程序的执行,或者使用System.Web.TraceContext名字空间中的Trace类.本文演示如何使用Trace类

随机推荐