前言
这篇文章起源于在公司写的一个PPT但是由于PPT本身的限制很多内容无法表达或是详细的解释于是变下定了决心写篇文档!
在这篇文章里我将尽量简单的描述下ADONET 的新特性尤其是配合SQL Server 所展现出来的强大实力如果想进一步了解ADONET 编程方面的话可以去阅读Glenn Johnson的ADONET 高级编程[微软推荐丛书] 定价:元网络购书的话打了折只要块就可以了
一功能强大的ADO
年底(年月)与 SQL Server 一起出现的是 NET Framework 版本其中用来访问数据库的 ADONET类也升级到 ADONET 版
ADONET 除了增强旧功能外也提供了相当多的新功能包含了以基础类为本(baseclassbased)的数据源提供程序(provider)模型异步访问架构批处理更新与大量数据复制(bulk copy)SQL Server 的回调通知单一连接同时多执行结果集(MARS)执行统计强化的 DataSet 类等等换句话说若要有效发挥 SQL Server 的功能前端应用程序最好用 ADONET 来开发
ADONET 提供了相当多的新增功能一些与数据源提供程序无关也就是访问各种数据库都可以用到的功能但有很大的一部分是专属于 SQL Server 针对 SQL Server 的新功能提供给前端应用程序开发使用
二: 使用多数据结果集(仅限)
在之前版本的 SQL Server 同一时间一条连接只能传递一个 SELECT 语法执行后返回的结果集如果想在一次连接后返回多个查询内容只能使用类似如下的方法来实现
SqlDataAdapter myDataAdapter = new SqlDataAdapter(StoredProcedureNamemyConnection);
myDataAdapterSelectCommandCommandType = CommandTypeStoredProcedure;
myDataAdapterSelectCommandParametersAdd(@sqlstrsqlstr);
DataSet ds = new DataSet();
myDataAdapterFill(ds);
return ds;
dsTables[]dsTables[]dsTables[]分别对应三个结果集
SQL Server 提供了在同一条连接上可以同时传递多个没有游标结构(cursorless)的结果集(也称为默认结果集)此功能称为 Multiple Active Resultsets(MARS)如此可以节省需要同时打开的连接数但要注意的是连接字符串设置要加上 MultipleActiveResultSets=true 属性否则默认不启动多数据结果集的功能
string connstr = server=(local);database=northwind;integrated security=true; ;
SqlConnection conn = new SqlConnection(connstr);
connOpen();
SqlCommand cmd = new SqlCommand(select * from customers conn);
SqlCommand cmd = new SqlCommand(select * from orders conn);
SqlDataReader rdr = cmdExecuteReader();
// next statement causes an error prior to SQL Server
SqlDataReader rdr = cmdExecuteReader();
// now you can reader from rdr and rdr at the same time
三异步执行Command命令
在 ADONET 以前通过 Command 类(如 SqlCommandOleDbCommand等)执行 SQL
命令的线程一定要停下来等待执行结果ADONET 新增了异步程序访问接口(asynchronous API)让线程发出命令后可以继续执行接下去的程序代码
而在 ADONET 当前的版本只有 SqlClient 支持异步程序访问接口
以往编写程序时我们可以直接通过NET Framework 所提供的多线程机制或是以 Delegate 类包装多线程的方式在 NET Framework 所提供的异步架构下设计调用执行 Command 对象实例这些方法都是让一条工作线程(Worker Thread)停止在后台中等待执行结果一旦有结果后工作线程再通过标准的机制告知结果
原本 ADONET 的 Command 对象执行 SQL 语法的方法有
ExecuteReaderExecuteNonQueryExecuteXmlReader 以及 ExecuteScalar 等搭配 NET
Framework 原来就提供的异步模型惯例除了 ExecuteScalar 方法外其余的方法都新增了以 Begin 和 End 关键字开始的一对方法也就是说 ExecuteReader 方法是同步执行若要以异步的方式执行相同的功能则调用 BeginExecuteReader 和 EndExecuteReader 这一组方法在 NET Framework 中以 Begin 为字首的方法负责传入同名方法所需的参数而以 End
为字首的方法用来取回执行结果
例如某个方法的定义如下
public override int ExecuteNonQuery()
则以异步调用的起始方法定义如下
public IAsyncResult BeginExecuteNonQuery(AsyncCallback callback object stateObject)
Begin~ 系列的方法会多加存放回调方法(Delegation)的指针参数也就是上述语法中的 callback 参数并提供语法中的 stateObject参数让你设置想要带到 End~ 对应方法的信息而 Begin~ 系列方法最后返回的是代表异步执行状态的 IAsyncResult 对象实例而不是原本同步执行方法的返回结果你可以藉此查询异步执行的状况
而获得执行结果的方法定义如下
public int EndExecuteNonQuery(IAsyncResult asyncResult)
在调用与 Begin~ 对应的 End~ 方法时需要带入 Begin~ 方法所返回的 IAsyncResult
对象实例异步执行完毕后取回与原先同步执行方法相同的执行结果
由于我们在执行完 Command 对象访问数据库的方法后都会返回对象如 ExecuteReader 取回 DataReader实例ExecuteNonQuery 取回受影响的记录条数ExecuteXmlReader 取回 XmlReader 实例因此大概都需要通过End系列方法来获得执行结果否则这些结果就遗失在系统中
若要异步执行 Command 命令另一个必需设置的是数据库连接字符串内要加上 async=true 属性若连接字符串没有加上该属性而通过 Command对象实例调用异步执行的方法则会产生异常(exception)若 Command 通过连接执行时重头到尾都是以同步的方式执行则依照默认 async=false 的方式设置比较节省资源若某些命令需要同步执行另一些需要异步执行则可以考虑使用不同的连接
在介绍范例应用程序前我们先稍微谈一下 NET Framework 所提供的公共的异步运行应用程序设计模式不只是 ADONET在其他访问耗时的程序编写上也都可以套用这个模式
NET Framework内置了让应用程序异步运行的功能让你在编写应用程序时不会因为某些耗时等待的操作让程序停止响应操作界面停滞让用户感觉起来好像死机一样一般会以多线程的方式处理这种需求但若你不熟悉线程的运行或是想利用线程池(Thread Pool)的好处都可以在较为耗时的操作上采用 NET Framework 所提供的异步功能
一般来说文件 I/O网络访问乃至于 Web Services 访问以及本节所讨论的 DB 访问等都较为耗时NET Framework为这一类的类都提供了上述以 Begin~/End~开头的非同步执行方法而这些方法皆成对出现当然也有可能是自己编写的方法其商业逻辑非常复杂导致调用该方法后需要等待一段时间来完成这时还可以通过 NETFramework 所提供的委托(Delegate)类来创建异步运行
但是实际在我们的应用中
但我们不需要获知DB服务器的返回信息时我们推荐使用委托尤其是在Web开发中
因为在页面线程启动异步数据库访问时当页面业务执行完毕后仍然无法放开访问数据库的异步线程这是我们不希望看到的但是使用委托却可以避免这个麻烦(webservice异步应用中一样如此)
四使用SqlBulkCopy批量装载数据(仅限SqlClient)
以往访问 SQL Server 时若有大量的数据记录需要添加到数据库内例如从主机系统或是 NCR TeradataOracle等数据库系统下载大量数据记录我们想要将它们快速添加到 SQL Server 中可以有的选择是调用 TSQL 的 Bulk Insert 语法通过Linked Server 执行 SELECT INTO 语法或是执行 bcpexe 工具程序以及通过 DTS 的 Bulk Insert Task 或启动Transform Data Task 的快速装载(Use Fast Load)设置
但若要通过自行编写的程序完成批次装载只能以 C/C++ 调用 OLEDB 或 ODBC 的 Bulk API无法通过 ADONET 或 ADO 等对象来执行
ADONET 的 SqlClient 提供了一个新的类称为 SqlBulkCopy它让 DataSet 内大量的数据或是 DataReader通过数据流(Stream)直接读取大量的记录可以快速将这些记录添加到目的数据库的数据表中但要注意的是它并非如我们一般用的 bcpexe工具程序可以从某个符号分隔文件读取大量数据选择性地搭配格式文件(Format File)将记录装载到数据库中或是将数据库内的数据导出成为一个文件但由于DataSet 能集成 XML 数据因此依然可以采用 SqlBulkCopy 类型轻松地通过 DataSet 将 XML 文件数据大量转入到数据库
可以利用SqlBulkCopy类快速写入大批量数据针对SQL Server的优化可以写入DataRow数据DataTableDataReader并且可以映射不同的数据列名
WriteToServer(DataTable)写入数据表
WriteToServer(DataRow[])批次写入数据行
WriteToServer(DataTable DataRowState)按行状态写入数据库表
WriteToServer(IDataReader)写入DataReader对象
下面是个示例
using (SqlConnection sqlcon =
new SqlConnection(Data Source=;user id=oa;password=oapassword;initial catalog=test))
{
sqlconOpen();
using (SqlBulkCopy bcp = new SqlBulkCopy(sqlcon))
{
bcpBulkCopyTimeout = ;
bcpDestinationTableName = dboTest;
bcpColumnMappingsAdd(id id);
bcpColumnMappingsAdd(name name);
bcpColumnMappingsAdd(name name);
bcpColumnMappingsAdd(name name);
//映射到不同名列
bcpColumnMappingsAdd(changedname name);
bcpWriteToServer(dt);
sqlconClose();
}
}
但是SqlBulkCopy使用时要注意以下几点
.确认确实需要大容量更新在执行此操作(几十行的数据请尽量使用别的渠道把)
.
.确认数据一致性与检查机制以免遇到主键沖突数据不符格式等意外
.
.SqlBulkCopy操作可能会导致对目标表元数据的更改(例如禁用约束检查时)如果出现这种情况访问大容量插入表的并发快照隔离事务将失败
SqlBulkCopy将向数据库下大容量更新锁请注意并发性以免其他连接因长时间等待而超时
五DataSet的性能提升
对于开发人员来说ADONET 最激动人心的变化莫过开发组终于实现了他们许诺多年的事情确实提升Dataset的性能了
由于版本Dataset令人不敢恭维的性能使得Dataset许多方面被其性能问题而掩盖
现在在大幅度提升了Dataset的性能后Dataset终于能日趋完美了
提升是多方面的被提升的方面包括下面几块
. 索引引擎被大大的提升
在对ADONET 的Dataset作了相当数目的测试后微软终于宣布Dataset的数据访问能力获得极大的提高广泛的数字是增加倍!!!而且不像中排序的陡峭曲线中的排序尽量做到了线型递增!
真不知小组是以前做的实在太烂了还是有了新的狠招被发明了^_^
二进序列制化的Dataset
Dataset有个好属性是支持序列化但是有很多人对其提出了批评不是这些人不喜欢Dataset支持序列化而是序列化后的Dataset真的是太肥了大量的
<xs:element name=type…占用了大量的空间以至让人难以忍受
现在我们只需要设置 RemotingFormat 属性为SerializationFormatBinary(默认是SerializationFormatXML)则在序列化时完全采用二进制的数据格式如此数据较小因而较有效率(官方的观点是缩为SerializationFormatXML的/)