ADONET 为数据在内存中的操作和储存提供了一种新模式因此我们在处理层次数据时该换换脑了本文将对其独到之处作一个简单的描述
读者要求基本掌握 Visual DataAdapter 和 DataSet
环境
[配置一]
操作系统 Windows 服务器
计算机 Dell Inspiron 笔记本
内存 mb
处理器 PIII MHz
工具 NET SDK Beta
数据库 SQL 服务器 的 Pubs 数据库
[配置二]
操作系统 Windows XP Professional
计算机Network! 笔记本
内存mb
处理器 PIII MHz
工具NET Final
数据库MSDE 的 Pubs 数据库
简单的数据检索
我们首先要做的是通过 SQLAdapter 向数据库提交两个查询语句
本例中SQLAdapter 使用由两个 select 语句组成的 SQL 命令分别向 Pubs 数据库中的两个 table 发出查询请求
string sSQL = SELECT Pub_Id Title Price FROM; SELECT Pub_ID Pub_Name FROM Publishers
在 fill 模式下 SQLAdapter 将在查询命令前插入 sp_executesql 再以 RPC 的形式一并提交给数据库
Exec sp_executesql
NSELECT Pub_Id Title Price FROM Titles; SELECT Pub_ID Pub_Name FROM Publishers
数据库也通过 RPC 返回两个 rowset在 Dataset 中rowset 与基本表是一一对应的不幸的是在 fill 模式下无法对这些基本表命名相反它为所有基本表提供一个共同的基本名事实上基本名就是第一个基本表的名字随后的基本表命名都是在基本名后面加上一个不同的数字以互相区别例如Titles Titles等但是通过简单的属性设置就能给所有基本表命名了
daTestFill(dsTest Titles)
dsTestTables[]TableName = Publishers
这种显式命名有助于基本表的处理和引用
关于存储过程
在 ADONET 中如何使用存储过程?天太复杂了!但我还是要简单地介绍一两点为以后讨论层次数据作个铺垫吧!
利用存储过程同时获取多个行集(rowset)的方法有两种
第一种方法是一个存储过程多个输出行集例如我们可以在前一个例子的基础上增加一个存储过程将两条 select 语句包含进去
CREATE PROCEDURE [dbo][TitlesPERPublisher]
AS
Begin
SELECT Pub_Id Title Price FROM Titles
SELECT Pub_ID Pub_Name FROM Publishers
End
够简单吧!这段代码提交两个 select 语句因而将返回两个行集
为了提高效率我们可以借助 dataAdapter 的 Selectcommand 属性设置指令类型为
CommandTypeStoredProcedure
daHDAta = new SqlDataAdapter(sSQLCmd cnstring)
dahDataSelectCommandCommandType = CommandTypeStoredProcedure
因为这样可以指示 dataAdapter 使用效率较高的 tsql 语句 Exec 执行存储过程如果省略这一步dataAdapter 就以低效的 sp_executesql 执行它了
第二种方法是两个存储过程两个输出行集然而此法造成数据来回传递况且无论数据转输抑或 RPC 建立都是耗时过程效率自然大打折扣
由此我们得出结论争取用一个存储过程返回全部行集 就本例而言最简单的做法莫过于用一个新过程捆绑两个存储过程此法或许不尽完美但是别忘了这是最简单的
至于如何在应用程序和 中同时调用两个存储过程由于篇幅有限请自行参考有关 sqlCommand 对象的文章
关系
为了处理现实中的层次数据必须理清基本表之间的关系借助 Dataset 的关系集合很容易建立起关系语法简洁明了应该不成问题
public void Add(DataRelation);
public virtual DataRelation Add(DataColumn DataColumn);
public virtual DataRelation Add(DataColumn[] DataColumn[]);
public virtual DataRelation Add(string DataColumn DataColumn);
public virtual DataRelation Add(string DataColumn[] DataColumn[]);
public virtual DataRelation Add(string DataColumn DataColumn bool);
public virtual DataRelation Add(string DataColumn[] DataColumn[] bool);
为了建立关系必须提供一个关系名字符串和至少两个列如果关系已经存在或者列有问题 (比如它们不存在)则运行环境将产生一个异常详情请见 NET 框架 SDK
下列代码在现有的基本表之间新增了一个简单的关系
dsTestRelationsAdd(PubTitles
dsTestTables[Publishers]Columns[Pub_ID]
dsTestTables[Titles]Columns[Pub_ID])
此代码在名为 PubTitles 的关系集合中创建了一个 relation 对象和一个关系PublishersPub_ID 是父表而 TitlesPub_id 是子表
显示数据
为了选取子列datarow 对象提供了一个 GetChildRows 方法它的参数是关系名或许关系对象名
public DataRow[] GetChildRows(DataRelation);
public DataRow[] GetChildRows(string);
public DataRow[] GetChildRows(DataRelation DataRowVersion);
public DataRow[] GetChildRows(string DataRowVersion);
类似的方法还有 GetParentRow 和 GetParentRows 它们根据子列返回父列的名字
现在有了 GetChildRows 方法就向数据进军吧! GetChildRows 返回一个 DataRowCollection 对象后者的父类 InternalDataCollectionBase 是对 ICollection 和 IEnumerable 的具体实现
接下来的循环处理只是举手之劳了下列代码演示了显示数据关系的一种简单方法
foreach(DataRow drPublisher in dtPublishersRows)
{
ConsoleWriteLine(drPublisher[Pub_Id] + \t + drPublisher[Pub_Name]);
ConsoleWriteLine(=====================);
foreach(DataRow drTitle in drPublisherGetChildRows(PubTitles))
{
ConsoleWrite(drTitle[Title] + \t);
ConsoleWrite((drTitle[price]ToString() != null ? drTitle[price] : n/a));
}
}
当然也可明确指定一个 relation 对象
DataRelation drPubsTitles = dsHDataRelationsAdd(PubTitles
dtPublishersColumns[Pub_ID]
dsHDataTables[Titles]Columns[Pub_ID]);
foreach(DataRow drPublisher in dtPublishersRows)
{
ConsoleWriteLine(drPublisher[Pub_Id] + \t + drPublisher[Pub_Name]);
ConsoleWriteLine(=====================);
foreach(DataRow drTitle in drPublisherGetChildRows(drPubsTitles))
{
ConsoleWrite(drTitle[Title] + \t);
ConsoleWrite((drTitle[price]ToString() != null ? drTitle[price] : n/a));
}
}
ADONET 能让程序员在数据表中创建自定义视图这是由 DataView 类实现的
public class DataView : MarshalByValueComponent IBindingList
IList ICollection IEnumerable ITypedList ISupportInitialize
当然限于篇幅这里仅仅列举了部分函数
数据视图提供了两个有趣的属性RowFilter 和 Sort RowFilter 与 ADO recordset 对象的 Filter 属性相似它相当于与 SQL 语法中的 WHERE 语句能够筛去匹配的列
dtPublishersDefaultViewRowFilter=Pub_ID < ;
最终得到的列被置于 DataRowView 集合中因此能用 for each 语句循环处理它们
Sort 属性用于指定输出数据的排序方式它与 SQL 语法中的 ORDER BY 命令相似
dtPublishersDefaultViewSort=PUB_ID Desc;
每个基本表对应一个 DataView 对象上述DefaultView 就是其属性于是只需做些小小的修改我们就能有选择地循环显示数据了
foreach(DataRow drPublisher in dtPublishersRows)
{
ConsoleWriteLine(drPublisher[Pub_Id] + \t + drPublisher[Pub_Name]);
ConsoleWriteLine(=====================);
foreach(DataRow drTitle in drPublisherGetChildRows(PubTitles))
{
ConsoleWrite(drTitle[Title] + \t);
ConsoleWrite((drTitle[price]ToString() != null ? drTitle[price] : n/a));
}
}
结论
ADONET 大大简化了层次数据的处理并且提供了改良的方案
读过本文是否跃跃欲试呢?若要追求更强的功能恐怕还得另请高明了
本文没有考虑性能优化因为我们讨论的 SDK 还是 beta 版