命令的执行

       ADO.NET对象模型提供物两种类型的命令对象:一种是一次性的命令,另一种是数据适配器。一次性命令用于执行SQL命令或存储过程,返回的是一种游标。如果使用游标,那么连接必须处于打开状态。数据适配器是一个功能更强大的对象,内部使用的也是命令和游标。它用于获取数据,并将其加载到容器类中—DataSet或DataTable。客户端应用程序可以在不与数据源连接的情况下处理数据。

SqlCommand类

       SqlCommand代表SQL Server语句或存储过程,派生自DbCommand类,实现了IDbCommand和ICloneable接口。命令在连接上下文中执行,也可使用事务。其构造函数如下:

public SqlCommand();
public SqlCommand(string);
public SqlCommand(string, SqlConnection);
public SqlCommand(string, SqlConnection, SqlTransaction);
       SqlCommand类的属性如下表所示:

 

       SqlCommand类的方法如下表所示:

 

       SqlCommand能以同步或异步方式执行。同步方式方法:ExecuteNonQuery、ExecuteReader、ExecuteScalar、ExecuteXmlReader。

ADO.NET数据读取器

       基于数据读取器的方法在系统开销和性能方面表现都很好。每次只有一条记录缓存,而不必等待整个结果集都加载到内存中。

       SqlDataReader类的属性见下表:

 

       SqlDataReader类的方法见下表:

 

       Close方法关闭读取器对象,注意,关闭读取器不会自动关闭底层的连接。

       数据读取器还配有各种针对DBMS的get方法,如GetSqlDouble、GetSqlMoney等。GetXXX与GetSqlXXX方法的不同在于:GetXXX返回的为.NET Framework类型,而GetSqlXXX方法返回的类型是SQL Server类型的.NET Framework封装(如:SqlDouble、SqlMoney等)。

       数据读取器是从数据源读取数据最有效的方式,但应该尽快读取数据并释放连接。每次只能读取一行,借助Read方法,可遍历整个结果集。

       执行SqlCommand类的ExecuteReader方法时,我们可以选择“命令行为”:

       cmd.ExecuteReader(CommandBehavior.CloseConnection);

       CommandBehavior是一个枚举类型,其成员见下表:

 

       数据读取器和数据库连接都是独立的对象,必须分别管理和关闭。这两个对象都有Close方法,应该分别调用。首先关闭数据读取器,然后关闭数据库连接。指定CloseConnection行为后,在关闭数据读取器的同时也会自动关闭底层连接。

       数据读取器不能直接创建,它的构造函数被标记为内部使用(internal),只有定义在同一程序集的类才可调用它。数据读取器会在ExecuteReader方法被执行时隐式创建。

多结果集的访问

       查询有可能返回多个结果集,默认情况下,数据读取器首先会定位在第一个结果集上。我们可使用Read方法遍历当前结果集的各条记录,当前结果集的遍历完成后,为移动到下一结果集,我们可使用NextResult方法。如果没有可供读取的结果集,该方法会返回false。

异步命令

       ADO.NET 2.0针对SQL Server的.NET数据提供程序提供了异步执行命令支持。我们可以通过三种方式构建异步执行的命令:非阻塞法、轮循法、回调法。

       为启用异步命令,我们需要将连接字符串的Async属性设置为true。异步命令的启用会在某种程度上提高整体性能的开销,为此,最好只对那些需要执行异步操作的连接对象使用Async关键字。

       虽然我们可以在开户异步操作支持的连接上调用同步方法,但那只会消耗更多资源,导致性能下降。如果既要使用同步命令,又要使用异步命令,那么尽量选择不同的连接。

       异步命令并不是通过创建新的线程并阻塞其执行而实现的。若启用异步命令,ADO.NET会以重叠(overlapped)模式打开到数据库的TCP套接字,并将其绑定到I/O完成端口上。这样使同步操作能够模仿异步操作的执行方式。

       使用非阻塞命令是实现异步命令最简单的方式,启动该操作后,可以继续执行其他不相关的方法,在其稍后返回时获取结果。无论选择哪种模型,执行异步命令的第一步都是调用BeginExecuteXXX方法。以读取命令为例:      

//启动异步操作
IAsyncResult iar = cmd.BeginExecuteReader();
//执行其他操作
……
//等待异步操作完成,加上此段代码便成为轮循法
do
{
……
}
while(!iar.IsCompleted)

