鷹翔宇空

          學(xué)習(xí)和生活

          BlogJava 首頁 新隨筆 聯(lián)系 聚合 管理
            110 Posts :: 141 Stories :: 315 Comments :: 1 Trackbacks

          引自:http://www.microsoft.com/china/msdn/library/architecture/architecture/architecturetopic/SCArchDeGuide/Chapter1Introduction.mspx

          第 2 章 — 處理數(shù)據(jù)

          發(fā)布日期: 8/20/2004 | 更新日期: 8/20/2004

          智能客戶端體系結(jié)構(gòu)與設(shè)計(jì)指南

          David Hill, Brenton Webster, Edward A. Jezierski, Srinath Vasireddy and Mohammad Al-Sabt, Microsoft Corporation; Blaine Wastell, Ascentium Corporation; Jonathan Rasmusson and Paul Gale, ThoughtWorks; and Paul Slater, Wadeware LLC

          相關(guān)鏈接

          Microsoft? patterns & practiceshttp://www.microsoft.com/resources/practices/default.mspx

          Application Architecture for .NET: Designing Applications and Serviceshttp://msdn.microsoft.com/library/en-us/dnbda/html/distapp.asp

          摘要:本章分析在客戶端處理數(shù)據(jù)時的各種注意事項(xiàng),包括數(shù)據(jù)緩存、數(shù)據(jù)并發(fā)以及數(shù)據(jù)集和 Windows 窗體數(shù)據(jù)綁定的使用。

          *
          本頁內(nèi)容
          數(shù)據(jù)類型 數(shù)據(jù)類型
          緩存數(shù)據(jù) 緩存數(shù)據(jù)
          數(shù)據(jù)并發(fā) 數(shù)據(jù)并發(fā)
          使用 ADO.NET 數(shù)據(jù)集來管理數(shù)據(jù) 使用 ADO.NET 數(shù)據(jù)集來管理數(shù)據(jù)
          Windows 窗體數(shù)據(jù)綁定 Windows 窗體數(shù)據(jù)綁定
          小結(jié) 小結(jié)

          在智能客戶端中,可在客戶端上使用應(yīng)用程序數(shù)據(jù)。要使您的智能客戶端有效工作,很重要的一點(diǎn)是對該數(shù)據(jù)進(jìn)行適當(dāng)?shù)墓芾恚源_保其有效、一致和安全。

          可以通過服務(wù)器端應(yīng)用程序(例如,通過 Web 服務(wù))向客戶端提供應(yīng)用程序數(shù)據(jù),或者應(yīng)用程序可以使用它自己的本地?cái)?shù)據(jù)。如果數(shù)據(jù)是由應(yīng)用程序提供的,則智能客戶端應(yīng)用程序可以緩存數(shù)據(jù)以改善性能或者支持脫機(jī)使用。在這種情況下,您需要決定客戶端應(yīng)用程序應(yīng)該如何處理就該服務(wù)器而言已經(jīng)過時的數(shù)據(jù)。

          如果智能客戶端應(yīng)用程序提供在本地修改數(shù)據(jù)的能力,則必須在以后將客戶端更改與服務(wù)器端應(yīng)用程序進(jìn)行同步。在這種情況下,您必須決定客戶端應(yīng)用程序如何處理數(shù)據(jù)沖突,以及如何跟蹤需要發(fā)送到服務(wù)器的更改。

          在設(shè)計(jì)您的智能客戶端應(yīng)用程序時,您需要認(rèn)真考慮這些問題以及其他許多問題。本章分析了在客戶端上處理數(shù)據(jù)時的各種注意事項(xiàng),包括:

          ?

          數(shù)據(jù)類型。

          ?

          緩存數(shù)據(jù)。

          ?

          數(shù)據(jù)并發(fā)。

          ?

          使用 ADO.NET 數(shù)據(jù)集來管理數(shù)據(jù)。

          ?

          Windows 窗體數(shù)據(jù)綁定。

          本章未討論其他許多與處理數(shù)據(jù)有關(guān)的問題。具體說來,在第 5 章:安全性注意事項(xiàng)中討論了數(shù)據(jù)處理安全性問題,在第 4 章:偶爾連接的智能客戶端中討論了脫機(jī)注意事項(xiàng)。

          數(shù)據(jù)類型

          智能客戶端通常必須處理兩種類別的數(shù)據(jù):

          ?

          只讀引用數(shù)據(jù)

          ?

          瞬態(tài)數(shù)據(jù)

          通常情況下,需要以不同的方式處理這些類型的數(shù)據(jù),因此更詳細(xì)地分析一下每種類型將是很有用的。

          只讀引用數(shù)據(jù)

          只讀引用數(shù)據(jù)是不會由客戶端更改并且被客戶端用于引用目的的數(shù)據(jù)。因此,從客戶端的觀點(diǎn)看來,該數(shù)據(jù)為只讀數(shù)據(jù),并且客戶端不會對其執(zhí)行更新、插入或刪除操作。只讀引用數(shù)據(jù)很容易在客戶端上進(jìn)行緩存。引用數(shù)據(jù)在智能客戶端應(yīng)用程序中具有許多種用途,包括:

          ?

          提供靜態(tài)引用或查找數(shù)據(jù)。這方面的示例包括產(chǎn)品信息、價格表、發(fā)貨選項(xiàng)和價格。

          ?

          支持?jǐn)?shù)據(jù)驗(yàn)證,允許檢查用戶輸入數(shù)據(jù)的正確性。示例有針對交貨時間表檢查輸入的日期。

          ?

          幫助與遠(yuǎn)程服務(wù)進(jìn)行通訊。示例在本地將用戶選擇轉(zhuǎn)化為產(chǎn)品 ID,然后將該信息發(fā)送到 Web 服務(wù)。

          ?

          呈現(xiàn)數(shù)據(jù)。示例包括呈現(xiàn)幫助文本或用戶界面標(biāo)簽。

          通過在客戶端上存儲和使用引用數(shù)據(jù),您可以減少需要從客戶端傳輸?shù)椒?wù)器的數(shù)據(jù)量,改善應(yīng)用程序的性能,幫助啟用脫機(jī)功能,并提供早期數(shù)據(jù)驗(yàn)證以提高應(yīng)用程序的可用性。

          盡管客戶端無法更改只讀引用數(shù)據(jù),但可以在服務(wù)器上進(jìn)行更改(例如,由管理員或超級用戶更改)。您需要確定在發(fā)生數(shù)據(jù)更改時用于更新客戶端的策略。此類策略可能涉及到在發(fā)生更改時將更改推到客戶端上,或者按照特定的時間間隔或在客戶端上執(zhí)行某些操作之前從服務(wù)器拉入更改。但是,因?yàn)閿?shù)據(jù)在客戶端上是只讀的,所以您無須跟蹤客戶端更改。這就簡化了需要對只讀引用數(shù)據(jù)進(jìn)行處理的方式。

          瞬態(tài)數(shù)據(jù)

          瞬態(tài)數(shù)據(jù)既可以在服務(wù)器上更改,也可以在客戶端上更改。通常情況下,瞬態(tài)數(shù)據(jù)作為用戶輸入和操作的直接或間接結(jié)果而發(fā)生更改。在此情況下,在客戶端或服務(wù)器進(jìn)行的更改都需要在某個時刻進(jìn)行同步。這種類型的數(shù)據(jù)在智能客戶端中具有許多種用途,包括:

          ?

          添加新信息。示例包括添加銀行業(yè)務(wù)交易或客戶詳細(xì)信息。

          ?

          修改現(xiàn)有信息。示例更新客戶詳細(xì)信息。

          ?

          刪除現(xiàn)有信息。示例從數(shù)據(jù)庫中刪除客戶。

          在智能客戶端處理瞬態(tài)數(shù)據(jù)的最困難的方面之一在于,這些數(shù)據(jù)通常可能在多個客戶端上同時進(jìn)行修改。當(dāng)數(shù)據(jù)非常不穩(wěn)定時,該問題將惡化,因?yàn)樗龈母锌赡芑ハ鄾_突。

          您需要跟蹤您對瞬態(tài)數(shù)據(jù)進(jìn)行的任何客戶端更改。在與服務(wù)器同步數(shù)據(jù)并且已經(jīng)解決任何沖突之前,您不應(yīng)該認(rèn)為瞬態(tài)數(shù)據(jù)已被確認(rèn)。您應(yīng)該非常小心以避免依賴未確認(rèn)的數(shù)據(jù)進(jìn)行重要決策,或者在未認(rèn)真考慮如何保證數(shù)據(jù)一致性(甚至在同步失敗時)的情況下使用該數(shù)據(jù)作為其他本地更改的基礎(chǔ)。

          有關(guān)圍繞脫機(jī)時處理數(shù)據(jù)的問題以及如何處理數(shù)據(jù)同步的詳細(xì)信息,請參閱第 4 章:偶爾連接的智能客戶端

          返回頁首返回頁首

          緩存數(shù)據(jù)

          智能客戶端通常需要在本地緩存數(shù)據(jù)(無論是只讀引用數(shù)據(jù)還是瞬態(tài)數(shù)據(jù))。通過緩存數(shù)據(jù),有可能改善應(yīng)用程序的性能并提供脫機(jī)工作所需的數(shù)據(jù)。但是,您需要認(rèn)真考慮在客戶端緩存哪些數(shù)據(jù)、如何管理這些數(shù)據(jù)以及可以在哪個上下文中使用這些數(shù)據(jù)。

          要啟用數(shù)據(jù)緩存,您的智能客戶端應(yīng)用程序應(yīng)該實(shí)現(xiàn)某種形式的緩存基礎(chǔ)結(jié)構(gòu),以便透明地處理數(shù)據(jù)緩存細(xì)節(jié)。您的緩存基礎(chǔ)結(jié)構(gòu)應(yīng)該包括下列緩存機(jī)制中的一種或兩種:

          ?

          短期數(shù)據(jù)緩存。在內(nèi)存中緩存數(shù)據(jù)對性能有益,但不能持久,因此您可能需要在重新運(yùn)行應(yīng)用程序時從源拉入數(shù)據(jù)。這樣做可能會妨礙您的應(yīng)用程序脫機(jī)操作。

          ?

          長期數(shù)據(jù)緩存。通過在持久性媒體(如獨(dú)立存儲或本地文件系統(tǒng))中緩存數(shù)據(jù),可以在沒有連接到服務(wù)器時使用應(yīng)用程序。您可以選擇將長期存儲與短期存儲結(jié)合起來以改善性能。

          無論您采用哪種緩存機(jī)制,都應(yīng)該確保僅將用戶有權(quán)訪問的數(shù)據(jù)提供給客戶端。而且,在客戶端緩存的敏感數(shù)據(jù)要求進(jìn)行認(rèn)真處理以確保它的安全。因此,您可能需要在將數(shù)據(jù)傳輸?shù)娇蛻舳艘约霸诳蛻舳舜鎯?shù)據(jù)時,對數(shù)據(jù)進(jìn)行加密。有關(guān)詳細(xì)信息,請參閱第 5 章:安全性注意事項(xiàng)中的“處理敏感數(shù)據(jù)”。

          當(dāng)您設(shè)計(jì)智能客戶端以支持?jǐn)?shù)據(jù)緩存時,您應(yīng)該考慮為客戶端提供一種請求新數(shù)據(jù)的機(jī)制,而無論緩存的狀態(tài)如何。這意味著您可以確保應(yīng)用程序隨時能夠執(zhí)行新的事務(wù),并且不會使用過時的數(shù)據(jù)。您還可以將客戶端配置為預(yù)先獲取數(shù)據(jù),以便減少在緩存數(shù)據(jù)到期時處于脫機(jī)狀態(tài)的風(fēng)險(xiǎn)。

          只要有可能,您都應(yīng)該將某種形式的元數(shù)據(jù)與該數(shù)據(jù)關(guān)聯(lián)起來,以便使客戶端能夠以聰明的方式管理這些數(shù)據(jù)。此類元數(shù)據(jù)可用于指定數(shù)據(jù)的標(biāo)識和任何約束,或者指定所需的與該數(shù)據(jù)關(guān)聯(lián)的行為。您的客戶端緩存基礎(chǔ)結(jié)構(gòu)應(yīng)該消耗該元數(shù)據(jù),并且使用它來適當(dāng)處理緩存的數(shù)據(jù)。

          客戶端緩存的所有數(shù)據(jù)都應(yīng)該是可以唯一標(biāo)識的(例如,通過版本號或日期戳),以便在確定是否需要更新數(shù)據(jù)時,可以正確地識別相應(yīng)的數(shù)據(jù)。這樣,您的緩存基礎(chǔ)結(jié)構(gòu)就能夠詢問服務(wù)器它所具有的數(shù)據(jù)當(dāng)前是否有效,并且確定是否需要進(jìn)行更新。

          元數(shù)據(jù)還可以用來指定與緩存數(shù)據(jù)的使用和處理相關(guān)的約束或行為。示例包括:

          ?

          時間約束。這些約束指定可以使用緩存數(shù)據(jù)的時間或日期范圍。當(dāng)該數(shù)據(jù)過時或到期時,可以將其從緩存中丟棄,或者通過從服務(wù)器獲取最新數(shù)據(jù)來自動刷新數(shù)據(jù)。在某些情況下,合適的做法可能是讓客戶端使用過時的引用數(shù)據(jù),并且在與服務(wù)器進(jìn)行同步時將過時數(shù)據(jù)映射到最新數(shù)據(jù)。

          ?

          地理約束。某些數(shù)據(jù)可能僅適用于特定地區(qū)。例如,您可能對于不同的地點(diǎn)有不同的價格表。可以使用您的緩存基礎(chǔ)結(jié)構(gòu)分別針對不同的地點(diǎn)來訪問和存儲數(shù)據(jù)。

          ?

          安全性要求。可以將專門提供給特定用戶的數(shù)據(jù)加密,以確保只有相應(yīng)的用戶可以訪問這些數(shù)據(jù)。在此情況下,所提供的數(shù)據(jù)已經(jīng)進(jìn)行了加密,并且用戶必須向緩存基礎(chǔ)結(jié)構(gòu)提供憑據(jù)以便對數(shù)據(jù)進(jìn)行解密。

          ?

          業(yè)務(wù)規(guī)則。您可能擁有與緩存數(shù)據(jù)關(guān)聯(lián)的業(yè)務(wù)規(guī)則,用來規(guī)定如何使用這些數(shù)據(jù)。例如,您的緩存基礎(chǔ)結(jié)構(gòu)可能考慮用戶的角色,以便確定向該用戶提供哪些數(shù)據(jù)以及如何處理這些數(shù)據(jù)。

          您的緩存基礎(chǔ)結(jié)構(gòu)可以通過與數(shù)據(jù)關(guān)聯(lián)的元數(shù)據(jù)來適當(dāng)?shù)靥幚磉@些數(shù)據(jù),從而使您的應(yīng)用程序無須關(guān)心數(shù)據(jù)緩存問題或?qū)崿F(xiàn)細(xì)節(jié)。您可以在引用數(shù)據(jù)本身內(nèi)部傳遞與這些數(shù)據(jù)關(guān)聯(lián)的元數(shù)據(jù),或者您可以使用帶外機(jī)制。用于將元數(shù)據(jù)傳輸?shù)娇蛻舳说拇_切機(jī)制取決于您的應(yīng)用程序與網(wǎng)絡(luò)服務(wù)的通訊方式。當(dāng)使用 Web 服務(wù)時,利用 SOAP 頭將元數(shù)據(jù)傳輸?shù)娇蛻舳耸且环N很好的解決方案。

          只讀引用數(shù)據(jù)與瞬態(tài)數(shù)據(jù)之間存在的區(qū)別有時意味著您需要使用兩個緩存,一個用于引用數(shù)據(jù),一個用于瞬態(tài)數(shù)據(jù)。引用數(shù)據(jù)在客戶端是只讀的,并且不需要回過頭來與服務(wù)器進(jìn)行同步,但它的確需要偶爾進(jìn)行刷新以反映在服務(wù)器上進(jìn)行的任何更改和更新。

          瞬態(tài)數(shù)據(jù)既可以在服務(wù)器上更改,也可以在客戶端上更改。既然有時在客戶端更新緩存中的數(shù)據(jù),有時在服務(wù)器更新,有時在這兩個位置更新,那么對客戶端數(shù)據(jù)進(jìn)行的更改需要在某個時刻與服務(wù)器進(jìn)行同步。如果數(shù)據(jù)同時在服務(wù)器上發(fā)生了更改,則會發(fā)生數(shù)據(jù)沖突,需要對其進(jìn)行適當(dāng)?shù)奶幚怼?/P>

          要幫助確保維持?jǐn)?shù)據(jù)一致性,并且避免不適當(dāng)?shù)厥褂脭?shù)據(jù),您應(yīng)該小心地跟蹤您在客戶端對瞬態(tài)數(shù)據(jù)進(jìn)行的任何更改。在成功地與服務(wù)器進(jìn)行同步或確認(rèn)之前,此類更改是未提交的 或暫定的。

          您應(yīng)該對您的智能客戶端應(yīng)用程序進(jìn)行適當(dāng)?shù)脑O(shè)計(jì),以使其能夠區(qū)分已經(jīng)成功地與服務(wù)器進(jìn)行同步的數(shù)據(jù)和仍然暫定的數(shù)據(jù)。這一區(qū)分過程可以幫助應(yīng)用程序更加容易地檢測和處理數(shù)據(jù)沖突。而且,您可能需要禁止應(yīng)用程序或用戶基于暫定數(shù)據(jù)進(jìn)行重要決策或者啟動重要操作。在將此類數(shù)據(jù)與服務(wù)器進(jìn)行同步之前,不應(yīng)該依賴它們。通過使用適當(dāng)?shù)木彺婊A(chǔ)結(jié)構(gòu),可以跟蹤暫定數(shù)據(jù)和已經(jīng)確認(rèn)的數(shù)據(jù)。

          緩存應(yīng)用程序塊(Caching Application Block)

          緩存應(yīng)用程序塊是一個 Microsoft? .NET 框架擴(kuò)展,它使開發(fā)人員可以容易地緩存來自服務(wù)提供程序的數(shù)據(jù)。生成和設(shè)計(jì)它的目的是將 Microsoft 建議的緩存準(zhǔn)則封裝在 .NET 框架應(yīng)用程序中,如位于 http://msdn.microsoft.com/library/en-us/dnbda/html/CachingArch.asp 的 Caching Architecture Guide for .NET Framework Applicationss 所述。

          緩存塊的總體體系結(jié)構(gòu)如圖 2.1 所示。


          2.1 緩存塊工作流

          緩存工作流包含下列步驟:

          1.

          客戶端或服務(wù)代理向 CacheManager 發(fā)出對緩存數(shù)據(jù)項(xiàng)的請求。

          2.

          如果該數(shù)據(jù)項(xiàng)已被緩存,則 CacheManager 會從存儲中檢索該項(xiàng),并將其作為 CacheItem 對象返回。如果該項(xiàng)尚未緩存,則會通知客戶端。

          3.

          在從服務(wù)提供程序檢索未緩存的數(shù)據(jù)之后,客戶端將該數(shù)據(jù)發(fā)送給 CacheManagerCacheManager 會將一個簽名(即,元數(shù)據(jù))如密鑰、到期時間或優(yōu)先級等添加到該數(shù)據(jù)項(xiàng)中,并將其加載到緩存中。

          4.

          CacheService 監(jiān)控 CacheItems 的生存期。當(dāng) CacheItem 到期時,CacheService 會刪除它并根據(jù)情況調(diào)用回調(diào)委托。

          5.

          CacheService 還可以將所有數(shù)據(jù)項(xiàng)從緩存中清除出去。

          緩存塊提供了多種緩存到期選項(xiàng),如表 2.1 所述。

          2.1 緩存塊到期選項(xiàng)

          說明

          AbsoluteTime

          用于設(shè)置到期時間的絕對時間。

          ExtendedFormatTime

          用于基于表達(dá)式(如 every minute 或 every Monday)設(shè)置到期時間。

          FileDependency

          用于基于文件是否更改來設(shè)置到期時間。

          SlidingTime

          用于設(shè)置項(xiàng)的生存期,方法是基于項(xiàng)的上次訪問時間來指定到期時間。

          下列存儲機(jī)制可供緩存塊使用:

          ?

          內(nèi)存映射文件 (MMF)。MMF 最適合于基于客戶端的高性能緩存方案。您可以使用 MMF 來開發(fā)可在同一臺計(jì)算機(jī)中的多個應(yīng)用程序域和進(jìn)程之間共享的緩存。.NET 框架不支持 MMF,因此 MMF 緩存的任何實(shí)現(xiàn)都以非托管代碼的形式運(yùn)行,并且不會從任何 .NET 框架功能中受益,包括內(nèi)存管理功能(如垃圾回收)和安全性功能(如代碼訪問安全性)。

          ?

          Singleton 對象。可以使用 .NET 遠(yuǎn)程處理 singleton 對象來緩存可在一臺或多臺計(jì)算機(jī)中的進(jìn)程之間共享的數(shù)據(jù)。方法是使用通過 .NET 遠(yuǎn)程處理為多個客戶端提供服務(wù)的 singleton 對象來實(shí)現(xiàn)緩存服務(wù)。單例緩存的實(shí)現(xiàn)很簡單,但它缺乏基于 Microsoft SQL Server? 的解決方案所提供的性能和可伸縮性。

          ?

          Microsoft SQL Server 2000 數(shù)據(jù)庫。SQL Server 2000 存儲最適合于應(yīng)用程序要求具有高持續(xù)性或者您需要緩存大量數(shù)據(jù)的場合。因?yàn)榫彺娣?wù)需要通過網(wǎng)絡(luò)訪問 SQL Server,并且使用數(shù)據(jù)庫查詢檢索數(shù)據(jù),所以數(shù)據(jù)訪問的速度相對比較慢。

          ?

          Microsoft SQL Server 桌面引擎 (MSDE)。MSDE 是 SQL Server 2000 的輕型數(shù)據(jù)庫替代產(chǎn)品。它提供了可靠性和安全性功能,但具有比 SQL Server 更小的客戶端足跡,因此它需要較少的設(shè)置和配置。因?yàn)?MSDE 支持 SQL,所以開發(fā)人員可以得到數(shù)據(jù)庫的很多功能。如有必要,您可以將 MSDE 數(shù)據(jù)庫遷移到 SQL Server 數(shù)據(jù)庫。

          返回頁首返回頁首

          數(shù)據(jù)并發(fā)

          正如前面所提到的,使用智能客戶端的一個問題是:在將任何客戶端更改與服務(wù)器進(jìn)行同步之前,服務(wù)器上保存的數(shù)據(jù)可能發(fā)生更改。您需要采用某種機(jī)制來確保在對數(shù)據(jù)進(jìn)行同步時,數(shù)據(jù)沖突能夠得到適當(dāng)?shù)奶幚恚⑶易詈蟮玫降臄?shù)據(jù)是一致和正確的。數(shù)據(jù)能夠由多個客戶端進(jìn)行更新的能力稱為“數(shù)據(jù)并發(fā)”。

          您可以使用兩種方法來處理數(shù)據(jù)并發(fā):

          ?

          保守式并發(fā)。保守式并發(fā)允許一個客戶端保持?jǐn)?shù)據(jù)上的鎖,以禁止任何其他客戶端修改數(shù)據(jù),直至客戶端自己的更改完成為止。在這種情況下,如果另一個客戶端嘗試修改數(shù)據(jù),則在鎖的擁有者釋放該鎖之前,這些嘗試將失敗或者被阻止。

          ?

          保守式并發(fā)可能有問題,因?yàn)閱蝹€用戶或客戶端可能由于疏忽而長時間地保持鎖定。所以,該鎖可能會妨礙重要資源(如數(shù)據(jù)庫行或文件)及時得到釋放,從而嚴(yán)重影響應(yīng)用程序的可伸縮性和可用性。但是,當(dāng)您需要完全控制對重要資源所做的更改時,保守式并發(fā)可能是適當(dāng)?shù)摹U堊⒁猓绻目蛻舳艘摍C(jī)工作,則不能使用這種并發(fā),因?yàn)榭蛻舳藷o法對數(shù)據(jù)加鎖。

          ?

          開放式并發(fā)。開放式并發(fā)不會鎖定數(shù)據(jù)。要判斷是否實(shí)際需要更新,可以將原始數(shù)據(jù)隨更新請求和已更改的數(shù)據(jù)一起發(fā)送。隨后,將針對當(dāng)前數(shù)據(jù)檢查原始數(shù)據(jù),以查看是否同時對原始數(shù)據(jù)進(jìn)行了更新。如果原始數(shù)據(jù)和當(dāng)前數(shù)據(jù)匹配,則執(zhí)行更新;否則,拒絕請求,并產(chǎn)生開放式失敗。要優(yōu)化該過程,您可以在數(shù)據(jù)中使用時間戳或更新計(jì)數(shù)器,而不必發(fā)送原始數(shù)據(jù),此時只需要檢查時間戳或計(jì)數(shù)器。

          開放式并發(fā)提供了一種良好的機(jī)制,可用來更新不會非常頻繁更改的主數(shù)據(jù),如客戶的電話號碼或地址。開放式并發(fā)允許每個人讀取數(shù)據(jù),在發(fā)生更新的概率小于讀取操作的情況下,開放式失敗的風(fēng)險(xiǎn)或許是可以接受的。在數(shù)據(jù)頻繁更改以及開放式更新可能經(jīng)常失敗的情況下,開放式并發(fā)可能并不適合。

          在大多數(shù)智能客戶端方案(包括客戶端將要脫機(jī)工作的方案)中,開放式并發(fā)是正確的方法,因?yàn)樗试S多個客戶端同時使用數(shù)據(jù),而不會不必要地鎖定數(shù)據(jù)和影響所有其他客戶端。

          有關(guān)開放式和保守式并發(fā)的詳細(xì)信息,請參閱 .NET Framework Developer's Guide 中的“Optimistic Concurrency”,網(wǎng)址為:http://msdn.microsoft.com/library/en-us/cpguide/html/cpconoptimisticconcurrency.asp

          返回頁首返回頁首

          使用 ADO.NET 數(shù)據(jù)集來管理數(shù)據(jù)

          DataSet 是一個表示一個或多個關(guān)系數(shù)據(jù)庫表的對象。數(shù)據(jù)集在斷開連接的緩存中存儲數(shù)據(jù)。DataSets的結(jié)構(gòu)與關(guān)系數(shù)據(jù)庫類似:它公開了一個由表、行和列組成的層次結(jié)構(gòu)對象模型。另外,它還包含為DataSets定義的約束和關(guān)系。

          ADO.NET DataSet 包含零個或更多個由 DataTable 對象表示的表組成的集合。DataTableSystem.Data 命名空間中定義,并且表示單個由內(nèi)存駐留數(shù)據(jù)組成的表。它包含由 DataColumnCollection 表示的列和由 ConstraintCollection 表示的約束組成的集合,它們共同定義了該表的架構(gòu)。DataTable 還包含由 DataRowCollection(它包含該表中的數(shù)據(jù))表示的行組成的集合。與其當(dāng)前狀態(tài)一起,DataRow 保留其當(dāng)前版本和原始版本,以便標(biāo)識對該行中存儲的值所做的更改。

          DataSets可以強(qiáng)類型化或非類型化。類型化的 DataSetDataSet 基類繼承,但是向 DataSet 中添加了強(qiáng)類型化的語言功能,從而使用戶可以用更加強(qiáng)類型化的編程方式訪問內(nèi)容。在生成應(yīng)用程序時,可以使用任一種類型。但是,Microsoft Visual Studio ? 開發(fā)系統(tǒng)對類型化DataSets具有更多支持,它們使得用DataSets編程變得更加容易,而且更不容易出錯。

          DataSets在智能客戶端環(huán)境中尤其有用,因?yàn)樗鼈兲峁┝四軌驇椭蛻舳嗽诿摍C(jī)狀態(tài)下使用數(shù)據(jù)的功能。它們可以跟蹤對數(shù)據(jù)進(jìn)行的本地更改,這有助于與服務(wù)器同步數(shù)據(jù)以及協(xié)調(diào)數(shù)據(jù)沖突,并且它們還可用于合并來自不同源的數(shù)據(jù)。

          有關(guān)如何使用DataSets的詳細(xì)信息,請參閱 Visual Basic and Visual C# Concepts 中的“Introduction to DataSets”,網(wǎng)址為:http://msdn.microsoft.com/library/en-us/vbcon/html/vbconDataSets.asp

          用DataSets合并數(shù)據(jù)

          DataSets具有將 DataSetDataTableDataRow 對象的內(nèi)容合并到現(xiàn)有DataSets的能力。對于跟蹤在客戶端上進(jìn)行的更改以及與服務(wù)器的已更新內(nèi)容進(jìn)行合并而言,該功能尤其有用。圖 2.2 顯示了一個從 Web 服務(wù)請求更新的智能客戶端,新數(shù)據(jù)作為數(shù)據(jù)傳輸對象 (DTO) 返回。DTO 是一種企業(yè)模式,它使您可以將所有需要與 Web 服務(wù)進(jìn)行通訊的數(shù)據(jù)打包到一個對象中。使用 DTO 通常意味著您可以對 Web 服務(wù)進(jìn)行單個調(diào)用而不是多個調(diào)用。


          2.2 通過使用DataSets合并客戶端上的數(shù)據(jù)

          在該示例中,當(dāng) DTO 被返回到客戶端時,該 DTO 將被用于在客戶端上以本地方式創(chuàng)建一個新的DataSets。

          在合并操作之后,ADO.NET 不會自動將行狀態(tài)從 modified 更改為 unchanged。因此,在將新的DataSets與本地客戶端DataSets合并之后,您需要調(diào)用DataSets上的 AccceptChanges 方法,將 RowState 屬性重置為 unchanged。

          有關(guān)如何使用DataSets的詳細(xì)信息,請參閱 .NET Framework Developer's Guide 中的“Merging DataSet Contents”,網(wǎng)址為:http://msdn.microsoft.com/library/en-us/cpguide/html/cpconmergingDataSetcontents.asp。

          提高DataSets的性能

          DataSets通常可以包含大量數(shù)據(jù),如果通過網(wǎng)絡(luò)傳遞這些數(shù)據(jù),則可能導(dǎo)致性能問題。幸而,通過 ADO.NET DataSets,您可以使用DataSets上的 GetChanges 方法來確保只在客戶端和服務(wù)器之間傳送在DataSets中更改過的數(shù)據(jù),并且將該數(shù)據(jù)打包到 DTO 中。該數(shù)據(jù)隨后將被合并到其目的地的DataSets中。

          圖 2.3 顯示了一個智能客戶端,它對本地?cái)?shù)據(jù)進(jìn)行更改,并且使用DataSets上的 GetChanges 方法僅將已更改的數(shù)據(jù)提交給服務(wù)器。出于性能原因,該數(shù)據(jù)被傳輸給 DTO。


          2.3 使用 DTO 改善性能

          可以將 GetChanges 方法用于需要脫機(jī)工作的智能客戶端應(yīng)用程序。當(dāng)應(yīng)用程序重新聯(lián)機(jī)時,您可以使用 GetChanges 方法確定哪些信息已經(jīng)更改,并且隨后生成一個與 Web 服務(wù)通訊的 DTO,以便確保將更改提交給數(shù)據(jù)庫。

          返回頁首返回頁首

          Windows 窗體數(shù)據(jù)綁定

          通過 Windows 窗體數(shù)據(jù)綁定,您可以將應(yīng)用程序的用戶界面連接到該應(yīng)用程序的基礎(chǔ)數(shù)據(jù)。Windows 窗體數(shù)據(jù)綁定支持雙向綁定,因此您可以將數(shù)據(jù)結(jié)構(gòu)綁定到用戶界面,向用戶顯示當(dāng)前數(shù)據(jù)值,使用戶可以編輯數(shù)據(jù),然后使用用戶輸入的值自動更新基礎(chǔ)數(shù)據(jù)。

          您可以使用 Windows 窗體數(shù)據(jù)綁定將幾乎任何數(shù)據(jù)結(jié)構(gòu)或?qū)ο蠼壎ǖ接脩艚缑婵丶娜魏螌傩浴D梢詫蝹€數(shù)據(jù)項(xiàng)綁定到控件的單個屬性,還可以將更為復(fù)雜的數(shù)據(jù)(例如,數(shù)據(jù)項(xiàng)集合或數(shù)據(jù)庫表)綁定到該控件,以便它可以在數(shù)據(jù)網(wǎng)格或列表框中顯示所有數(shù)據(jù)。

          您可以綁定任何支持一個或多個公共屬性的對象。您只能綁定到類的公共屬性而不是公共成員。

          通過 Windows 窗體數(shù)據(jù)綁定,您可以隨您的應(yīng)用程序一起提供靈活的、數(shù)據(jù)驅(qū)動的用戶界面。您可以使用數(shù)據(jù)綁定提供對用戶界面外觀的自定義控制(例如,通過綁定到某些控件屬性,如背景或前景顏色、大小、圖像或圖標(biāo))。

          數(shù)據(jù)綁定具有許多種用途。例如,可以使用它完成下列任務(wù):

          ?

          向用戶顯示只讀數(shù)據(jù)。

          ?

          使用戶可以從用戶界面更新數(shù)據(jù)。

          ?

          提供數(shù)據(jù)上的主從視圖。

          ?

          使用戶可以瀏覽復(fù)雜的相關(guān)數(shù)據(jù)項(xiàng)。

          ?

          提供查找表功能,使用戶界面可以連接用戶友好的顯示名稱。

          本節(jié)分析數(shù)據(jù)綁定的一些功能,并討論一些您經(jīng)常需要在智能客戶端應(yīng)用程序中實(shí)現(xiàn)的數(shù)據(jù)綁定功能。

          有關(guān)數(shù)據(jù)綁定的詳細(xì)信息,請參閱“Windows Forms Data Binding and Objects”,網(wǎng)址為:http://msdn.microsoft.com/library/en-us/dnadvnet/html/vbnet02252003.asp

          Windows 窗體數(shù)據(jù)綁定體系結(jié)構(gòu)

          Windows 窗體數(shù)據(jù)綁定提供了一種用于將數(shù)據(jù)雙向連接到用戶界面的靈活的基礎(chǔ)結(jié)構(gòu)。圖 2.4 顯示了 Windows 窗體數(shù)據(jù)綁定的總體體系結(jié)構(gòu)的示意圖。


          2.4 Windows 窗體數(shù)據(jù)綁定的體系結(jié)構(gòu)

          Windows 窗體數(shù)據(jù)綁定使用下列對象:

          ?

          數(shù)據(jù)源。數(shù)據(jù)源是包含要綁定到用戶界面的數(shù)據(jù)的對象。數(shù)據(jù)提供程序可以是任何具有公共屬性的對象,可以是支持 IList 接口的數(shù)組或集合,還可以是復(fù)雜數(shù)據(jù)類(例如,DataSet 或 DataTable)的實(shí)例。

          ?

          CurrencyManagerCurrencyManager 對象用于跟蹤綁定到用戶界面的數(shù)組、集合或表內(nèi)的數(shù)據(jù)的當(dāng)前位置。通過 CurrencyManager 可以將數(shù)據(jù)集合綁定到用戶界面以及在相應(yīng)的數(shù)據(jù)中導(dǎo)航,同時更新用戶界面以反映集合內(nèi)當(dāng)前選擇的項(xiàng)。

          ?

          PropertyManagerPropertyManager 對象負(fù)責(zé)維護(hù)綁定到控件的對象的當(dāng)前屬性。PropertyManager 類和 CurrencyManager 類都從公用基類 BindingManagerBase 中繼承。所有綁定到控件的數(shù)據(jù)提供程序都具有一個關(guān)聯(lián)的 CurrencyManagerPropertyManager 對象。

          ?

          BindingContext。每個 Windows 窗體都具有一個默認(rèn)的 BindingContext 對象,該對象跟蹤相應(yīng)窗體上的所有 CurrencyManagerPropertyManager 對象。通過 BindingContext 對象可以容易地檢索特定數(shù)據(jù)源的 CurrencyManagerPropertyManager 對象。您可以將特定的 BindingContext 對象分配給包含數(shù)據(jù)綁定控件的容器控件(如 GroupBoxPanelTabControl)。這樣做可以使窗體的每個部分都由它自己的 CurrencyManagerPropertyManager 對象管理。

          ?

          BindingBinding 對象用于在控件的單個屬性與另一個對象的屬性或某個對象列表中當(dāng)前對象的屬性之間創(chuàng)建和維護(hù)簡單綁定。

          將數(shù)據(jù)綁定到 Windows 窗體控件

          有許多可用于綁定到特定 Windows 窗體控件的屬性和方法。表 2.2 顯示了其中一些比較重要的屬性和方法。

          2.2 用于綁定到 Windows 窗體控件的屬性和方法

          屬性或方法 Windows 窗體控件 說明

          DataSource 屬性

          ListControls(例如,ListBox 或 Combo Box)、

          DataGrid 控件

          使您可以指定要綁定到用戶界面控件的數(shù)據(jù)提供程序?qū)ο蟆?/P>

          DisplayMember 屬性

          ListControls

          使您可以指定要顯示給用戶的數(shù)據(jù)提供程序的成員。

          ValueMember 屬性

          ListControls

          使您可以指定與顯示值相關(guān)聯(lián)的、供您的應(yīng)用程序內(nèi)部使用的值。

          DataMember 屬性

          DataGrid 控件

          如果數(shù)據(jù)源包含多個數(shù)據(jù)源(例如,如果您指定了包含多個表的DataSets),請使用 DataMember 屬性來指定要綁定到網(wǎng)格的數(shù)據(jù)源。(參閱表后面的備注。)

          SetDataBinding 方法

          DataGrid 控件

          使您可以在運(yùn)行時重置 DataSource 方法。

          如果 DataSourceDataTableDataView、集合或數(shù)組,則無須設(shè)置 DataMember 屬性。

          您還可以使用所有 Windows 窗體控件對象上提供的 DataBindings 集合屬性將 Binding 對象顯式添加到任何控件對象。Binding 對象用于將控件上的單個屬性綁定到數(shù)據(jù)提供程序的單個數(shù)據(jù)成員。下面的代碼示例在一個文本框控件的 Text 屬性和一個數(shù)據(jù)集的 customers 表中的客戶名稱之間添加了綁定。

          textBox1.DataBindings.Add( 
                  new Binding( "Text", DataSet, "customers.customerName" ) ); 
          

          當(dāng)您用 Binding 構(gòu)造函數(shù)構(gòu)建 Binding 示例時,您必須指定要綁定到的控件屬性的名稱、數(shù)據(jù)源以及可解析為該數(shù)據(jù)源中的列表或?qū)傩缘膶?dǎo)航路徑。該導(dǎo)航路徑可以是空字符串、單個屬性名或句點(diǎn)分隔的名稱層次結(jié)構(gòu)。您可以使用分層的導(dǎo)航路徑在 DataSet 對象中的數(shù)據(jù)表和關(guān)系中導(dǎo)航,或者在對象的屬性向其他對象返回實(shí)例的對象模型中導(dǎo)航。如果您將導(dǎo)航路徑設(shè)置為空字符串,則會在基礎(chǔ)數(shù)據(jù)源對象上調(diào)用 ToString 方法。

          如果屬性是只讀的(即,對象不支持對該屬性進(jìn)行的設(shè)置操作),則數(shù)據(jù)綁定默認(rèn)情況下不會使綁定的 Windows 窗體控件成為只讀的。這可能給用戶帶來混亂,因?yàn)橛脩艨梢跃庉嬘脩艚缑嬷械闹担壎▽ο笾械闹祵⒉粫玫礁隆K裕埓_保將所有被綁定到只讀屬性的 Windows 窗體控件的只讀標(biāo)志設(shè)置為 true

          將控件綁定到DataSets

          將控件綁定到數(shù)據(jù)集通常是有用的。這樣做使您可以在數(shù)據(jù)網(wǎng)格中顯示數(shù)據(jù)集數(shù)據(jù),并且使用戶可以容易地更新數(shù)據(jù)。您可以使用以下代碼將數(shù)據(jù)網(wǎng)格控件綁定到 DataSet

          DataSet newDataSet = webServiceProxy.GetDataSet(); 
          this.DataGrid.SetDataBinding( newDataSet, "tableName" ); 
          

          有時,在已經(jīng)建立與您的控件的所有綁定之后,您需要替換數(shù)據(jù)集的內(nèi)容。但是,在用新的集合替換現(xiàn)有集合時,所有綁定仍將指向舊的數(shù)據(jù)集。

          比用新的數(shù)據(jù)源手動重新創(chuàng)建數(shù)據(jù)綁定更好的辦法是,您可以使用 DataSet 類的 Merge 方法將新數(shù)據(jù)集中的數(shù)據(jù)導(dǎo)入現(xiàn)有數(shù)據(jù)集,如下面的代碼示例所示。

          DataSet newDataSet = myService.GetDataSet(); 
          this.DataSet1.Clear();    
          this.DataSet1.Merge( newDataSet ); 
          

          要避免線程化問題,您應(yīng)該只在 UI 線程上更新綁定的數(shù)據(jù)對象。有關(guān)詳細(xì)信息,請參閱第 6 章:使用多個線程

          在數(shù)據(jù)集合中導(dǎo)航

          如果您的數(shù)據(jù)源包含項(xiàng)集合,則可以將該數(shù)據(jù)集合綁定到 Windows 窗體控件,并且在該數(shù)據(jù)集合中逐項(xiàng)導(dǎo)航。用戶界面將自動更新以反映集合中的當(dāng)前項(xiàng)。

          您可以綁定到任何支持 IList 接口的集合對象。當(dāng)您綁定到對象集合時,您可以讓用戶導(dǎo)航該集合中的每個項(xiàng),并自動更新每個項(xiàng)的用戶界面。.NET Framework 提供的許多集合和復(fù)雜數(shù)據(jù)類已經(jīng)支持 IList 接口,因此您可以容易地綁定到數(shù)組或復(fù)雜數(shù)據(jù),如數(shù)據(jù)行或數(shù)據(jù)視圖。例如,任何作為 System.Array 類的實(shí)例的數(shù)組對象默認(rèn)情況下都實(shí)現(xiàn)了 IList 接口,因而可以綁定到用戶界面。許多 ADO.NET 對象還支持 IList 接口或它的派生接口,從而使這些對象也可以容易地綁定。例如,DataViewManagerDataSetDataTableDataViewDataColumn 類都以這種方式支持?jǐn)?shù)據(jù)綁定。

          實(shí)現(xiàn)了 IList 接口的數(shù)據(jù)源由 CurrencyManager 對象管理。該對象通過它的 Position 屬性維護(hù)數(shù)據(jù)集合的索引。該索引用于確保綁定到該數(shù)據(jù)源的所有控件都讀/寫數(shù)據(jù)集合中的相同項(xiàng)。

          如果您的窗體包含綁定到多個數(shù)據(jù)源的控件,則它將具有多個 CurrencyManager 對象,分別對應(yīng)于各個獨(dú)立的數(shù)據(jù)源。BindingContext 對象提供對該窗體上的所有 CurrencyManager 對象的方便訪問。下面的代碼示例顯示了如何在 customers 集合內(nèi)部遞增當(dāng)前位置。

          this.BindingContext[ DataSet, "customers" ].Position += 1; 
          

          您應(yīng)該像以下代碼示例中所示的那樣,使用 CurrencyManager 對象上的 Count 屬性來確保不會設(shè)置無效位置。

          if ( this.BindingContext[ DataSet, "customer" ].Position < 
               ( this.BindingContext[ DataSet, "customer" ].Count – 1 ) ) 
          { 
              this.BindingContext[ DataSet, "customers" ].Position += 1; 
          } 
          

          CurrencyManager 對象還支持 PositionChanged 事件。您可以創(chuàng)建該事件的處理程序,以便更新您的用戶界面以反映當(dāng)前綁定位置。下面的代碼示例顯示了一個標(biāo)簽,以說明當(dāng)前位置和記錄總數(shù)。

          this.BindingContext[ DataSet, "customers" ].PositionChanged += 
                  new EventHandler( this.BindingPositionChanged ); 
          

          方法 BindingPositionChanged 的實(shí)現(xiàn)方式如下所示。

          private void BindingPositionChanged( object sender, System.EventArgs e ) 
          {    
              positionLabel.Text = string.Format( "Record {0} of {1}", 
                  this.BindingContext[dsPubs1, "authors"].Position + 1,  
                  this.BindingContext[dsPubs1, "authors"].Count ); 
          } 
          

          自定義格式和數(shù)據(jù)類型轉(zhuǎn)換

          您可以使用 Binding 類的FormatParse 事件為綁定到控件的數(shù)據(jù)提供自定義格式。通過這些事件,您可以控制在用戶界面中顯示數(shù)據(jù)的方式以及從用戶界面中獲取數(shù)據(jù)和分析數(shù)據(jù)的方式,以便更新基礎(chǔ)數(shù)據(jù)。還可以使用這些事件來轉(zhuǎn)換數(shù)據(jù)類型,以便源數(shù)據(jù)類型和目標(biāo)數(shù)據(jù)類型兼容。

          如果控件上綁定屬性的數(shù)據(jù)類型與數(shù)據(jù)源中數(shù)據(jù)的數(shù)據(jù)類型不匹配,則會引發(fā)異常。如果您需要綁定不兼容的類型,則應(yīng)該使用 Binding 對象上的 FormatParse 事件。

          當(dāng)從數(shù)據(jù)源中讀取數(shù)據(jù)并將其顯示在控件中時,以及當(dāng)從控件中讀取數(shù)據(jù)并使用它來更新數(shù)據(jù)源時,將發(fā)生 Format 事件。當(dāng)從數(shù)據(jù)源中讀取數(shù)據(jù)時,Binding 對象將使用 Format 事件在控件中顯示格式化數(shù)據(jù)。當(dāng)從控件中讀取數(shù)據(jù)并使用它來更新數(shù)據(jù)源時,Binding 對象將使用 Parse 事件來分析數(shù)據(jù)。

          FormatParse 事件使您可以創(chuàng)建用于顯示數(shù)據(jù)的自定義格式。例如,如果表中的數(shù)據(jù)的類型是 Decimal,則您可以通過將 ConvertEventArgs 對象的 Value 屬性設(shè)置為 Format 事件中的格式化值,以本地貨幣格式顯示數(shù)據(jù)。因此,您必須在 Parse 事件中格式化顯示的值。

          下面的代碼示例將訂單金額綁定到文本框。FormatParse 事件用于在文本框期望的 string 類型和數(shù)據(jù)源期望的 decimal 類型之間進(jìn)行轉(zhuǎn)換。

          private void BindControl() 
          { 
              Binding binding = new Binding( "Text", DataSet, 
          "customers.custToOrders.OrderAmount" ); 
              // Add the delegates to the event. 
              binding.Format += new ConvertEventHandler( DecimalToCurrencyString ); 
              binding.Parse  += new ConvertEventHandler( CurrencyStringToDecimal ); 
              text1.DataBindings.Add( binding ); 
          } 
          private void DecimalToCurrencyString( object sender, ConvertEventArgs cevent ) 
          { 
              // The method converts only to string type. Test this using the  
          DesiredType. 
              if( cevent.DesiredType != typeof( string ) ) return; 
              // Use the ToString method to format the value as currency ("c"). 
              cevent.Value = ((decimal)cevent.Value).ToString( "c" ); 
          } 
          private void CurrencyStringToDecimal( object sender, ConvertEventArgs cevent ) 
          { 
              // The method converts back to decimal type only.  
              if( cevent.DesiredType != typeof( decimal ) ) return; 
              // Converts the string back to decimal using the static Parse method. 
              cevent.Value = Decimal.Parse( cevent.Value.ToString(), 
                          NumberStyles.Currency, null ); 
          } 
          

          使用模型-視圖-控制器模式來實(shí)現(xiàn)數(shù)據(jù)驗(yàn)證

          通過將數(shù)據(jù)結(jié)構(gòu)綁定到用戶界面元素,用戶可以編輯數(shù)據(jù)并確保所做更改隨后被寫回到基礎(chǔ)數(shù)據(jù)結(jié)構(gòu)。通常,您需要檢查用戶對數(shù)據(jù)所做的更改,以確保輸入的值有效。

          上一節(jié)中介紹的 FormatParse 事件提供了一種用于截獲用戶對數(shù)據(jù)所做更改的方法,以便可以檢查數(shù)據(jù)的有效性。但是,該方法要求與自定義格式代碼一起實(shí)現(xiàn)數(shù)據(jù)驗(yàn)證邏輯(通常是在窗體級別)。如果在事件處理程序中同時實(shí)現(xiàn)這兩種職責(zé),則會使您的代碼難以理解和維護(hù)。

          更為雅致的辦法是對代碼進(jìn)行設(shè)計(jì),以使其使用模型-視圖-控制器 (MVC) 模式。該模式提供了在通過數(shù)據(jù)綁定編輯和更改數(shù)據(jù)時涉及到的各種職責(zé)的自然分隔。您應(yīng)該在負(fù)責(zé)以特定格式呈現(xiàn)數(shù)據(jù)的窗體內(nèi)實(shí)現(xiàn)自定義格式,然后將驗(yàn)證規(guī)則與數(shù)據(jù)本身相關(guān)聯(lián),以便在多個窗體中重新使用這些規(guī)則。

          在 MVC 模式中,數(shù)據(jù)本身被封裝在模型對象中。視圖對象是數(shù)據(jù)所綁定到的 Windows 窗體控件。對該模型所做的所有更改都由一個中間控制器對象處理,該對象負(fù)責(zé)提供對數(shù)據(jù)的訪問,并且負(fù)責(zé)控制通過視圖對象對數(shù)據(jù)所做的任何更改。控制器對象提供了一個用于驗(yàn)證對數(shù)據(jù)所做更改的自然位置,所有用戶界面驗(yàn)證邏輯都應(yīng)該在這里實(shí)現(xiàn)。

          圖 2.5 描繪了 MVC 模式中的三個對象之間的結(jié)構(gòu)關(guān)系。


          2.5 模型-視圖-控制器模式中的對象

          以這種方式使用控制器對象具有許多優(yōu)點(diǎn)。您可以配置一個普通的控制器以提供自定義驗(yàn)證規(guī)則,這些規(guī)則可以在運(yùn)行時根據(jù)某些上下文信息(例如,用戶的角色)進(jìn)行配置。或者,您還可以提供許多個控制器對象,每個控制器對象都實(shí)現(xiàn)特定的驗(yàn)證規(guī)則,然后在運(yùn)行時選擇適當(dāng)?shù)膶ο蟆o論采用哪種方法,因?yàn)樗序?yàn)證邏輯都被封裝在控制器對象中,所以視圖和模型對象都不需要更改。

          除了分隔數(shù)據(jù)、驗(yàn)證邏輯和用戶界面控件以外,MVC 模型還為您提供了一種在基礎(chǔ)數(shù)據(jù)更改時自動更新用戶界面的簡單方法。控制器對象負(fù)責(zé)在發(fā)生通過其他某些編程手段對數(shù)據(jù)進(jìn)行更改時通知用戶界面。Windows 窗體數(shù)據(jù)綁定偵聽由綁定到控件的對象生成的事件,以便用戶界面可以自動響應(yīng)對基礎(chǔ)數(shù)據(jù)所做的更改。

          要實(shí)現(xiàn)用戶界面的自動更新,您應(yīng)該確保控制器為每個可能更改的屬性實(shí)現(xiàn)一個更改通知事件。事件應(yīng)該遵循命名約定<property>Changed,其中 <property> 是屬性的名稱。例如,如果控制器支持 Name 屬性,則它還應(yīng)該支持 NameChanged 事件。如果名稱屬性的值更改,則應(yīng)該激發(fā)該事件,以便 Windows 窗體數(shù)據(jù)綁定可以處理它并更新用戶界面。

          下面的代碼示例定義了一個 Customer 對象,該對象實(shí)現(xiàn)了 Name 屬性。CustomerController 對象處理 Customer 對象的驗(yàn)證邏輯并支持 Name 屬性,而該屬性又表示基礎(chǔ) Customer 對象上的 Name 屬性。每當(dāng)該名稱更改時,此控制器都將激發(fā)一個事件。

          public class Customer 
          { 
              private string _name; 
              public Customer( string name ) { _name = name; } 
              public string Name 
              { 
                  get { return _name; } 
                  set { _name = value; } 
              } 
          } 
          public class CustomerController 
          { 
              private Customer _customer = null; 
              public event EventHandler NameChanged; 
              public Customer( Customer customer ) 
              { 
                  this._customer = customer; 
              } 
              public string Name 
              { 
                  get { return _customer.Name; } 
                  set 
                  { 
                       // TODO: Validate new name to make sure it is valid. 
                      _customer.Name = value;  
                      // Notify bound control of change. 
                      if ( NameChanged != null ) 
                          NameChanged( this, EventArgs.Empty ); 
                  } 
              } 
          } 
          

          Customer 數(shù)據(jù)源成員在聲明時需要進(jìn)行初始化。在前面的示例中,需要將 customer.Name 成員初始化為空字符串。這是因?yàn)樵跀?shù)據(jù)綁定發(fā)生之前,.NET 框架沒有機(jī)會與該對象進(jìn)行交互并設(shè)置默認(rèn)的空字符串設(shè)置。如果未初始化 customer 數(shù)據(jù)源成員,則在嘗試從未初始化的變量中檢索值時,將導(dǎo)致運(yùn)行時異常。

          在下面的代碼示例中,窗體具有一個 TextBox 對象 textbox1,它需要綁定到客戶的名稱。代碼將 TextBox 對象的 Text 屬性綁定到控制器的 Name 屬性。

          _customer = new Customer( "Kelly Blue" ); 
          _controller = new CustomerController( _customer ); 
          Binding binding = new Binding( "Text", _controller, "Name" ); 
          textBox1.DataBindings.Add( binding ); 
          

          如果更改了客戶的名稱(使用控制器上的 Name 屬性),則會激發(fā) NameChanged 事件,并且自動更新文本框以反映新的名稱值。

          在基礎(chǔ)數(shù)據(jù)更改時更新用戶界面

          您可以使用 Windows 窗體數(shù)據(jù)綁定在相應(yīng)的基礎(chǔ)數(shù)據(jù)更改時自動更新用戶界面。通過在綁定的對象上實(shí)現(xiàn)一個更改通知事件,可以完成該任務(wù)。更改通知事件按照以下約定命名。

          public event EventHandler Changed; 
          

          因此,假設(shè)您將某個對象的 Name 屬性綁定到用戶界面,然后該對象的名稱由于其他某種處理而更改,則您可以通過實(shí)現(xiàn)綁定對象上的 NameChanged 事件來自動更新用戶界面,以反映新的 Name 值。

          返回頁首返回頁首

          小結(jié)

          在確定如何在智能客戶端處理數(shù)據(jù)時,涉及到許多不同的注意事項(xiàng)。您需要確定是否緩存以及如何緩存您的數(shù)據(jù),并且確定如何處理數(shù)據(jù)并發(fā)問題。您將經(jīng)常決定使用 ADO.NET 數(shù)據(jù)集來處理您的數(shù)據(jù),并且您還可能將決定利用 Windows 窗體數(shù)據(jù)綁定功能。

          在許多情況下,只讀引用數(shù)據(jù)和瞬態(tài)數(shù)據(jù)需要進(jìn)行不同的處理。因?yàn)橹悄芸蛻敉ǔJ褂眠@兩種類型的數(shù)據(jù),所以您需要確定在應(yīng)用程序中處理各個類別數(shù)據(jù)的最佳方式。

          轉(zhuǎn)到原英文頁面


          posted on 2006-02-10 14:51 TrampEagle 閱讀(546) 評論(0)  編輯  收藏 所屬分類: 技術(shù)文摘
          主站蜘蛛池模板: 乐安县| 黄浦区| 南安市| 湖南省| 红桥区| 合作市| 太谷县| 赣州市| 临沂市| 紫金县| 曲松县| 葵青区| 历史| 莎车县| 新蔡县| 龙南县| 禄丰县| 宕昌县| 陇南市| 大关县| 淳化县| 宁津县| 综艺| 旺苍县| 山东省| 西安市| 永康市| 麻阳| 桂东县| 神池县| 南乐县| 新竹县| 乌拉特中旗| 鄂托克旗| 商河县| 自贡市| 大竹县| 阿克苏市| 安达市| 木兰县| 徐闻县|