Community Server專題二:體系結構
在進行CS細節分析的之前,有必要先了解CS工程(解決方案)的組成,以及組成CS工程中項目的結構,本文分為三個部分:1、工程結構 2、三層構架 3、數據庫構架。
1:工程結構
CS工程主要分為4個部分
a:系統底層構架項目CommunityServerComponents、CommunityServerControls,提供給其他項目父類、接口、全局變量、CS系統設置、公用用戶自定義控件、用戶與權限管理業務邏輯、異常處理等。
b: CommunityServerBlogs、CommunityServerForums、CommunityServerGalleries、 CommunityServerDocuments、CommunityServerGuestBook。這些項目都是通過繼承、調用全局方法等實現自己的業務邏輯並且抽象出自己的Data Provider,業務邏輯不同,但項目都是采用三層結構。
c:UI項目,這裡指 CommunityServerWeb。該項目中幾乎不包含邏輯代碼,只是簡單的Html與對運用項目中的Skin(Skin是 *.ascx文件,但沒有關聯相應的*.cs,大致可以這樣理解:如CommunityServerBlogs 中的Skin 文件*.ascx相關聯的*.cs邏輯代碼在 CommunityServerBlogs項目中實現,並且不保存在與*.ascx文件相同的目錄下,而是與CommunityServerBlogs中其他業務邏輯一起編譯為CommunityServer.Blogs.dll)。同時UI項目中還保存了Languages文件與一些配置文件等。
d: DataProvider,目前只實現了SqlDataProvider,對SQL Server數據庫進行操作。DataProvider實際上是對b 部分中實體項目數據庫操作抽象的具體實現。數據操作的Provider方式帶來幾個好處,不關心具體實現、支持多數據庫、有利於團隊協作分工等。
2、三層構架
在CommunityServerBlogs、CommunityServerForums、CommunityServerGalleries等幾個項目中都采用了三層構架,如下圖:
業務邏輯是穿插在Contorls與Components中,在單獨的一個項目中Contorls與Components體現在namespace裡,下面以CommunityServerGalleries項目為例講述一下具體項目的三層結構:
為了便於文件的管理,項目中建立了Components與Contorls文件夾分別存放名字空間為 CommunityServer.Galleries.Components與CommunityServer.Galleries.Controls。如果你是一個初學者或者對三層結構不是太了解,可能很多時候你會對三層構架感到困惑,其實這個層的概念沒有絕對的劃分界限,更不是用類作為最小的單位。這種劃分是相對的,是一種為編寫代碼功能的劃分。CommunityServerGalleries項目中沒有直接編寫對數據庫的操作代碼,而是使用了 Provider的方式把操作的方法進行抽象:
例:public abstract Hashtable GetGalleries(bool mergePermissions);
抽象後的代碼可以和普通方法一樣被業務邏輯調用,由於使用了Provider的方式,使得數據層與業務層之間是松散耦合的,可以很容易的進行數據庫更換(只需要更換對抽象數據操作類的具體實現方法,而不會影響到業務邏輯層的代碼)。
業務邏輯包括幾個部分:CommunityServer.Galleries.Components下所有的實體類,這些實體類大多數通過繼承Post、 IThread、PermissionBase等在CommunityServerComponents項目中定義過或者申明過的類與接口。 CommunityServer.Galleries命名空間下的一些類,這些類用來處理業務邏輯運行過程中的數據,同時進行緩存和過濾等操作(過濾操作是通過在CommunityServerComponents項目中的CSApplication.cs類下定義委托與事件完成的,要理解這個過程需要對 CS有一定的了解,後續我會做一個CS中委托與事件的專題)。還有一部分業務邏輯混淆在 CommunityServer.Galleries.Controls命名空間下的一些類中,他們與UI表示層較為緊密,你很難准確的定義他們是屬於業務邏輯還是表現層代碼。
CS中表示層中的類大致可以分為三部分,1:是需要*.ascx的直接處理用戶界面或者用戶輸入輸出的代碼,這些類都間接的繼承CommunityServerControls項目中的TemplatedWebControl類。2:要進行換膚就少不了使用一些輔助的類,這些類提供一些基礎服務,如:找到*.cs文件對應*.ascx所在路徑等。3:是不需要*.ascx的用戶自定義控件,一般繼承自.Net提供的 WebControls。這些類被放入Controls/Utility文件夾下面。
傳統的Asp.net Web頁面設計時在建立 *.aspx或者*.ascx都會同時建立一個同名的*.cs文件,用來實現對頁面中控件的操作,頁面這個時候就像一個容器。通過Codebehind頁面在運行時會自動找到對應的類(這個過程如何實現沒有去分析過,但是我們可以通過反射達到同樣的效果,同時可以獲得更高的靈活性)。CS系統中的UI就是通過反射尋找到*.ascx對應的類從而實現相應的UI處理函數,而*.ascx只要保持名稱和內容中控件的ID不變,具體Html代碼如何更換並不影響到整個系統的功能,CS系統也正是通過這樣的手段達到換膚的目的,同時加入MasterPage又可以減少Html代碼中重復部分。最後Html與CSS 樣式表的結合你就可以很容易改變網站的皮膚的樣式,包括文字樣子和div的布局了。*.cs與*.ascx文件剝離後網站美工與程序設計人員就真正的分開了,有利於團隊協作,發揮個人特長。
還有一點必須說明:在CS項目中很多*.aspx文件只是一個加入了MasterPage的框架頁,甚至是一個什麼都沒有的空文件(如多數default.aspx頁面),框架文件中嵌入了大量的類似於“<Galleries: GalleryAdmin id="GalleryAdmin" runat="server" />”這樣的控件,其實這個控件對應於Skin- GalleryAdmin.ascx的皮膚。如果你能理解到這裡,想看明白CS的大部分代碼應該不會有問題。
3、數據庫構架
先看一下DataProvider模型:
模型中可以看出抽象的DataProvider是與具體的數據庫操作DataProvider分離的,在CS中Components與抽象的 DataProvider被編譯在一個項目中,而SQL Server DataProvider則被單獨的編譯出來。好處都可以看到那就是更換不同的 DataProvider抽象實現不同的數據庫操作,另外這種松散耦合的方式有利於團隊開放。如何實現這樣的DataProvider方式呢(我這裡簡述一些,具體的請關注後續的專題)?
先看抽象類,抽象類被存放在相應項目的Providers目錄下,以Gallery項目為例子,它的命名空間是CommunityServer.Galleries.Components
整個類都是public abstract class,這個很好理解,其實關鍵的是在“Instance”
部分,在 Instance裡通過調用CommunityServerComponents項目DataProviders.cs類中的CreateInstance方法初始化一個GalleryDataProvider。
過程是先在Communityserver.config文件中找到
public static readonly string GalleryDataProviderName = "GalleryDataProvider";
中的“GalleryDataProvider”,這裡為:
<add name = "GalleryDataProvider" type = "CommunityServer.Data.GallerySqlDataProvider, CommunityServer.SqlDataProvider" connectionStringName = "SiteSqlServer" databaseOwnerStringName = "SiteSqlServerOwner" />
根據“type”中的內容,運用Type.GetType與Activator.CreateInstance把 CommunityServer.SqlDataProvider.dll程序集中對應的 CommunityServer.Data.GallerySqlDataProvider類實例化,實例化後類似 GalleryDataProvider.Instance().GetGalleries(true)的調用其實就是直接操作 CommunityServer.SqlDataProvider.dll程序集中 CommunityServer.Data.GallerySqlDataProvider類下的
public override Hashtable GetGalleries(bool mergePermissions)方法。這個過程可能比較難理解,但是理解只是時間問題。
數據訪問層的中與數據庫最緊密接觸的就是SqlDataProvider(SqlDataProvider是對SQL Server數據庫操作抽象的實現,你也可以對其他數據庫進行抽象實現,目前CS只提供SQL Server實現),在SqlDataProvider裡使用的是對存儲過程的操作而沒有使用 SQL Text。在前一片專題中我寫過這個做的好處,這裡不再多說。主要說明的是對數據的緩存與序化:
緩存:我個人習慣是把緩存寫在數據層裡,而CS是把緩存管理寫在業務邏輯層中,而且緩存的數量是很大的,如對CommunityServerGalleries項目中的讀取單個Gallery方法:
public static Gallery GetGallery(string applicationKey, bool cacheable)
一般的做法是為這個方法寫一個存儲過程,然後當有數據操作的時候從數據庫中調用相關數據,同時根據參數是否緩存數據,這看起來很好。我也總是覺得內存寶貴,能少緩存一點就少緩存一點,但是CS的做法是把全部Gallery讀入Hashtable,緩存掉!要讀取單個Gallery的時候從緩存中找,根本不去管數據庫,更不要寫存儲過程(這倒是很方便)。當然了,CS中是對緩存定義了時間的。時間到期後緩存就自動被釋放了,但是在緩存釋放之前新的數據是不會被顯示出來的,對於一些更新不是很快的數據集來說這算是一種比較好的解決方案(在SQL 2005中有更好的緩存解決方法,可以在新數據更新時更新緩存)。
數據序化:開發過CRM的朋友應該都有體會,很多字段需要預留在數據庫中,因為你不了解使用CRM系統的客戶會有一些什麼樣的存儲要求,如:CRM用戶需要保存他客戶的年齡,但是CRM系統設計過程中不可能為這樣一個問題特意的加入這個存儲字段,通常的做法是給一些空字段,用戶使用的時候相應的對他進行初始化。但是導致的結果就是CRM的數據庫慘不忍睹。更可怕的是,如果要進行軟件升級的時候如果需要添加一些原本沒有的字段,非常的麻煩,從實體類到數據庫操作的存儲過程都需要更改。而數據序化可以解決這個問題,其實當我第一次看到CS這種做法的時候是非常興奮的:第一,實現了添加字段不需要重新寫數據操作類,更不需要對相關的存儲過程進行修改。第二,存儲的字段很工整,全部值都保存在兩個字段中,如圖:
先分析一下存儲的數據,首先是PropertyNames字段,“EnableComments:S:0:4:ModerateComments:S:4: 5:EnableRatings:S:9:4:” “EnableComments”其實是在實體類中定義的一個屬性名稱,“:”表示定義完畢,“S: 0:4”表示在PropertyValues字段中的字符從0開始後面4位屬於“EnableComments”的屬性值,同理:“S:4:5”表示,從第四個字符開始,後面5個表示“ModerateComments”的屬性值,以此類推可以獲得PropertyNames所有的字段的值。(記得在 Asp.net Forums時數據的序化是用Binary存儲的,在CS中改為ntext可以對其進行搜索,解決序化後的數據搜索不便的問題。),關於如何進行序化等後續專題加以敘述。
在CS的最底層就是數據庫和存儲過程了,關鍵的幾個表cs_Groups、cs_Sections、 cs_Threads和cs_Posts表分別對應於CommuntyServer.Components命名空間下的Group、Section、 Thread和Post類,Groups是分組,對應論壇來說就是“板塊組”、版塊”、“線索”和“帖子”,對應於Blog就是“博客分組”、“博客” “線索”、“隨筆或者文章”。這個cs_Threads有點難理解,其實它是對相應Section下的Post相關信息的統計與跟蹤,如最後回帖時間、評論總數等等。
上面大致分析了Community Server項目的體系結構,細節部分會在後續的專題中一一的分析。不管怎麼說Community Server是一個相對龐大的工程,要完全的講解與系統的分析還需要很多的文字。