SqlDataReader reader
= cmd.EndExecuteReader(iar);

//处理结果
ProcessData(reader);

  使用回调函数示例:

//启动异步命令

IAsyncResult ar
= cmd.BeginExecuteReader(new AsyncCallback(ProcessData), cmd);

//回调函数处理

public void ProcessData(IAsyncResult ar)

{

//取出异步上下文环境

SqlCommand cmd
= (SqlCommand)iar.AsyncState;

//等待操作完成

SqlDataReader reader
= cmd.EncExecuteReader(iar);

……

}
作为第二个参数传入BeginExecuteReader中的调用上下文会被存储在IAsyncResult对象的AsyncState属性中。

  回调函数将在线程池线程中被调用。

事务的使用

       ADO.NET中有两种事务类型:本地事务和分布式事务。本地事务只使用一个资源,通常是要连接的数据库。分布式事务涉及多个不同类型的资源,如果整个事务被提交或回滚,需要确保每一步做出的更改也被提交或回滚。分布式事务需要事务进程监视器。

       ADO.NET 1.x中,要通过许多数据库特定的事务对象(如针对SQL Server事务的SqlTransaction)来管理本地事务。

       ADO.NET 2.0和更高版本中,本地事务和分布式事务可以通过System.Transactions命名空间中的类来管理。

ADO.NET 1.x中本地事务的管理

       要启动本地事务,可使用连接类的BeginTransaction方法,我们可以为事务指定名称和隔离级别。该方法映射到SQL Server中的Begin Transaction语句。

       示例代码:      

SqlTransaction tran = conn.BeginTransaction();
SqlCommand cmd1
= new SqlCommand(cmdText1);

cmd1.Connection
= conn;
cmd1.Transaction
= tran;
……
SqlCommand cmd2
= new SqlCommand(cmdText2);
cmd2.Connection
= conn;
cmd2.Transaction
= tran;
……

try
{
cmd1.ExecuteNonQuery();
cmd2.ExecuteNonQuery();
tran.Commit();
}
catch
{
tran.Rollback();
}
finally
{
conn.close();
}
事务的隔离级别用于指定连接的锁定行为,一般的值包括:ReadCommitted(默认)、ReadUncommitted、RepeatableRead和Serializable。各种隔离级别的作用见下表:

 

  我们可通过Commit和Roolback方法来显式终止事务。SqlTransaction支持事务中的“命名保存点”,可用于对部分事务进行回滚。命名保存点利用了SQL Server的特殊语句Save Transaction。

TransactionScope对象

       如果需要操纵不同数据库的事务该怎么做呢?在ADO.NET 1.x中,我们需要Enterprise Services来创建分布式事务。而在ADO.NET 2.0和更高版本中,通过新的TransactionScope对象,即可执行本地事务,也可执行分布式事务。如下所示:

using(TransactionScope ts = new TransactionScope())
{
using(SqlConnection conn = new SqlConnection(connString))
{
SqlCommand cmd
= new SqlCommand(cmdText, conn);
cmd.Connection.Open();
try
{
cmd.ExecuteNonQuery();
}
catch(SqlException ex)
{
lblMessage.Text
= ex.Message;
}
}
ts.Complete();
}

  连接对象定义在事务对象的作用范围内,因而会自动参与到事务中。我们唯一需要做的是提交事务而已。如果没有调用该方法,事务将失败,且无论这个过程中的命令执行何种操作,均进行回滚。显然,任何异常都会中断事务。

  分布式事务示例:      

using(TransactionScope ts = new TransactionScope())
{
using(SqlConnection conn = new SqlConnection(connString1))
{
SqlCommand cmd
= new SqlCommand(cmdText1, conn);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
using(SqlConnection conn = new SqlConnection(connString2))
{
SqlCommand cmd
= new SqlCommand(cmdText2, conn);
cmd.Connection.Open();
cmd.ExecuteNonQuery();
}
ts.Complete();
}
TransactionScope会自动确定需要本地事务还是分布式事务,如果必要,它会登记分布式资源,否则继续进行本地处理。如果代码在某处无法以本地方式运行,TransactionScope会以适当方式将事务提交给DTC。
posted on 2011-04-17 16:18  辛勤的代码工  阅读(990)  评论(0)    收藏  举报