本文為您提供了在 microsoft ado.net 應(yīng)用程序中實現(xiàn)和獲得最佳性能、可伸縮性以及功能的最佳解決方案;同時也講述了使用 ado.net 中可用對象的最佳實踐;并提出一些有助于優(yōu)化 ado.net 應(yīng)用程序設(shè)計的建議。相關(guān)閱讀: ASP.NET中大數(shù)據(jù)量的分頁(1)http://hi.baidu.com/lonetraveller/blog/item/ee78a0f07ef61cca7831aa67.html ASP.NET中大數(shù)據(jù)量的分頁(2) http://hi.baidu.com/lonetraveller/blog/item/6aa37cc219c361120ff4777b.html 本文包含:
使用 datareader、dataset、dataadapter 和 dataviewado.net 提供以下兩個對象,用于檢索關(guān)系數(shù)據(jù)并將其存儲在內(nèi)存中:dataset 和 datareader。dataset 提供一個內(nèi)存中數(shù)據(jù)的關(guān)系表示形式,一整套包括一些表在內(nèi)的數(shù)據(jù)(這些表包含數(shù)據(jù)、對數(shù)據(jù)進行排序并約束數(shù)據(jù)),以及表之間的關(guān)系。datareader 提供一個來自數(shù)據(jù)庫的快速、只進、只讀數(shù)據(jù)流。 當(dāng)使用 dataset 時,經(jīng)常會利用 dataadapter(也可能是 commandbuilder)與數(shù)據(jù)源進行交互。當(dāng)使用 dataset 時,也可以利用 dataview 對 dataset 中的數(shù)據(jù)應(yīng)用排序和篩選。也可以從 dataset 繼承,創(chuàng)建強類型 dataset,用于將表、行和列作為強類型對象屬性公開。 下列主題包括的信息涉及:使用 dataset 或 datareader 的最佳時機、如何優(yōu)化訪問它們所包含數(shù)據(jù)、以及如何優(yōu)化使用 dataadapter(包括 commandbuilder)和 dataview 的技巧。 dataset 與 datareader 當(dāng)設(shè)計應(yīng)用程序時,要考慮應(yīng)用程序所需功能的等級,以確定使用 dataset 或者是 datareader。 要通過應(yīng)用程序執(zhí)行以下操作,就要使用 dataset:
對于下列情況,要在應(yīng)用程序中使用 datareader:
注填充 dataset 時,dataadapter 使用 datareader。因此,使用 dataadapter 取代 dataset 提升的性能表現(xiàn)為節(jié)省了 dataset 占用內(nèi)存和填充 dataset 需要的循環(huán)。一般來說,此性能提升只是象征性的,因此,設(shè)計決策應(yīng)以所需功能為基礎(chǔ)。 使用強類型 dataset 的好處 dataset 的另一個好處是可被繼承以創(chuàng)建一個強類型 dataset。強類型 dataset 的好處包括設(shè)計時類型檢查,以及 microsoft visual studio .net 用于強類型 dataset 語句結(jié)束所帶來的好處。修改了 dataset 的架構(gòu)或關(guān)系結(jié)構(gòu)后,就可以創(chuàng)建一個強類型 dataset,把行和列作為對象的屬性公開,而不是作為集合中的項公開。例如,不公開客戶表中行的姓名列,而公開 customer 對象的 name 屬性。類型化 dataset 從 dataset 類派生,因此不會犧牲 dataset 的任何功能。也就是說,類型化 dataset 仍能遠程訪問,并作為數(shù)據(jù)綁定控件(例如 datagrid)的數(shù)據(jù)源提供。如果架構(gòu)事先不可知,仍能受益于通用 dataset 的功能,但卻不能受益于強類型 dataset 的附加功能。 處理強類型 dataset 中的空引用 使用強類型 dataset 時,可以批注 dataset 的 xml 架構(gòu)定義語言 (xsd) 架構(gòu),以確保強類型 dataset 正確處理空引用。nullvalue 批注使您可用一個指定的值 string.empty 代替 dbnull、保留空引用或引發(fā)異常。選擇哪個選項取決于應(yīng)用程序的上下文。默認情況下,如果遇到空引用,就會引發(fā)異常。 有關(guān)更多信息,請參閱 working with a typed dataset。 刷新 dataset 中的數(shù)據(jù) 如果想用服務(wù)器上的更新值刷新 dataset 中的值,就使用 dataadapter.fill。如果有在 datatable 上定義的主鍵,dataadapter.fill 會根據(jù)主鍵進行新行匹配,并且當(dāng)更改到現(xiàn)有行時應(yīng)用服務(wù)器上的值。即使刷新之前修改了它們,刷新行的 rowstate 仍被設(shè)置為 unchanged。注意,如果沒有為 datatable 定義主鍵,dataadapter.fill 就用可能重復(fù)的主鍵值添加新行。 如果想用來自服務(wù)器的當(dāng)前值刷新表,并同時保留對表中的行所做的任何更改,必須首先用 dataadapter.fill 填充表,并填充一個新的 datatable,然后用 preservechanges 值 true 把 datatablemerge 到 dataset 中。 在 dataset 中搜索數(shù)據(jù) 在 dataset 中查詢與特定條件相匹配的行時,可以利用基于索引的查找提高搜索性能。當(dāng)把 primarykey 值賦給 datatable 時,會創(chuàng)建一個索引。當(dāng)給 datatable 創(chuàng)建 dataview 時,也會創(chuàng)建一個索引。下面是一些利用基于索引進行查找的技巧。
dataview 構(gòu)造 如果創(chuàng)建了 dataview,并且修改了 sort、rowfilter 或 rowstatefilter 屬性,dataview 就會為基礎(chǔ) datatable 中的數(shù)據(jù)建立索引。創(chuàng)建 dataview 對象時,要使用 dataview 構(gòu)造函數(shù),它用 sort、rowfilter 和 rowstatefilter 值作為構(gòu)造函數(shù)參數(shù)(與基礎(chǔ) datatable 一起)。結(jié)果是創(chuàng)建了一次索引。創(chuàng)建一個“空”dataview 并隨后設(shè)置 sort、rowfilter 或 rowstatefilter 屬性,會導(dǎo)致索引至少創(chuàng)建兩次。 分頁 ado.net 可以顯式控制從數(shù)據(jù)源中返回什么樣的數(shù)據(jù),以及在 dataset 中本地緩存多少數(shù)據(jù)。對查詢結(jié)果的分頁沒有唯一的答案,但下面有一些設(shè)計應(yīng)用程序時應(yīng)該考慮的技巧。
有關(guān)更多信息,請參閱 .net data access architecture guide。 用架構(gòu)填充 dataset 當(dāng)用數(shù)據(jù)填充 dataset 時,dataadapter.fill 方法使用 dataset 的現(xiàn)有架構(gòu),并使用從 selectcommand 返回的數(shù)據(jù)填充它。如果在 dataset 中沒有表名與要被填充的表名相匹配,fill 方法就會創(chuàng)建一個表。默認情況下,fill 僅定義列和列類型。 通過設(shè)置 dataadapter 的 missingschemaaction 屬性,可以重寫 fill 的默認行為。例如,要讓 fill 創(chuàng)建一個表架構(gòu),并且還包括主鍵信息、唯一約束、列屬性、是否允許為空、最大列長度、只讀列和自動增量的列,就要把 dataadapter.missingschemaaction 指定為 missingschemaaction.addwithkey?;蛘?,在調(diào)用 dataadapter.fill 前,可以調(diào)用 dataadapter.fillschema 來確保當(dāng)填充 dataset 時架構(gòu)已到位。 對 fillschema 的調(diào)用會產(chǎn)生一個到服務(wù)器的額外行程,用于檢索附加架構(gòu)信息。為了獲得最佳性能,需要在調(diào)用 fill 之前指定 dataset 的架構(gòu),或者設(shè)置 dataadapter 的 missingschemaaction。 使用 commandbuilder 的最佳實踐 假設(shè) selectcommand 執(zhí)行單一表 select,commandbuilder 就會以 dataadapter 的 selectcommand 屬性為基礎(chǔ)自動生成 dataadapter 的 insertcommand、updatecommand、和 deletecommand 屬性。下面是為獲得最佳性能而使用 commandbuilder 的一些技巧。
批處理 sql 語句 很多數(shù)據(jù)庫支持把多條命令合并或批處理成一條單一命令執(zhí)行。例如,sql server 使您可以用分號 (;) 分隔命令。把多條命令合并成單一命令,能減少到服務(wù)器的行程數(shù),并提高應(yīng)用程序的性能。例如,可以把所有預(yù)定的刪除在應(yīng)用程序中本地存儲起來,然后再發(fā)出一條批處理命令調(diào)用,從數(shù)據(jù)源刪除它們。 雖然這樣做確實能提高性能,但是,當(dāng)對 dataset 中的數(shù)據(jù)更新進行管理時,可能會增加應(yīng)用程序的復(fù)雜性。要保持簡單,可能要在 dataset 中為每個 datatable 創(chuàng)建一個 dataadapter。 用多個表填充 dataset 如果使用批處理 sql 語句檢索多個表并填充 dataset,第一個表用指定給 fill 方法的表名命名。后面的表用指定給 fill 方法的表名加上一個從 1 開始并且增量為 1 的數(shù)字命名。例如,如果運行下面的代碼: 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 表的數(shù)據(jù)放在名為 "customers" 的 datatable 中。來自 orders 表的數(shù)據(jù)放在名為 "customers1" 的 datatable 中。 填充完 dataset 之后,可以很容易地把 "customers1" 表的 tablename 屬性改為 "orders"。但是,后面的填充會導(dǎo)致 "customers" 表被重新填充,而 "orders" 表會被忽略,并創(chuàng)建另外一個 "customers1" 表。為了對這種情況作出補救,創(chuàng)建一個 datatablemapping,把 "customers1" 映射到 "orders",并為其他后面的表創(chuàng)建其他的表映射。例如: 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"); 使用 datareader 下面是一些使用 datareader 獲得最佳性能的技巧,同時還回答了一些關(guān)于使用 datareader 的常見問題。
二進制大對象 (blob) 用 datareader 檢索二進制大對象 (blob) 時,應(yīng)該把 commandbehavior.sequentialaccess 傳遞給 executereader 方法調(diào)用。因為 datareader 的默認行為是每次 read 都把整行加載到內(nèi)存,又因為 blob 值可能非常大,所以結(jié)果可能由于單個 blob 而使大量內(nèi)存被用光。sequentialaccess 將 datareader 的行為設(shè)置為只加載請求的數(shù)據(jù)。然后還可以使用 getbytes 或 getchars 控制每次加載多少數(shù)據(jù)。 記住,使用 sequentialaccess 時,不能不按順序訪問 datareader 返回的不同字段。也就是說,如果查詢返回三列,其中第三列是 blob,并且想訪問前兩列中的數(shù)據(jù),就必須在訪問 blob 數(shù)據(jù)之前先訪問第一列的值,然后訪問第二列的值。這是因為現(xiàn)在數(shù)據(jù)是順序返回的,并且 datareader 一旦讀過該數(shù)據(jù),該數(shù)據(jù)就不再可用。 有關(guān)如何在 ado.net 中訪問 blob 的詳細描述,請參閱 obtaining blob values from a database。 返回頁首
使用命令ado.net 提供了幾種命令執(zhí)行的不同方法以及優(yōu)化命令執(zhí)行的不同選項。下面包括一些技巧,它們是關(guān)于選擇最佳命令執(zhí)行以及如何提高執(zhí)行命令的性能。 使用 oledbcommand 的最佳實踐 不同 .net 框架數(shù)據(jù)提供程序之間的命令執(zhí)行被盡可能標(biāo)準(zhǔn)化了。但是,數(shù)據(jù)提供程序之間仍然存在差異。下面給出一些技巧,可微調(diào)用于 ole db 的 .net 框架數(shù)據(jù)提供程序的命令執(zhí)行。
使用 sqlcommand 的最佳實踐 使用 sqlcommand 執(zhí)行存儲過程的快速提示:如果調(diào)用存儲過程,將 sqlcommand 的 commandtype 屬性指定為 storedprocedure 的 commandtype。這樣通過將該命令顯式標(biāo)識為存儲過程,就不需要在執(zhí)行之前分析命令。 使用 prepare 方法 對于重復(fù)作用于數(shù)據(jù)源的參數(shù)化命令,command.prepare 方法能提高性能。prepare 指示數(shù)據(jù)源為多次調(diào)用優(yōu)化指定的命令。要想有效利用 prepare,需要徹底理解數(shù)據(jù)源是如何響應(yīng) prepare 調(diào)用的。對于一些數(shù)據(jù)源(例如 sql server 2000),命令是隱式優(yōu)化的,不必調(diào)用 prepare。對于其他(例如 sql server 7.0)數(shù)據(jù)源,prepare 會比較有效。 顯式指定架構(gòu)和元數(shù)據(jù) 只要用戶沒有指定元數(shù)據(jù)信息,ado.net 的許多對象就會推斷元數(shù)據(jù)信息。下面是一些示例:
但是,每次用到這些特性,都會有性能損失。建議將這些特性主要用于設(shè)計時和即席應(yīng)用程序中。在可能的情況下,顯式指定架構(gòu)和元數(shù)據(jù)。其中包括在 dataset 中定義表和列、定義 dataadapter 的 command 屬性、以及為 command 定義 parameter 信息。 executescalar 和 executenonquery 如果想返回像 count(*)、sum(price) 或 avg(quantity) 的結(jié)果那樣的單值,可以使用 command.executescalar。executescalar 返回第一行第一列的值,將結(jié)果集作為標(biāo)量值返回。因為單獨一步就能完成,所以 executescalar 不僅簡化了代碼,還提高了性能;要是使用 datareader 就需要兩步才能完成(即,executereader + 取值)。 使用不返回行的 sql 語句時,例如修改數(shù)據(jù)(例如insert、update 或 delete)或僅返回輸出參數(shù)或返回值,請使用 executenonquery。這避免了用于創(chuàng)建空 datareader 的任何不必要處理。 有關(guān)更多信息,請參閱 executing a command。 測試 null 如果表(在數(shù)據(jù)庫中)中的列允許為空,就不能測試參數(shù)值是否“等于”空。相反,需要寫一個 where 子句,測試列和參數(shù)是否都為空。下面的 sql 語句返回一些行,它們的 lastname 列等于賦給 @lastname 參數(shù)的值,或者 lastname 列和 @lastname 參數(shù)都為空。 select * from customers where ((lastname = @lastname) or (lastname is null and @lastname is null)) 把 null 作為參數(shù)值傳遞 對數(shù)據(jù)庫的命令中,當(dāng)把空值作為參數(shù)值發(fā)送時,不能使用 null(visual basic廬 .net 中為 nothing)。而需要使用 dbnull.value。例如: visual basic 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; 執(zhí)行事務(wù) ado.net 的事務(wù)模型已經(jīng)更改。在 ado 中,當(dāng)調(diào)用 starttransaction 時,調(diào)用之后的任何更新操作都被視為是事務(wù)的一部分。但是,在 ado.net 中,當(dāng)調(diào)用 connection.begintransaction 時,會返回一個 transaction 對象,需要把它與 command 的 transaction 屬性聯(lián)系起來。這種設(shè)計可以在一個單一連接上執(zhí)行多個根事務(wù)。如果未將 command.transaction 屬性設(shè)置為一個針對相關(guān)的 connection 而啟動的 transaction,那么 command 就會失敗并引發(fā)異常。 即將發(fā)布的 .net 框架將使您可以在現(xiàn)有的分布式事務(wù)中手動登記。這對于對象池方案來說很理想;在該方案中,一個池對象打開一次連接,但是在多個獨立的事務(wù)中都涉及到該對象。.net 框架 1.0 發(fā)行版中這一功能并不可用。 有關(guān)事務(wù)的更多信息,請參閱 performing transactions 以及 .net data access architecture guide。 返回頁首
使用連接高性能應(yīng)用程序與使用中的數(shù)據(jù)源保持最短時間的連接,并且利用性能增強技術(shù),例如連接池。下面的主題提供一些技巧,有助于在使用 ado.net 連接到數(shù)據(jù)源時獲得更好的性能。 連接池 用于 odbc 的 sql server、ole db 和 .net 框架數(shù)據(jù)提供程序隱式緩沖連接。通過在連接字符串中指定不同的屬性值,可以控制連接池的行為。有關(guān)如何控制連接池的行為的詳細信息,請參閱 connection pooling for the sql server .net data provider 和 connection pooling for the ole db .net data provider。 用 dataadapter 優(yōu)化連接 dataadapter 的 fill 和 update 方法在連接關(guān)閉的情況下自動打開為相關(guān)命令屬性指定的連接。如果 fill 或 update 方法打開了連接,fill 或 update 將在操作完成的時候關(guān)閉它。為了獲得最佳性能,僅在需要時將與數(shù)據(jù)庫的連接保持為打開。同時,減少打開和關(guān)閉多操作連接的次數(shù)。 如果只執(zhí)行單個的 fill 或 update 方法調(diào)用,建議允許 fill 或 update 方法隱式打開和關(guān)閉連接。如果對 fill 和/或 update 調(diào)用有很多,建議顯式打開連接,調(diào)用 fill 和/或 update,然后顯式關(guān)閉連接。 另外,當(dāng)執(zhí)行事務(wù)時,顯式地在開始事務(wù)之前打開連接,并在提交之后關(guān)閉連接。例如: 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(); } 始終關(guān)閉 connection 和 datareader 完成對 connection 或 datareader 對象的使用后,總是顯式地關(guān)閉它們。盡管垃圾回收最終會清除對象并因此釋放連接和其他托管資源,但垃圾回收僅在需要時執(zhí)行。因此,確保任何寶貴的資源被顯式釋放仍然是您的責(zé)任。并且,沒有顯式關(guān)閉的 connections 可能不會返回到池中。例如,一個超出作用范圍卻沒有顯式關(guān)閉的連接,只有當(dāng)池大小達到最大并且連接仍然有效時,才會被返回到連接池中。 注 不要在類的 finalize 方法中對 connection、datareader 或任何其他托管對象調(diào)用 close 或 dispose。最后完成的時候,僅釋放類自己直接擁有的非托管資源。如果類沒有任何非托管資源,就不要在類定義中包含 finalize 方法。 在 c# 中使用 "using" 語句 對于 c# 程序員來說,確保始終關(guān)閉 connection 和 datareader 對象的一個方便的方法就是使用 using 語句。using 語句在離開自己的作用范圍時,會自動調(diào)用被“使用”的對象的 dispose。例如: //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 語句不能用于 microsoft廬 visual basic廬 .net。 避免訪問 oledbconnection.state 屬性 如果連接已經(jīng)打開,oledbconnection.state 屬性會對 dbprop_connectionstatus 屬性的 datasourceinfo 屬性集執(zhí)行本地 ole db 調(diào)用 idbproperties.getproperties,這可能會導(dǎo)致對數(shù)據(jù)源的往返行程。也就是說,檢查 state 屬性的代價可能很高。所以僅在需要時檢查 state 屬性。如果需要經(jīng)常檢查該屬性,監(jiān)聽 oledbconnection 的 statechange 事件可能會使應(yīng)用程序的性能好一些。有關(guān) statechange 事件的詳細信息,請參閱 working with connection events。 返回頁首
與 xml 集成ado.net 在 dataset 中提供了廣泛的 xml 集成,并公開了 sql server 2000 及其更高版本提供的部分 xml 功能。還可以使用 sqlxml 3.0 廣泛地訪問 sql server 2000 及其更高版本中的 xml 功能。下面是使用 xml 和 ado.net 的技巧和信息。 dataset 和 xml dataset 與 xml 緊密集成,并提供如下功能:
注 可以使用這種同步把 xml 功能(例如,xpath 查詢和 xslt 轉(zhuǎn)換)應(yīng)用到 dataset 中的數(shù)據(jù),或者在保留原始 xml 保真度的前提下為 xml 文檔中數(shù)據(jù)的全部或其中一個子集提供關(guān)系視圖。 關(guān)于 dataset 提供的 xml 功能的詳細信息,請參閱 xml and the dataset。 架構(gòu)推斷 從 xml 文件加載 dataset 時,可以從 xsd 架構(gòu)加載 dataset 架構(gòu),或者在加載數(shù)據(jù)前預(yù)定義表和列。如果沒有可用的 xsd 架構(gòu),而且不知道為 xml 文件的內(nèi)容定義哪些表和列,就可以在 xml 文檔結(jié)構(gòu)的基礎(chǔ)上對架構(gòu)進行推斷。 架構(gòu)推斷作為遷移工具很有用,但應(yīng)只限于設(shè)計階段應(yīng)用程序,這是由于推斷處理有如下限制。
有關(guān)更多信息,請參閱 inferring dataset relational structure from xml。 用于 xml 查詢的 sql server 如果正從 sql server 2000 for xml 返回查詢結(jié)果,可以讓用于 sql server 的 .net 框架數(shù)據(jù)提供程序使用 sqlcommand.executexmlreader 方法直接創(chuàng)建一個 xmlreader。 sqlxml 托管類 .net 框架中有一些類,公開用于 sql server 2000 的 xml 的功能。這些類可在 microsoft.data.sqlxml 命名空間中找到,它們添加了執(zhí)行 xpath 查詢和 xml 模板文件以及把 xslt 轉(zhuǎn)換應(yīng)用到數(shù)據(jù)的能力。 sqlxml 托管類包含在用于 microsoft sql server 2000 的 xml (sqlxml 2.0) 發(fā)行版中,可從 xml for microsoft sql server 2000 web release 2 (sqlxml 2.0) ??μ?。 返回頁首
更多有用的技巧下面是一些編寫 ado.net 代碼時的通用技巧。 避免自動增量值沖突 就像大多數(shù)數(shù)據(jù)源一樣,dataset 使您可標(biāo)識那些添加新行時自動對其值進行遞增的列。在 dataset 中使用自動增量的列時,如果自動增量的列來自數(shù)據(jù)源,可避免添加到 dataset 的行和添加到數(shù)據(jù)源的行之間本地編號沖突。 例如,考慮一個表,它的主鍵列 customerid 是自動增量的。兩個新的客戶信息行添加到表中,并接收到自動增量的 customerid 值 1 和 2。然后,只有第二個客戶行被傳遞給 dataadapter 的方法 update,新添加的行在數(shù)據(jù)源接收到一個自動增量的 customerid 值 1,與 dataset 中的值 2 不匹配。當(dāng) dataadapter 用返回值填充表中第二行時,就會出現(xiàn)約束沖突,因為第一個客戶行已經(jīng)使用了 customerid 值 1。 要避免這種情況,建議在使用數(shù)據(jù)源上自動增量的列以及 dataset 上自動增量的列時,把 dataset 中的列創(chuàng)建為 autoincrementstep 值等于 -1 并且 autoincrementseed 值等于 0,另外,還要確保數(shù)據(jù)源生成的自動增量標(biāo)識值從 1 開始,并且以正階值遞增。因此,dataset 為自動增量值生成負數(shù),與數(shù)據(jù)源生成的正自動增量值不沖突。另外一個選擇是使用 guid 類型的列,而不是自動增量的列。生成 guid 值的算法應(yīng)該永遠不會使數(shù)據(jù)源中生成的 guid 值與 dataset 中生成的 guid 值一樣。 如果自動增量的列只是用作唯一值,而且沒有任何意義,就考慮使用 guid 代替自動增量的列。它們是唯一的,并且避免了使用自動增量的列所必需的額外工作。 有關(guān)從數(shù)據(jù)源檢索自動增量的列值的示例,請參閱 retrieving identity or autonumber values。 檢查開放式并發(fā)沖突 按照設(shè)計,由于 dataset 是與數(shù)據(jù)源斷開的,所以,當(dāng)多個客戶端在數(shù)據(jù)源上按照開放式并發(fā)模型更新數(shù)據(jù)時,需要確保應(yīng)用程序避免沖突。 在測試開放式并發(fā)沖突時有幾項技術(shù)。一項技術(shù)涉及在表中包含時間戳列。另外一項技術(shù)是,驗證一行中所有列的原始值是否仍然與通過在 sql 語句中使用 where 子句進行測試時在數(shù)據(jù)庫中找到的值相匹配。 有關(guān)包含代碼示例的該主題的詳細討論,請參閱 optimistic concurrency。 多線程編程 ado.net 對性能、吞吐量和可伸縮性進行優(yōu)化。因此,ado.net 對象不鎖定資源,并且必須只用于單線程。一個例外是 dataset,它對多個閱讀器是線程安全的。但是,在寫的時候需要把 dataset 鎖定。 僅在需要的時候才用 com interop 訪問 ado ado.net 的設(shè)計目的是成為許多應(yīng)用程序的最佳解決方案。但是,有些應(yīng)用程序需要只有使用 ado 對象才有的功能,例如,ado 多維 (adomd)。在這些情況下,應(yīng)用程序可以用 com interop 訪問 ado。注意使用 com interop 訪問具有 ado 的數(shù)據(jù)會導(dǎo)致性能降低。在設(shè)計應(yīng)用程序時,首先在實現(xiàn)用 com interop 訪問 ado 的設(shè)計之前,先確定 ado.net 是否滿足設(shè)計需求 |
|