c#

位置:IT落伍者 >> c# >> 浏览文章

用ADO.NET处理层次数据


发布日期:2021年06月04日
 
用ADO.NET处理层次数据

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

               

上一篇:浅析.Net Micro Framework的数据永久存储

下一篇:.net Web用户控件使用技巧