ADO.NET最佳實踐(中)
2024-07-21 02:08:41
供稿:網友
使用datareader可以在你的應用程序中做以下事情:
i.不需要緩存數據;
ii.處理太大而不能存儲的數據;
iii.需要以只進、只讀和快速方式一次性訪問數據的。
g.使用一個自定義的強有力的dataset類型的好處
通過創建一個繼承于dataset的子對象,你可以在運行期間執行類型檢查和聲明。當你有了一個確定的計劃或者為你的dataset有相關的結構,你就可以創建一個用行和列表述一個對象的dataset。比如,你表露一個消費者對象的名字屬性來取代表露消費者表的一行中的名字列。有關此節詳細信息,請參考微軟站點上的文章:working with a typed dataset
h.在自定義的dataset中處理無效數據
通過xsd語言檢查你的dataset確保你的dataset適當地處理無效引用。nullvalue注釋使你把bbnull替換成別的字符,string.empty;或者保留無效引用,拋出錯誤提示,提示將取決于你應用程序的上下文,默認情況是引用了無效字符。
i.在dataset中刷新數據
如果你要從數據庫刷新你的dataset,使用dataadapter.fill,如果你的datatable擁有主鍵,dataadapter.fill將根據主鍵匹配新的行,同時從數據庫取值運用到已存在的行。除非已刷新行在再次刷新前被修改,否者它的rowstate將會被設置為unchanged。注意的是如果datatable沒有設置主鍵,你的dataset有可能出現重復的值。如果你想從數據庫刷新一個表并保留任何表中行的更改,那么你就要首先填充一個新表,然后利用preservechanges等于true來合并那個datatable到你的dataset中去。
j.在dataset中搜索數據
當你在一個dataset中查詢特殊標準的行時,利用索引查詢將會增加你的查詢性能。當你給一個datatable設計主鍵時,索引同時也創建了。當你為一個datatable創建dataview時,索引也同時創建了。以下是使用索引查詢的一些情況:
i.如果查詢與datatable中標識主鍵的列順序相反,使用datatable.rows.find代替datatable.select;
ii.如果查詢包括無主鍵的列,你可以使用dataview為數據的多重查詢改善性能。當你在dataview中使用排序時,查詢的同時就會創建一個索引。dataview使用find和findrows方法查詢datatable中的數據;
iv.假如你不需要表的排序視圖,你也可以利用dataview為datatable創建一個索引查詢。注意的是這僅僅在你執行多重查詢時才有優勢,如果你只是執行一個簡單查詢,使用此方法將會降低你的查詢效率。
k.dataview的結構
前面也講過,在給datatable創建dataview和sort、rowfilter或者rowstatefilter屬性發生更改的同時潛在的也給datatable創建了索引。創建dataview對象時,如果sort、rowfilter和rowstatefilter屬性也同時指定,那么索引將只創建一次;如果創建一個空的dataview,那么索引至少被創建兩次。
l.頁面調度
ado.net使你可以很清楚地控制從你的數據庫返回什么樣的數據和有多少數據存儲到一個dataset。以下沒有單一的介紹調度一個查詢結果,但是當你設計你的應用程序時應該考慮到以下情況:
i.避免在使用dataadapter.fill時,在startrecord和maxrecords值上溢出。
ii.解決這類問題的辦法是使用where語句、order by語句和top斷言。
iii.還有一種解決辦法是使用top斷言和嵌套的select聲明。比如如下代碼:
select top 10 * from
(select top 30 * from customers order by id asc) as table1 order by id desc
iv.如果你的日期不是經常改變,你可以使用dataset的存儲功能改善執行性能,比如你可以存儲相當10頁的數據到你的dataset,然后當用戶訪問超過在存儲區的firstpage和lastpage時才查詢數據庫以獲得新的數據。
m.有計劃地填充dataset
當使用數據填充dataset時,dataadapter.fill方法使用dataset已有的計劃和selectcommand返回的數據對dataset進行填充。如果dataset中沒有與之對應的表將會失敗,fill創建一個表,默認情況下,fill僅僅定義列和列的類型。你可以通過設置dataadapter的missingschemaaction屬性重載默認的fill方法。舉例,要使fill方法創建表時總是包含主鍵信息、唯一約束、列屬性、是否允許空值、列的最大長度、只讀列和自動增量列,指定dataadapter.missingschemaaction為missingschemaaction.addwithkey。作為選擇,你也可以在調用dataadapter.fill之前調用dataadpater.fillschema來保證填充dataset時計劃到位。調用fillschema會給數據庫增加額外的負擔來輸出schema信息,所以最好的建議是指定dataset的計劃,或者在調用fill之前設置dataadapter的missingschemaaction。
n.使用commandbuilder
commandbuilder自動地生成基于dataadapter的selectcommand的dataadapter的insertcommand、updatecommand和deletecommand屬性。提供selectcommand執行一個簡單的select,以下信息介紹使用commandbuilder的最佳處理。
i.在設計階段不要使用commandbuilder,否者產生dataadapter command屬性的進程將會受到干擾。如果你預先知道你的update、insert和delete聲明的內容,你應該清楚地指定。一個最好的設計方案是為你的update、insert和delete創建存儲過程,并在dataadapter的command屬性中設置和使用它們。
ii.commandbuilder使用selectcommand決定其他command屬性的值。如果dataadapter的selectcommand本身發生變化,應該使用refreshschema去刷新command的屬性。
iii.只要dataadapter的command屬性為空,commandbuilder就僅僅創建一個command,即使你明確地指定command的屬性值,commandbuilder也不會重寫,所以如果你想創建一個command并保留以前的屬性設置,那么就把command的屬性設置為null。
o.sql的批聲明和處理
很多的數據庫都支持在一條命令中使用綜合查詢或批處理或多條子命令。比如sql server中使用“;”。在一條命令中使用綜合的多重命令可以有效地減少與數據庫之間交互的次數并在你的應用程序中提高效率。比如在你的應用程序中使用批處理完成所有的刪除(delete)任務等等。
使用批處理確實提高了效率,但同時也在你的應用程序中管理更新dataset數據時增加了復雜性。要使得復雜性變簡單化,你就要在你的dataset中為每個datatable創建一個dataadapter。
p.使用多個表填充一個dataset
如果你是用批處理從多個表返回數據并把這些數據填充到一個dataset,fill方法將會使用第一個表的表名命名第一個表,以后的表命名將會采用在第一個表的表名基礎上加上一個遞增的數字。舉例,下面的代碼將逐步說明fill方法的工作原理:
‘visual basic
dim da as sqldataadapter = new sqldataadapter(“select * from customers;select * from orders;”,myconnection)
dim ds as dataset = new dataset()
da.fill(ds,”customers”)
‘c#
sqldataadapter da = new sqldataadapter(“select * from customers;select * from orders;”,myconnection);
dataset ds = new dataset();
da.fill(ds,”customers”);
如上面代碼所示,customers表數據將會存放在一個命名為customers的datatable中,而orders表數據將會放在一個命名為customers1的datatable中。當然你也可以在數據填充結束后很容易地修改customers1表屬性(tablename)為orders。然而,在以后的數據填充時,只會影響customers表中數據,而orders表將會忽略并同時創建一個新的命名為customers1的表。要解決這個問題,你就要在customers1和orders之間建立一個datatablemapping映射。其他表也如此。舉例說明:
‘visual basic
dim da as sqldataadapter = new sqldataadapter("select * from customers; select * from orders;", myconnection)
da.tablemappings.add("customers1", "orders")
dim ds as dataset = new dataset()
da.fill(ds, "customers")
‘c#
sqldataadapter da = new sqldataadapter("select * from customers; select * from orders;", myconnection);
da.tablemappings.add("customers1", "orders");
dataset ds = new dataset();
da.fill(ds, "customers");
q.使用datareader
下面是使用datareader的一些技巧和一些問題的回答:
i.在使用相關command訪問任何輸出參數之前必須關閉datareader;
ii.在讀取數據結束后應當關閉datareader。如果你的connection僅僅是用來返回datareader,那么在關閉datareader之后你也應該立即關閉它。另外一個關閉connection的方法是傳遞commandbehavior.closeconnection給executereader方法。此方法在你從一個方法中返回datareader并且對這個datareader沒有關閉控制權或者關聯的連接時使用時非常有用的。
iii.datareader是為已連接的數據存取設計;
iv.使用getstring、getint32等返回特殊數據類型數據;
v.一個連接只允許使用一個datareader。在ado中,如果你只創建一個連接并使用兩個recordsets,一個只讀游標和一個只進游標,但實際上,ado已經為你創建了一個隱式的連接,并在不用的時候隱式地關閉它。ado.net中是不行的,你必須為每個datareader創建一個connection,這也是為了讓你在使用connection時給予更多的控制信息。
vi.默認下,datareader每次讀取時把行中所有的數據裝載到內存中。并允許你隨機存取當前行中數據。如果隨即存取沒有必要(沒有必要把所有數據都裝載到內存),并想提高執行效率,給executereader方法傳遞commandbehavior.sequentialaccess,這樣就會改變datareader的默認動作為僅僅裝載請求的數據到內存。注意的是這種方法要求你順序地存取行中列數據,一旦你略過某一列,以后你將再不會讀取到該列的數據。
vii.如果當你在完成從一個datareader讀取數據后,仍然還有大量未讀不需要的數據,這就要在調用datareader的close命令之前調用cancel命令。調用datareader的close命令會導致把不需要的數據裝載進來并在關閉游標之前清空數據。而調用cancel命令就會丟棄這部分數據,從而datareader在關閉之前就不會讀取它們。如果你正在從你的命令返回輸出參數,調用cancel命令同樣會丟棄它們。如果你需要讀取任何輸出參數,你就不要使用cancel,而直接使用close。
r.blobs對象
當你使用datareader讀取二進制數據時,你應該傳遞commandbehavior.sequentialaccess給executereader方法調用。因為datareader的默認情況是每次讀取數據時把每行中所有的數據都存儲到內存,而二進制數據又非常的大,結果就會使大量的內存空間被一個單一的blob占用。sequentialaccess使得你的datareader默認行為為僅讀取需要的數據。然后你就可以使用getbytes或者getchars決定一次讀取多少數據。
記住的是使用sequentialaccess后,你不能次序顛倒地訪問datareader中不同的字段。就是說,如果你的查詢返回三列,其中第三列是blob數據類型,如果你想訪問第三列數據,那么你就必須先訪問第一列,然后是第二列,再然后才是第三列的blob數據。這是因為此時返回的數據是有序的,而且一旦你跳過某一列,再回過頭來讀取這一列是不行的。
s.使用命令
ado.net提供了執行命令的幾種不同方法,同時也提供了優化執行命令的幾種不同參數。下面將要介紹的是選擇最佳執行命令的技巧和改善一個可執行命令的性能。
i.oledbcommand最佳實踐
.net 框架中各種數據提供者之間的執行命令標準幾乎是一樣的。但是也有不同,下面是執行oledbcommand的一些技巧:
①使用commandtype.text調用存儲過程,使用commandtype.storedprocedure生成;
②確定設置oledbparameter的類型、大小(如果要求)和精度(如果是數字或者小數),注意的是,如果你不明確設置oledbparameter,oledbcommand將會為你的執行命令重新生成oledbparameter。
ii.sqlcommand最佳實踐
使用sqlcommand快速執行存儲過程:如果你要調用一個存儲過程,指定sqlcommand的commandtype為存儲過程的commandtype。這樣在執行命令時,就會提交此命令是調用存儲過程,從而達到快速執行。
iii.使用已準備的方法
command.prepare方法優化你的參數化執行命令。prepare結構為多重調用最優化指定命令。要使用prepare,你首先得理解你的數據庫是怎樣相應prepare調用。sql server 2000中,command已經被隱式優化和prepare不是必須的;在sql server7.0或其它數據庫中使用prepare是有效的。
iv.明確地指定計劃和元數據
ado.net中的很多對象都要推斷元數據信息,只要用戶不指定它,舉例如下:
①如果在dataset中不存在,dataadapter.fill方法就會創建表和列信息;
②commandbuilder為獨立表的select命令生成dataadpater命令參數;
③commandbuilder.deriveparameters組裝一個命令對象的參數信息;
如果什么時候都使用上面講的方法,可能會降低執行性能。推薦在設計階段和廣告段應用程序中使用。可能的情況下,一般都要指定計劃和元數據。這些包括指定dataset的表和列、指定dataadapter的command屬性和指定command的參數信息。
v.executescalar和executenonquery
如果你想只返回一個簡單值,比如count(*)、sum(price)或者avg(quantity),你可以使用executescalar,它幫助你一步到位得到你想要的值,從而避免使用datareader的兩步計算(executereader+getvalue);
當你不想返回行信息,比如修改數據(insert、update、delete)或者僅需要輸出參數或者返回值,使用executenonquery,它去掉不必要的處理創建一個空的datareader。
vi.空值檢查
如果在你的表中某列允許空值,你可以使用where語句進行空值檢查,下面舉例說明:
select * from customers where (([email protected]) or (lastname is null and @lastname is null))
上面語句檢查了列是否為空和參數是否為空。
vii.傳遞null參數值
當你在命令中傳遞null參數值給數據庫時,你不能使用null(nothing在vb中),應該使用dbnull.value。舉例:
‘vb
dim param as sqlparameter = new sqlparameter(“@name”,sqldbtype.nvarchar,20)
param.value = dbnull.value
‘c#
sqlparameter param = new sqlparameter(“@name”,sqldbtype.nvarchar,20);
param.value = dbnull.value;
viii.使用事務處理
ado.net中的事務處理模型已經改變,在ado中,一旦starttransaction被調用,任何事務下的更新都被認為是事務的一部分。然而,在ado.net中,當connection.begintransaction被調用,返回一個命令關聯的事務對象(事務屬性是由命令的事務屬性指定的)。這樣保證讓你在一個connection中執行多個事務。如果命令的command.transaction屬性與開始的事務不一致,命令就不會完成并拋出錯誤。
ix.使用connections
高效率的應用程序應該使用最少的時間與數據庫建立連接,比如使用connection pooling等。下面將介紹如何使用ado.net建立高效率應用的一些數據庫方面的技巧。
①connection pooling
在sql server、ole db和.net框架結構中的data provider中,都提供了隱式的連接池連接支持。你可以在connectionstring中指定不同的參數值控制連接池的行為。比如下面的例子使ole db的連接池無效并自動地進行事務處理:
provider=sqloledb;ole db services=-4;data source=localhost;integrated security=sspi;
在sql server.net data provider中提供了以下參數設置控制連接池的行為:connection lifttime、connection reset、enlist、max pool size、min pool size和pooling。
②使用dataadapter最優化連接
使用dataadpater的fill和update方法時會自動地打開相應的連接。如果fill或者update打開一個連接,在它操作完成后它會關閉此連接。最好的執行方式是在你需要的時候才建立連接。同時減少多個操作時打開和關閉連接的次數。
推薦你在僅僅執行一個fill或者update時,允許fill或者update方法隱式地打開和關閉連接;如果你要執行多個fill或者update,建議你顯式地建立連接、執行fill或者update操作然后顯式地關閉連接。
額外地,在我們執行事務處理時,在開始事務之前應該顯式地建立連接,并在事務結束后顯式地關閉連接。舉例:
‘visual basic
public sub runsqltransaction(da as sqldataadapter, myconnection as sqlconnection, ds as dataset)
myconnection.open()
dim mytrans as sqltransaction = myconnection.begintransaction()
mycommand.transaction = mytrans
try
da.update(ds)
mytrans.commit()
console.writeline("update successful.")
catch e as exception
try
mytrans.rollback()
catch ex as sqlexception
if not mytrans.connection is nothing then
console.writeline("an exception of type " & ex.gettype().tostring() & _
" was encountered while attempting to roll back the transaction.")
end if
end try
console.writeline("an exception of type " & e.gettype().tostring() & " was encountered.")
console.writeline("update failed.")
end try
myconnection.close()
end sub
‘c#
public void runsqltransaction(sqldataadapter da, sqlconnection myconnection, dataset ds)
{
myconnection.open();
sqltransaction mytrans = myconnection.begintransaction();
mycommand.transaction = mytrans;
try
{
da.update(ds);
mycommand.transaction.commit();
console.writeline("update successful.");
}
catch(exception e)
{
try
{
mytrans.rollback();
}
catch (sqlexception ex)
{
if (mytrans.connection != null)
{
console.writeline("an exception of type " + ex.gettype() +
" was encountered while attempting to roll back the transaction.");
}
}
console.writeline(e.tostring());
console.writeline("update failed.");
}
myconnection.close();
}
x.總是關閉connections和datareaders
在你使用完connection或者datareader對象后,你應該明確地關閉它們。系統中的碎片整理程序只是在最后需要的時候才進行整理,而一些很耗資源的連接還得由你自己來釋放。同時如果你不明確地關閉連接,此連接就有可能不返回連接池,除非連接池的max pool size已經達到并且此連接還仍然有效。
注意:在你的類的finalize方法中不要使用close或者dispose運用到一個connection或者一個datareader或者任何被管理對象上。在一個finalizer中,僅僅釋放你的類直接擁有的無法管理的資源。如果你的類不擁有任何無法管理的資源,就不要在你的類使用finalize方法。
xi.在c#中使用using聲明
在c#中,一個非常便利的保證關閉你使用過的connection和datareader對象的方法是使用using聲明。當對象超出了它的使用范圍,using聲明就會自動地釋放該對象。舉例:
‘c#
string connstring = "data source=localhost;integrated security=sspi;initial catalog=northwind;";
using (sqlconnection conn = new sqlconnection(connstring))
{
sqlcommand cmd = conn.createcommand();
cmd.commandtext = "select customerid, companyname from customers";
conn.open();
using (sqldatareader dr = cmd.executereader())
{
while (dr.read())
console.writeline("{0}/t{1}", dr.getstring(0), dr.getstring(1));
}
}
using聲明在visual basic.net中不可用。
xii.避免訪問oledbconnection.state屬性
如果你需要經常檢查state屬性,最好在oledbconnection上監聽statechange事件。下面的代碼演示當oledbconnection.state發生變化時使用statechange向控制臺發送一條消息:
‘visual basic
addhandler nwindconn.statechange, new statechangeeventhandler(addressof onstatechange)
protected shared sub onstatechange(sender as object, args as statechangeeventargs)
console.writeline("the current connection state has changed from {0} to {1}.", _
args.originalstate, args.currentstate)
end sub
‘c#
nwindconn.statechange += new statechangeeventhandler(onstatechange);
protected static void onstatechange(object sender, statechangeeventargs args)
{
console.writeline("the current connection state has changed from {0} to {1}.",
args.originalstate, args.currentstate);
}
ado.net最佳實踐(下)
http://www.csdn.net/develop/read_article.asp?id=22664