异步命令
有时,您希望在客户端或中间件代码中同时完成一件以上的工作,在本来就是多线程的中间件代码中,这是提高吞吐量的关键因素。在ADO.NET 2.0中,SqlClient目前支持异步命令执行。异步操作的.NET范型除了为同步操作提供一个方法以外,还能够为操作提供一组Begin和End方法。因为数据库命令的执行可能需要很长时间,所以SqlClient现在提供了内置的SqlCommand方法以便提供异步执行。下表列出了支持异步执行的方法和它们的同步对应方法。
|
同步方法
|
异步方法
|
|
ExecuteNonQuery
|
BeginExecuteNonQuery
EndExecuteNonQuery
|
|
ExecuteReader
|
BeginExecuteReader
EndExecuteReader
|
|
ExecuteXmlReader
|
BeginExecuteXmlReader
EndExecuteXmlReader
|
尽管异步执行可能是很好的功能,但不应当无故使用它,只有当您知道命令可能会运行很长时间并且您同时要完成某些有用的任务时才应该使用它。Windows NT系列操作系统中的Windows线程调度程序(该功能在Windows 9x和Me客户端上不可用)可花费它自己的开销以便在线程之间切换。同时请记住,某些.NET库是线程敏感型的;使用异步时,用于启动操作的线程未必是完成该操作的同一线程。但是,SQL Server网络库堆栈已经进行了增强,以便通过I/O完成端口来支持异步,并且这为异步SQL Server操作提供了更好的吞吐量。异步操作不但可以对多个操作语句和存储过程执行有效,而且在将其与SQL Server 2005中的多个活动结果集功能结合使用时,您还可以使用单个数据库连接多路复用异步SELECT语句。
批量导入
很多数据库应用程序可以快速、大批量地将行“插入”到SQL Server中。这方面的典型示例是一个将行插入SQL Server的应用程序,这些行对应于从硬件设备(例如电话交换机或医院患者监视器)进行的读取操作。尽管SQL Server随附了实用工具(例如bcp)以适应这种情况,但这些实用工具通常使用文件作为它们的输入。SqlClient包含一个名为SqlBulkCopy的新类。该类的目的不是像BCP那样直接消耗来自文件的输入并产生文件输出,而是为了适应快速而有效地将很多行从客户端插入到数据库中这一情况。SqlBulkCopy可以从数据读取器和数据集获得它的输入。这意味着,您不仅可以直接从提供程序流式传输一系列行(DataReader),还可以用从硬件设备(不是提供程序)获得的外部数据填充数据集以及直接更新数据集;在这种情况下,不需要任何提供程序作为源。
// Fill up a DataSet
DataSet ds = new DataSet();
FillDataSetFromHardwareDevice(ds);
// Copy the Data to SqlServer
string connect_string = GetConnectStringFromConfigFile();
SqlBulkCopy bcp = new SqlBulkCopy(connect_string);
bcp.DestinationTableName = "hardware_readings";
bcp.WriteToServer(ds);
提供程序统计信息
某些应用程序编写者发现在他们的应用程序中进行“实时”监视很有用。尽管您可以使用Windows性能监视器定义自己的性能类,以及使用内部的(并且可能会随着时间的推移而变得脆弱)SQL Server元数据调用来获得该信息,但SqlClient现在具有一种能够为您提供该信息的内置方式。通过SqlConnection类上的一个实例方法,您可以获得每个连接的统计信息(它们类似于ODBC API中提供的信息)。因为存储和收集这些统计信息需要花费自己的开销,所以有一个可以用来切换统计信息收集的属性。还有一个用于重置计数器的方法。当然,默认情况下会关闭统计信息收集;而且,当您通过在池方案中调用Dispose或Close将连接返回到连接池时会启动该功能。以下是产生的统计信息的示例:
string connect_string = GetConnectStringFromConfigFile();
SqlConnection conn = new SqlConnection(connect_string);
conn.Open();
// Enable
conn.StatisticsEnabled = true;
// do some operations
//
SqlCommand cmd = new SqlCommand("select * from authors", conn);
SqlDataReader rdr = cmd.ExecuteReader();
Hashtable stats = (Hashtable)conn.RetrieveStatistics();
// process stats
IDictionaryEnumerator e = stats.GetEnumerator();
while (e.MoveNext())
Console.WriteLine("{0} : {1}", e.Key, e.Value);
conn.ResetStatistics();
Connection-specific statistics
BuffersReceived : 1
BuffersSent : 1
BytesReceived : 220
BytesSent : 72
ConnectionTime : 149
CursorFetchCount : 0
CursorFetchTime : 0
CursorOpens : 0
CursorUsed : 0
ExecutionTime : 138
IduCount : 0
IduRows : 0
NetworkServerTime : 79
PreparedExecs : 0
Prepares : 0
SelectCount : 0
SelectRows : 0
ServerRoundtrips : 1
SumResultSets : 0
Transactions : 0
UnpreparedExecs : 1
有关这些统计信息所代表的确切含义的详细信息,请参阅ADO.NET 2.0或ODBC文档。
AttachDbFileName
SqlClient数据提供程序支持桌面应用程序(其中,数据库存储在用户的桌面上)以及客户端-服务器应用程序和基于中间件的应用程序。有一种特殊版本的SQL Server,称为MSDE;该产品的SQL Server 2005时代的名称是SQL Server 2005 Express Edition。在桌面应用程序中,数据库本身就特定于应用程序并且与应用程序捆绑在一起。用户甚至可能不知道SQL Server正在被用作数据储备库,因为应用程序安装程序将运行SQL Server Express安装。为了便于将数据库文件附加到应用程序内部的SQL Server Express实例,ADO.NET 1.0提供了一个连接字符串参数—AttachDbFileName。但是,必须将该参数指定为硬编码的路径名,以便使用户难以在默认位置以外的其他位置安装该应用程序。在ADO.NET 2.0中,AttachDbFileName参数可以是一个相对路径,并且与应用程序配置设置一起使用。这使得为SQL Server Express设置桌面应用程序就像连接到基于Microsoft Access文件的数据存储一样容易。
SqlClient中特定于SQL Server 2005的功能
MARS
当您使用SQL SELECT语句(作为独立语句或者在存储过程内部)选择一个行集时,SQL Server不会像某些数据库那样自动在该行集上产生游标。相反,它使用优化的方法在网络中流式传输结果集,并且有时会在网络库以数据包大小的块拉入数据时直接从数据库缓冲区中读取。这在SQL Server联机丛书中称为“SQL Sever的默认结果集”或“无游标结果集”。在SQL Server 2005之前的SQL Server版本中,在单个连接上一次只能有一个活动的无游标结果集。不同的数据库API和库将以不同的方式处理这个连接/无游标结果集行为。如果您试图打开第二个无游标结果集,则ADO.NET 1.0和1.1会产生错误;ADO“传统型”实际上会在幕后打开一个新的数据库连接。打开新的数据库连接更为方便,尽管它不如产生错误那样“精确”;这个方便的功能已经被某些程序员在无意中滥用,并且导致了比他们预料的更多的数据库连接。在SQL Server 2005中,数据库已经进行了增强,以允许多个无游标结果集同时在单个连接上处于活动状态。这就产生了功能缩写词“MARS”(多个活动结果集)。网络库进行了一些更改以支持该行为,并且新的网络库和新的数据库都需要启用MARS。
在SqlClient代码中,您可以通过让多个SqlCommand实例使用同一个连接来多路复用结果集。每个SqlCommand都可以容纳一个通过调用Command.ExecuteReader产生的SqlDataReader,并且多个SqlDataReader可以串连使用。在ADO.NET 1.0和1.1中,您必须先关闭一个SqlDataReader才能获得另一个—即使使用了多个SqlCommand。请注意,您无法多路复用从同一SqlCommand实例的多个ExecuteReader调用中产生的SqlDataReader。下面是一个简短(但在功能上不是非常有用)的示例:
// connection strings should not be hardcoded
string connstr = GetConnStringFromConfigFile();
SqlConnection conn = new SqlConnection(connstr);
SqlCommand cmd1 = new SqlCommand(
"select * from employees", conn)
SqlCommand cmd2 = new SqlCommand(
"select * from jobs", conn)
SqlDataReader rdr1 = cmd1.ExecuteReader();
// next statement causes an error prior to SQL Server 2005
SqlDataReader rdr2 = cmd2.ExecuteReader();
// now you can reader from rdr1 and rdr2 at the same time.
该功能不只是与减少错误有关,也不只是为了阐明曾经的ADO库魅力,它与上述的异步操作结合使用时,可能极为有用。多个异步SELECT语句或存储过程调用可以串连执行,从而节省了数据库连接并优化了吞吐量。请想像一下使用单个连接在窗体上同时填充20个下拉列表框。您还可以在结果集处于活动状态时执行无结果集返回语句。尽管同时可以有多个执行流处于活动状态,但所有执行流都必须共享同一个事务(如果事务存在的话)。事务仍然属于连接范围而不是命令范围。您可以通过像在以前版本的ADO.NET中那样设置SqlCommand Transaction属性来将SqlTransaction实例与SqlCommand相关联。
SqlDependency和SqlNotificationRequest
在中间层缓存情况下,如果能够基于他人已经在数据库中更改某行的事实来刷新缓存则极为有帮助。程序员已经借助于一些不同的技术来实现该目标,例如,编写在表或视图更改时更新文件的触发器,或者无论数据库更改与否都经常刷新缓存。在SqlClient SqlNotificationRequest类和SqlDependency类出现之前,没有针对数据库通知进行注册的简单方式。SqlDependency是一个高级别类,它包装了SqlNotificationRequest并且将通知信息呈现为.NET事件。对于SqlNotificationRequest而言没有事件,您必须自己完成注册通知以及获得信息的“繁重工作”。绝大多数程序员都将使用SqlDependency。SqlDependency可以用作独立的类,并且在使用ASP.NET Cache类时可以直接使用它的功能。这一特定于SQL Server 2005的功能取决于SQL Server Service Broker—它是一项实现可伸缩队列系统的新功能。请注意,在使用ASP.NET Cache类时,应使用数据库轮询而不是Service Broker来获得类似的功能。在使用Service Broker和SQL Server 2005时,您不必为了获得通知而维护到数据库的连接。SqlDependency使用您选择的HTTP或TCP协议,并且在基础行更改时与您联系。该通知不包含任何特定于行的信息,当您获得通知时必须重新获取整个行集并重新注册该通知。该功能正是您对于单个缓存或有限的用户组所需要的,但是当您对同时侦听的大量用户使用它时,请务必小心。当任一行更改时,每个用户都必须刷新缓存中的整个行集。对于大量更改和大量用户而言,用于刷新的SELECT语句可能会对数据库产生重大影响。
SQL Server 2005提供了一种机制,以便使用与其他集成了登录的密码策略遵循同一过期机制的SQL登录(连接到SQL Server的Windows登录),该功能要求SQL Server 2005运行于Windows Server 2003上。如果SQL登录密码(例如“sa”)即将过期,则您将无法使用传统的Windows机制和密码更改API来更改它。您只能使用最终调用Transact SQL ALTER LOGIN的SQL Server客户端来更改该密码。SqlClient通过SqlConnection类上的ChangePassword方法适应这一机制。请注意,该方法只有在针对SQL Server 2005实例执行时才可用;尽管您可以在较旧版本的数据库上更改SQL登录,但该API使用的是其他SQL Server版本都不支持的网络数据包类型。密码更改的另一个需要考虑的方面是,在程序的连接字符串中不再对SQL Server登录ID和密码进行硬编码。除非您将生成.NET中间语言并在每次密码更改时都替换可执行文件(这不是一个可行的选择),否则必须在配置文件中存储您的SQL Server密码。严谨的SQL Server开发人员已经在相当长的一段时间内使用配置文件来存储(很可能是经过加密的)密码。更好的做法是始终使用SQL Server集成安全性(如果可能)。
ADO.NET 2.0中的SqlClient提供程序与新的System.Transactions命名空间相集成,从而启用了称为可升级事务的行为。尽管Transact SQL可以用来启动本地或分布式事务(BEGIN TRANSACTION和BEGIN DISTRIBUTED TRANSACTION),但在某些情况下(特别是在客户端/中间层编程方面),程序员可能希望编写可以在一个数据库方案或多个数据库方案中使用的组件。这些方案可能包含多个SQL Server实例,并且SQL Server可以自动检测到多实例访问并将事务从本地“提升”到多实例(分布式)。即使在使用多个数据库产品或多个连接的情况下,这也是可能的,前提是第一个数据库(在分布式事务术语中称为资源管理器)是SQL Server。在ADO.NET中,默认情况下启用可升级的事务。
SQL Server 2005通过数据库镜像支持“热备用(hot spare)”功能。如果SQL Server实例失败,工作可以自动转移到备份服务器。这需要一个称为(并不意外)“目击实例”的实例来见证故障转移。热备用方案还要求现有的客户端连接必须“知道”故障转移(建立与新服务器实例的连接)。在下一次尝试访问时产生错误并且必须通过客户端编程手动进行“故障转移”的客户端连接是次最优的方案。ADO.NET 2.0中的SqlClient支持客户端故障转移,而不必对应用程序进行专门编程。
SQL Server 2005通过两个方法支持事务隔离:锁定和版本控制。以前版本的SQL Server支持锁定,但不支持版本控制。SQL Server 2005支持两种类型的版本控制,它们被称为语句级别版本控制和事务级别版本控制。该功能旨在有选择地减少极端环境中的锁定,并简化为版本控制数据库而设计的应用程序的转换。为版本控制数据库设计的应用程序在移植到锁定数据库时通常需要进行重大更改,反之亦然。版本控制数据库的默认行为几乎总是语句级别版本控制。有关差异的详细信息,请参阅Beauchemin、Berglund和Sullivan撰写的 A First Look at SQL Server 2005 for Developers(http://shopping.msn.com/search/detail.aspx?pcId=4644&prodId=2190289&ptnrid=141&ptnrdata=0)。
不同的版本控制和锁定行为等同于使用特定的事务隔离级别启动事务。ANSI SQL规范定义了四个事务隔离级别:
· READ UNCOMMITED.
· READ COMMITTED.
· REPEATABLE READ.
· SERIALIZABLE.
SQL Server支持所有四个隔离级别,甚至在SQL Server 2005之前就是如此。版本控制数据库通常仅支持READ COMMITTED和SERIALIZABLE。在版本控制数据库中,READ COMMITTED实现语句级别版本控制,而SERIALIZABLE实现事务级别版本控制。READ COMMITTED是几乎所有数据库的默认行为,无论是使用锁定还是使用版本控制。通过以数据库为单位设置数据库选项,可以启用语句级别版本控制,并且使其成为默认行为。在启用语句版本控制时,指定IsolationLevel.ReadCommitted或IsolationLevel.ReadUncommitted会使用该行为。为了支持事务级别隔离,SQL Server 2005定义了一个新的隔离级别—IsolationLevel.Snapshot,SqlClient(并且只有SqlClient)支持该隔离级别。该隔离级别是必需的,因为您可以分别启用语句级别或事务级别的版本控制,并且IsolationLevel.Serializable已经由SQL Server使用以便与锁定行为相对应。
数据类型——UDT、XML数据类型以及“MAX”BLOB和CLOB
SQL Server 2005添加了对用户定义类型的支持,一个原生XML数据类型和更好的大型数据支持。它通过使用Transact SQL类型VARCHAR(MAX)、NVARCHAR(MAX)和VARBINARY(MAX)改进了大型数据支持。用户定义的类型和原生XML类型由SQL:1999和SQL:2003规范定义。为了将这些数据类型与SqlClient一起使用,定义了System.Data.SqlTypes命名空间中的新类(SqlUdt和SqlXml),将支持添加到SqlDbTypes枚举,并且增强了IDataReader.GetValue以支持将UDT作为.NET对象类型返回以及将XML作为.NET字符串返回。这些新的SQL Server 2005类型在SQL SELECT语句所返回的DataReader中受到支持,并且作为使用SqlParameter的参数。特殊的SqlMetaData类可以返回有关这些新数据类型的扩展属性的信息(例如某个强类型XML列所依附的XML架构集合)或某个UDT的数据库名称。您可以直接从客户端在通用代码中以及在数据集中使用这些类型。最后,您可以从客户端对“MAX”数据类型执行部分更新,这要求使用ADO.NET 2.0之前的特殊SQL函数。
|