在 ASP.net 2.0 的配置文件中,經常看到 Provider的影子,比如說StarterKit 中的XmlSiteMapProvider以及使用Login Controls時隱含的SqlMembershipProvider。如此眾多的Provider,我猜測它們肯定有共同的父親!查閱了一下資料,果然不出我所料,它們的父親是 ProviderBase。
[圖中的 三個點 代表直接父類的名稱]
我們以Membership為例,來看一下這麼多的Provider是怎麼被系統所使用的,以及使用它們會給我們帶來什麼樣的好處。
LoginControls(包括登錄、創建用戶、修改密碼等控件)是服務器控件,這些服務器控件通過MembershipAPI 來執行相應的操作。MembershipAPI是密封類(System.Web.Security.Membership類),其中定義了很多靜態的方法,包括 CreateUser、DeleteUser等等,但是本身並不提供具體的實現,而是使用MembershipProvider提供的服務。MembershipProvider是一個抽象類,其中也定義了CreateUser、DeleteUser 等方法,SqlMembershipProvider是它的一個實現,可以將Membership的數據持久化到Sql Server 2005中。
1.Strategy
這裡用到了一個非典型的策略模式(strategy)。在典型的策略模式中,Provider應該是通過構造方法注入,而在此處,Membership是一個靜態類,它的Provider和ProviderCollection都是只讀屬性,因此我猜測Provider並非是由其他類注入的,而是由Provider自己通過應用上下文獲取到的。策略模式的作用是將 MembershipAPI 對具體的Provider(比如SqlMembershipProvider)的依賴轉化成了 對抽象類 MembershipProvider 的依賴 + SqlMembershipProvider對MembershipProvider的依賴,從而符合開閉原則(OCP)。這樣可以保證系統的靈活性和效率。
2.IoC
這裡應用的策略模式的一個附帶的好處是實現了 控制反轉(IoC)。通常我們設計分層應用的時候,都是自底層向上層設計,上層的組件使用下層提供的服務並直接依賴於下一層的組件。這樣做的缺點是 造成底層組件很難修改,因為任意的修改(比如修改方法的功能或者名稱)都可能導致上層應用崩潰。使用了策略模式模式以後,我們把MembershipAPI和MembershipProvider抽象類置於同一層(上層)中,底層的SqlMembershipProvider則依賴於上一層(實現MembershipProvider中的抽象方法)。很明顯,我們將上層組件對下層組件的依賴轉化成了下層組件對於上層組件的依賴,這就是控制反轉。好處是給我們帶來了即插即用功能---Provider是可插接的,由於Provider造成的一切系統崩潰都由 Provider 自己負責。
3.Dependency Injection
我們可以實現自己的MembershipProvider,比如說OracleMembershipProvider---使用Oracle來存儲Membership和Profile數據---只要我們繼承MembershipProvider類並覆寫的部分方法即可。那麼我們編寫的 OracleMembershipProvider 或者 系統自帶的 SqlMembershipProvider是如何被 MembershipAPI 使用的呢?這裡有一個重要的概念---依賴注入(Dependency Injection)。依賴注入的意思是將應用程序依賴的組件在運行時注入給應用,依賴注入一般要使用反射技術。
依賴注入通常分為3種形式:
構造注入:通過構造方法將組件注入
設值注入:通過設置屬性的值將組件注入
接口注入:從容器中獲取指定接口的實現
注入過程一般由某種 IoC 容器(比如Spring)完成,而且特別適合由容器完成。很難說在MembershipAPI 中使用了某種典型的依賴注入方式。我們可以這麼說,僅僅使用了反射工廠。因為在這裡,僅僅使用反射已經足夠,不需要復雜的對象裝配過程。
如果不考慮內部的實現過程,我們要做的,僅僅是將我們的實現拷貝到Bin目錄,並在Web.Config文件中system.web節中增加下面的節點:
<Membership>
<providers>
<clear/>
<add name="OracleMembershipProvider" type="Oracle.OracleMembershipProvider"/>
</providers>
</Membership>
其中 type 中定義的就是 我們自定義的Provider的全名稱(包括名字空間)。