asp.net
一、 引言
早在2001年,我就著手開發一個ASP.NET線上訊息板應用程式WebForums.NET。其目的是建立一個基於ASP.NET的訊息板系統,而且該系統可以容易插入到一個現有網站中。構建這樣一個端對端應用程式的特別挑戰之一就是,要為客戶提供一種方式以便能夠把它整合到他們自己的系統中去。例如,一個線上論壇明顯需要使用某種資料存放區來儲存使用者資訊、論壇、回寄資訊等;但是,最好不要把客戶鎖定到一種特定的資料存放區中。也就是說,你不應該說,"我的應用程式必須使用微軟的SQL Server 2000";因為這樣的話,使用Oracle或Access的客戶怎麼會使用你的軟體呢?
另一個整合到客戶現有資料中的問題是,所有的線上論壇網站都提供使用者帳戶和一種建立新帳戶的方式。典型情況下,這被建模為一個論壇架構(以一個資料庫中的Users表形式存在)。但是,客戶很可能已經有他們自己的擁有成千的使用者帳戶的資料庫表。或者,一個客戶可能想在一個內部網設定中使用該論壇,並且想使用活動目錄而不是某種資料庫表來認證和儲存使用者資訊。因此,當一個論壇軟體系統建立一個Users資料庫表並對其客戶說"這就是你儲存使用者的方式"時,那些已經擁有現有基礎結構和使用者資料的客戶很可能會疏遠這樣的軟體。
因此,當你使用一種"僵硬"的API構建一個系統時,會產生特別的挑戰。一種"僵硬"的API不是提供一種方式來定製邏輯而是寫入程式碼實現細節(例如,你必須使用SQL Server作為你的後端資料存放區,且在這個資料庫有一個Users表,並將在其中儲存所有的使用者資訊)。然而,通過使用提供者設計模式,你可以輕易地打破這種"僵硬"性。藉助於提供者設計模式,系統架構師只需要定義API,至於編程功能則由系統來提供。對於一個線上論壇應用程式來說,這可能包括一個具有例如Authenticate(username,password)和GetUserDetails(username)等方法的Users類。
提供者模型的優秀在於客戶實現方案可以指定一個系統應該使用的定製類。這種定製類必須實現系統的良好定義的API;但是,它允許無縫地插入任何定製實現。也就是說,一旦定義這個API,系統實現者可以建立一個使用SQL Server和一個Users表的預設的具體實現-大多數客戶可以直接使用之而不必要作任何修改。那些有定製需要的客戶(他們想使用Oracle或以另外一些方式儲存使用者資料)可以建立他們自己的類,該類提供必要的功能並且把它們插入到這些客戶的系統中。
其實,提供者設計模式被應用於整個ASP.NET 2.0實現中。當然,網上也存在一些如何在ASP.NET 1.x應用程式中使用這一功能的教程。
在本文中,我們將詳細探討提供者模型並分析如何把它應用於ASP.NET 2.0開發中。
二、 打破"僵硬"的API實現
在我早期的WebForums.NET開發中,我認識到,這種"僵硬"的API實現將會成為一個問題。我的軟體設計目標之一就是:儘可能靈活且可定製,並且使使用者使用SQL Server,而且我的使用者資料模型實現應該看起來充其量只是有些限制性。為了克服這些問題,我構建了一個包含下面兩部分的系統:
1. 一組定義了系統的核心功能的抽象基類;
2. 能夠在運行時刻動態地載入一個擴充抽象基類。具體地說,該代碼負責檢查包含一個<ConfigSetting>節(該節中給出要使用的類的完全限定名)的Web.config檔案。
藉助於這一架構,我可以通過一系列抽象基類來定義系統的功能,並使用SQL Server 2000和Users表來提供這些類的具體實現。滿足這一配置的客戶可以只管使用該應用程式,並且一切將工作良好,且不需要他們編寫一行代碼。然而,那些需要定製的開發人員們可以通過建立他們自己的派生自適當的抽象基類的類來實現。通過簡單地把該程式集放到應用程式的/bin目錄並更新Web.config檔案,他們可以讓系統使用這個新類。具體地說,WebForums.NET發行中帶有一個抽象基類DataProvider,它清楚地列舉出了系統中的所有方法,類似如下:
public abstract class DataProvider { public abstract bool AuthenticateUser(string username,string password); public abstract User GetUserInfo(string username); ... public static DataProvider Instance() { ... } } |
AuthenticateUser(username,password)和GetUserInfo(username)方法是系統定義的許多方法中的兩個方法的代表。而靜態Instance()方法是該DataProvider類的主要實現;它包含檢查代表了WebForums.NET配置資訊(該資訊指示系統要使用的類的全稱限定名)的Web.config檔案的代碼。然後,該方法使用反射(和緩衝)來建立該類的一個執行個體並且把它返回到系統。
WebForums.NET發行中還帶有一個派生自DataProvider基類的SqlDataProvider類,這個類提供分類方法的具體實現。例如,SqlDataProvider的所有方法都可以操作儲存於一個SQL Server 2000資料庫中的資料;與使用者相關的方法可以與一個預定義的Users資料庫表一起工作。一個想改變後端功能的客戶可以建立他自己的派生自DataProvider的類,這些資訊都可以展示於Web.config檔案中(指明應該使用他們的定製類)。例如,WebForums.NET中的Web.config可能包括下列內容:
<WebForumsSettings> <add key="DataProviderAssemblyPath" value="path" /> <add key="DataProviderClassName" value="Namespace.Classname" /> </WebForumsSettings> |
預設情況下,這個設定資訊引用隨同WebForums.NET一起發行的SqlDataProvider類。然而,如果一個客戶建立他自己的API實現,那麼他可以提供自己的類的細節,並且系統會自動地開始使用他的實現來建立預設實現。
藉助於這一架構,使用WebForums.NET的頁面開發人員可以使用如下所示的代碼來認證一個使用者:
if (DataProvider.Instance().AuthenticateUser(username,password)) //使用者被認證 else //使用者名稱/口令無效! |
當調用DataProvider.Instance()方法時,上面的設定檔被檢查並且返回適當類的一個執行個體。如果客戶還沒有建立他們自己的實現的話,這將是預設的SqlDataProvider類;而如果他們已經實現的話,它將是他們自己的類。一旦DataProvider.Instance()方法返回一個適當的提供者執行個體,我們就可以簡單地調用該API的成員(在這個例子中是AuthenticateUser())。
WebForums.NET提供者模型-一個早期的原型
相對於微軟建議使用的提供者模型,Andy的提供者模型含有一些不足。一方面,WebForums.NET中提供了單個抽象基類,所有的API定義都聚集在這個類中。其負面作用在於,如果一個客戶僅想定製系統的一小部分,例如使用者資訊的儲存方式,那麼他必須提供該系統中所有方法的實現。一種更好的方案是,為系統中的每一個邏輯實體建立一個抽象基類。例如,對於一個線上訊息板應用程式來說,它可能需要一些類,如UsersProvider,ForumsProvider,PostsProvider,等等。然而,在你提供給一個客戶的提供者數目之間也存在一個平衡問題。更多提供者允許更為細緻的系統定製,但是也會相應地提高要求的配置標記的數量。
另外,我已經展示了對WebForums.NET的提供者模型實現代碼的作了進一步改進,以便使其更相似於微軟在ASP.NET 2.0中所使用的代碼。我認為,Andy的想法應該是提供者模型的先驅,儘管微軟的提供者模型實現更為清晰且更強壯一些。
一方面,WebForums.NET在2002年三月為微軟所收購;另一方面,Rob Howard及其他人又在系統中加入了大量的新特徵並且在ASP.NET論壇中以自由方式發行它。Today Rob及其小組成員已經把ASP.NET論壇變成了一個Community Server(它簡直把部落格、論壇、畫廊、列表格服務器、新聞閱讀器等全部融為一體)。今天,Andy所建立的概念與實現被廣泛應用於ASP.NET論壇和Community Server中,甚至被應用於許多核心ASP.NET 2.0組件中。
三、 提供者模型優點
提供者模型提供許多優點。首先,在代碼和後端實現之間存在清晰的分離。不管認證一個使用者的代碼是針對一個SQL Server 2000資料庫的Users表還是針對於一個活動目錄存放,從頁面開發人員的觀察看來代碼都是相同的:
DataProvider.Instance().AuthenticateUser(username,password);
而且,後端實現變化是透明的。
因為系統架構師被鼓勵建立預設的實現,所以提供者模型提供了兩種世界的最好結合:如果對預設實現已經比較滿意,那麼系統會按預期進行工作;對於需要定製系統的使用者來說,他們儘管定製好了而不必幹擾現有代碼或編程邏輯。這個設計模式也使得原型化和靈敏開發容易許多。例如,在早期系統使用階段,僅使用預設實現可能更容易。然而,以後你可能需要定製某些方面以便把該工作與你的公司的現有系統整合到一起。這時,你可以通過提供者模型實現需要的定製。這意味著,不需要改變你的早期工作來反映後端實現的變化。
就象許多好的設計模型一樣,提供者模型也提供了開發人員之間的職責分離。這樣以來,一部分開發人員可以使用他們精通的系統API進行工作,而另一部分開發人員可以專註於後端實現和定製任務開發;而且,這兩組人員可以工作在同一個系統上而不會相互幹擾。而且,如果他們所使用的系統是一種工業標準(例如ASP.NET 2.0),那麼這兩類任務中的技能都可以被容易地移植到未來的工作中。
四、 ASP.NET 2.0提供者模型
ASP.NET 2.0在全部其架構中都利用提供者模型。例如,它的許多子系統-會員,網站導覽,個人化等都利用了提供者模型技術。而且,每個子系統都提供一種預設實現,但也能使客戶定製其功能以滿足他們自己的需要。例如,ASP.NET 2.0的網站導覽部分允許一個頁面開發人員定義他們網站的可導航的結構。然後,這些資料就可以被各種Web控制項所使用以便顯示網站地圖、樹狀檢視或菜單-它們能夠高亮網站的導航並且/或者顯示使用者的網站位置。除了與導航相關的Web控制項外,網站導覽API還提供了一組方法用於實現與網站導航資訊的互動。
預設地,網站的可導航資訊必須以一個正確格式化的XML檔案編碼。這種資料存放區方式是,預設網站導覽被寫入程式碼使用。然而,ASP.NET 2.0的提供者模型可以使你更容易地使用你自己的資料存放區來實現網站導覽。例如,在一個我當前開發的工程中,使用的一個資料庫包含網站中的頁面資訊以及不同的使用者擁有頁面中什麼樣的許可權。不是在一個XML檔案中重新定義這些資訊並且必須努力保持兩份資訊的及時更新;而是,通過利用ASP.NET 2.0中的網站導覽功能,我可以簡單地建立一個提供者類,它能夠直接與資料庫資訊工作。一旦建立這個類並且在網站的配置中指定,導航Web控制項就可以根據儲存在資料庫中的應用程式的定製導航資訊進行工作。(注意:在本文成文之時,這個工程使用的仍然是ASP.NET 1.x;然而,這個例子卻有希望能夠向你展示提供者模型的優點。)
作為個人,我想,提供者模型只是ASP.NET 2.0提供的最優秀的遷移特徵之一。ASP.NET 2.0提供了很多開發人員在1.x版本中必須定製的新特徵。如果這些2.0版本的新特徵使用過於"僵硬"的實現方式,那麼它將阻止基於定製方案的正在使用中的1.x應用程式向它的遷移,因為許多新的ASP.NET 2.0 Web控制項都使用了這些新的子系統。然而,有了提供者模型後,我們就可以把我們的1.x應用程式升級到2.0版本並且建立一個提供者以便使2.0版本的新的子系統與我們的定製方案整合到一起。這意味著,當遷移到2.0版本時,我們可以使用新的Web控制項並且使它們通過提供者模型而自然地使用我們的現有系統。
五、 補充資訊
隨著提供者模型成為ASP.NET 2.0中的一個重要組成部分,微軟出版了很多關於這個主題的文章。如果你想瞭解更多這方面的資訊,那麼我鼓勵你閱讀一下Rob Howard的兩篇文章:
· 《提供者模型設計模式與規範》;
· 《ASP.NET 1.x提供者模型》。
其中,第二篇文章分析了如何把提供者模型應用於你的ASP.NET 1.x應用程式。還有兩篇文章討論了ASP.NET 2.0的網站導覽子系統中的提供者用法:
· 《理解和擴充ASP.NET 2.0中的網站導覽系統》,作者David Gristwood;
· 《定製ASP.NET 2.0中的提供者》,作者Morgan Skinner。
注意,微軟還發行了另外一個提供者開發套件,它也用於建立ASP.NET 2.0提供者。而且,還有一篇不錯的文章《ASP.NET 2.0提供者模型》可供你參考。
六、 結論
當建立具有各種要求的大量客戶所使用的系統時,一種"僵硬"的API實現可能會"嚇壞"了開發人員;這種"僵硬"的實現往往會強迫客戶同意且被鎖定於系統架構師的"視野"之內。而一般地,公司往往更對能夠與他們的現有方案協同工作的應用程式和架構感興趣而不是強迫他們的方案服從供應商提供的系統。
提供者模型提供了一種打破這種"僵硬"的實現問題的方法。藉助於提供者模型,系統就能夠靈活地使用擴充特定基類的任何類。這樣以來,客戶可以建立他們自己的包括他們的定製邏輯和商務規則的衍生類別。而且,這些新類可以無縫地插接到系統中,而不必幹擾應用程式中的現有代碼或任何自建立以來的新的定製代碼。
總之,提供者模型在ASP.NET 2.0中得到普通使用;而且這些概念也可以應用於ASP.NET 1.x應用程式中。