讀者要求:基本掌握 visual c#.net 、dataadapter 和 dataset 。
環境
[配置一]
操作系統: windows 2000 服務器
計算機: dell inspiron 8000 筆記本
內存: 512mb
處理器: piii 750 mhz
工具: .net sdk beta 2
數據庫: sql 服務器 2000 的 pubs 數據庫
[配置二]
操作系統: windows xp professional
計算機:network! 3000 筆記本
內存:256mb
處理器: piii 850 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
n'select pub_id, title, price from titles; select pub_id, pub_name from publishers'
數據庫也通過 rpc 返回兩個 rowset。在 dataset 中,rowset 與基本表是一一對應的。不幸的是,在 fill 模式下無法對這些基本表命名。相反,它為所有基本表提供一個共同的基本名。事實上,基本名就是第一個基本表的名字。隨后的基本表命名都是在基本名后面加上一個不同的數字以互相區別,例如:titles ,titles1等。但是,通過簡單的屬性設置就能給所有基本表命名了:
datest.fill(dstest, "titles")
dstest.tables[1].tablename = "publishers"
這種顯式命名有助于基本表的處理和引用。
關于存儲過程
在 ado.net 中如何使用存儲過程?天,太復雜了!但我還是要簡單地介紹一兩點,為以后討論層次數據作個鋪墊吧!
利用存儲過程同時獲取多個行集(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 屬性設置指令類型為
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 版。
新聞熱點
疑難解答
圖片精選