用ADO.NET处理层次数据(二)
关于存储过程
在 ADO.NET 中如何使用存储过程?天,太复杂了!但我还是要简单地介绍一两点,为以后讨舅端魍蒗论层次数据作个铺垫吧!
利用存储过程同时获取多个行集(rowset)的方法有两种。
第一种方法,是“一个存储过程,多个输出行集”。例如,我们可以在前一个例子的基础上增加一个存储过程,将两条 select 语句包含进去:
CREATE PROCEDURE [dbo].[TitlesPERPublisher]ASBeginSELECT Pub_Id, Title, Price FROM TitlesSELECT Pub_ID, Pub_Name FROM PublishersEnd
够简单吧!这段代码提交两个 select 语句,因而将返回两个行集。
为了提高效率,我们可以借助 dataAdapter 的 Selectcommand 属性设置指令类型为
CommandType.StoredProcedure :
daHDAta = new SqlDataAdapter(sSQLCmd, cnstring)dahData.SelectCommand.CommandType = CommandType.StoredProcedure
因为这样可以指示 dataAdapter 使用效率较高的 t-sql 语句 Exec 执行存储过程。如果省略这一步,dataAdapter 就以低效的 sp_executesql 执行它了。
第二种方法,是“两个存储过程,两个输出行集”。然而,此法造成数据来回传递,况且无论数据转输抑或 RPC 建立都是耗时过程,效率自然大打折扣。
由此我们得出结论:争取用一个存储过程返回全部行集 。就本例而言,最简单的做法莫过于用一个新过程捆绑两个存储过程。此法或许不尽完美,但是别忘了,这是“最简单”的。
至于如何在应用程序和ado.net 中同时调用两个存储过程,由于篇幅有限,请自行参考有关 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 。
下列代码在现有的基本表之间新增了一个简单的关系:
dsTest.Relations.Add("PubTitles",dsTest.Tables["Publishers"].Columns["Pub_ID"],dsTest.Tables["Titles"].Columns["Pub_ID"])
此代码在名为 PubTitles 的关系集合中创建了一个 relation 对象和一个关系:Publishers.Pub_ID 是父表,而 Titles.Pub_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 dtPublishers.Rows){Console.WriteLine(drPublisher["Pub_Id"] + "\t" + drPublisher["Pub_Name"]);Console.WriteLine("=====================");
foreach(DataRow drTitle in drPublisher.GetChildRows("PubTitles")){Console.Write(drTitle["Title"] + "\t");Console.Write((drTitle["price"].ToString() != null ? drTitle["price"] : "n/a"));}}
当然,也可明确指定一个 relation 对象:
DataRelation drPubsTitles = dsHData.Relations.Add("PubTitles",dtPublishers.Columns["Pub_ID"],dsHData.Tables["Titles"].Columns["Pub_ID"]);foreach(DataRow drPublisher in dtPublishers.Rows){Console.WriteLine(drPublisher["Pub_Id"] + "\t" + drPublisher["Pub_Name"]);Console.WriteLine("=====================");
foreach(DataRow drTitle in drPublisher.GetChildRows(drPubsTitles)){Console.Write(drTitle["Title"] + "\t");Console.Write((drTitle["price"].ToString() != null ? drTitle["price"] : "n/a"));}}
ADO.NET 能让程序员在数据表中创建自定义视图。这是由 DataView 类实现的:
public class DataView : MarshalByValueComponent, IBindingList,IList, ICollection, IEnumerable, ITypedList, ISupportInitialize
当然,限于篇幅,这里仅仅列举了部分函数。
数据视图提供了两个有趣的属性:RowFilter 和 Sort 。RowFilter 与 ADO recordset 对象的 Filter 属性相似,它相当于与 SQL 语法中的 WHERE 语句,能够筛去匹配的列:
dtPublishers.DefaultView.RowFilter="Pub_ID < 2000";
最终得到的列被置于 DataRowView 集合中,因此能用 for each 语句循环处理它们。
Sort 属性用于指定输出数据的排序方式。它与 SQL 语法中的 ORDER BY 命令相似:
dtPublishers.DefaultView.Sort="PUB_ID Desc";
每个基本表对应一个 DataView 对象,上述DefaultView 就是其属性。于是,只需做些小小的修改,我们就能有选择地循环显示数据了。
foreach(DataRow drPublisher in dtPublishers.Rows){Console.WriteLine(drPublisher["Pub_Id"] + "\t" + drPublisher["Pub_Name"]);Console.WriteLine("=====================");
foreach(DataRow drTitle in drPublisher.GetChildRows("PubTitles")){Console.Write(drTitle["Title"] + "\t");Console.Write((drTitle["price"].ToString() != null ? drTitle["price"] : "n/a"));}}
ADO.NET 大大简化了层次数据的处理,并且提供了改良的方案。
读过本文,是否跃跃欲试呢?若要追求更强的功能,恐怕还得另请高明了。
本文没有考虑性能优化,因为我们讨论的 SDK 还是 beta 2 版。