Membership三步曲之進階篇

發表於2016-05-08

  Membership 三步曲之進階篇 – 深入剖析Provider Model

本文的目標是讓每一個人都知道Provider Model 是什麼,並且能靈活的在自己的專案中使用它。

在入門篇中我們已經從0開始將Membership整合到一個空的MVC站點中,並且與ASP.NET的許可權管理體系相結合。本篇(進階篇)將剖析Membership的設計理念以及它的結構。別忘了我們還有高階篇我們將會擴充套件自己的MembershipProvider和RolesProvider,目地是直接利用我們老系統中已經存在的使用者表和角色表,也就是用我們已經存在的資料庫去整合Membership。

我們瞭解到Membership的重要組成部分是MembershipProviders。 要理解Membership的架構設計,我們首先要理解Provider。Provider 的全稱是 Provider Model (中文是提供程式模型),它早就已經不是什麼新鮮事了,它是在ASP.NET 1.1的時候被 Rob Howard 設計出來的,從ASP.NET 2.0時代,它就已經開始大行其道了。到現在的MVC中,各種Provider已經隨處可見了, 貌似微軟對它特別青睞。下面我們就來好好看一看Provider到底是個神馬東西,Membership是如何利用它的!

目錄

  1. 隨處可見的Provider
  2. Provider並不是一種設計模式
    1. 策略模式
    2. 工廠方法
    3. 單件模式
    4. 外觀模式
  3. Provider Model 的配置及初始化框架
  4. 利用Provider Model實現記日誌元件 
  5. 參考引用

隨處可見的Provider

各位Provider們,先來和大家混個眼熟吧,相信下面的幾種Provider大家並不會覺得陌生:

前面兩種Provider我們在Membership入門篇裡面已經接觸到了,後面兩種我想也不用說了吧? 而在MVC中的下面幾個兄弟,你們都見識過吧?

那麼Provider到底是起了一個什麼樣的作用呢?為什麼微軟要如此廣泛的使用它?又或者我們是不是可以在自己的專案中使用這種設計呢?

首先,一個Provider可以是一個類,或者好幾個類共同組成的一個模組,它們提供了一些特定的功能,這些特定的功能要麼用介面中的方法或者抽象類的抽象方法暴露出來(.NET框架中一般使用抽象類)。這就是ProvderModel的第一要素:具要良好定義的公有API。比如說我們的MembershipProvider提供了GetUser, CreateUser, ChangePassword等與使用者管理息息相關的功能,這些API並不是越全越好,而是越精越好,只定義最核心的功能,其它的可以新增自己的擴充套件去實現,正是小巧又精悍呀。

其次,要有一種配置機制。這種配置機制能夠將具體的Provider與我們定義的功能集繫結起來。比如說我們有SqlMembershipProvider,還有ActiveDirectoryMemberProvider,他們都是抽象類MembershipProvider的子類。我還可以自己去新增其它的子類以不同的方式實現同樣的功能,那麼我就需要一種靈活的方法將我的子類加入到程式中。ASP.NET是通過web.config裡面的配置結點實現的。

最後,我們還需要有一種載入機制,通過這種載入機制(我們下面會探討這種載入機制),我們可以讀取配置並建立Provider的具體例項傳遞給我們的功能API。.NET已經為我們提供好了一些類來幫助我們達到簡單配置和初始化Providers的目地,我們馬上就會說到。

那麼Provider model能給我們帶來哪些好處呢?

  • 提高靈活性和可擴充套件性
  • 隔絕功能程式碼和具體的資料訪問程式碼
  • 讓擴充套件變得簡單。既可以從抽象類繼承,也可以從其它具體Provider繼承,只實現不一樣的地方,最後只需要輕鬆配置就可以搞定。

拿Membership來舉例,核心功能通過Membership呼叫完成。而Membership並不負責具體的實現,它定義了公開良好的介面在MembershipProvider中。 所以Membership實際上並不處理業務程式碼,大量的功能是呼叫具體的Provider來實現的。這就起到了隔絕的作用,使用者資訊可以儲存在SQL資料庫中,還可以儲存在域控制器中,這些Membership不用關心,只需要交給具體的Provider就可以了。同理,如果我們想讓使用者儲存到其它地方,只需要新增自己的Provider來實現就可以了。

Membership.GetUser的程式碼

所以最後怎麼樣去實現,實現上還是看我們具體配置的Provider如何去做。

Provider並不是一種設計模式

Provider在最開始(ASP.NET 1.1)的時候其實是一種設計模式,全稱是 Provider Model Design Pattern,但是到了ASP.NET2.0 的時候其實就直接叫Provider Model 了(具體叫不叫設計模式,是不明確的,還有的地方叫Provider Pattern,在這裡我們就不做爭辯了)。 但是實際上我們會在Provider Model身上發現好幾種設計模式的影子,比如說:

  • 策略模式
  • 工廠方法模式
  • 單一模式
  • 外觀模式

本文不會詳細討論這四種設計模式,但是為了讓大家更好的理解Provider Model,我們來簡單的結合Provider Model的結構提一下每個設計模式的定義。

2.1 策略模式

關於策略模式有一個故事,話說老王有時候開車特別快,開的快有時候就會被警察逮住。但是有時候遇到的是比較好說話的警察可能不會扣他的分,只是口頭警告一下,有時候會遇到比較嚴格又正直的警察一定要扣分。但是在沒有被逮住之前,我們都不知道那個會是一個好說話的警察還是一個嚴格又正直的警察。

有沒有發現這個類圖和我們上面的Membership的類圖很像? 簡單一句話概括策略模式:將一個特性的主要功能抽象出來,允許不同的實現方式,在執行時可以任意切換到不同的實現。MembershipProvider抽象類為我們定義了一組具體的API, 而Membership類則負責呼叫這些API,但是Membership並不在乎現在是哪一個實現。

2.2 工廠方法模式

工廠方法(如果想詳細瞭解工廠方法的同學,可以參考Terry Lee的這篇部落格)的關鍵在於: 使用者不需要知道也不用知道哪一個具體物件會被建立。對於Membership類來說,它會呼叫具體的Provider相關的方法而不用知道當前是哪一個Provider在工作。 在Membership內部有一個名叫Provider的屬性,它的型別是基類- 抽象類MembershipProvider。而它的初始化是藉助於.NET為我們提供的一套配置和初始化的基礎框架來實現的,這兩套框架我們馬上就會說到了。

2.3 單件模式

單件模式簡單的說即在整個應用程式的生命週期內,某個類的例項至始至終都是同一個。在Membership的內部,維護著一個Provider的屬性,這就是singleton的實現。

2.4 外觀模式

對外觀模式淺顯易懂的定義是“為一組分佈在不同的子系統或者不同地方的介面提供一個統一的入口點”。大家可以對比一下,所有的和Membership相關的功能都是通過呼叫Membership完成的,即使裡面有一些功能是由MembershipUser和其它的類來實現,但是沒有關係,Membership給這些所有的功能作了一個統一。這就是傳說中的外觀模式。

Provider Model的配置及初始化框架

Provider Model之所以能在.NET的世界裡面大放光彩,不僅僅是因為它提供了我們上述的功能及特性,而是它還提供了這樣的一套配置及初始化框架,從而使得我們在.NET中使用Provider Model中使用Provider Model是那麼的容易。下面我們就來看看它是如何做到的。

我們在上一篇中列舉了Membership中使用到的一些型別,但是用到起到讀取配置作用的實際上是MembershipSection這個類,它會幫助我們把配置檔案中membership結點資訊載入到一個MembershipSection的例項中。我們可以來看看Membership中的Initialize方法:

而初始化是由System.Web.Configuration.ProvidersHelper來完成的,它裡面只包含兩個公有的靜態方法: InstantiateProviders和InstantiateProvider。大家可以看到前者只不過是一個包裝方法,其實是通過遍歷呼叫後者來載入所有的provider例項的。後者的程式碼我就不貼了,其實就是用了反射去建立物件而已,還記得我們已經將具體型別配置到config檔案去了麼?

有了這一套配置和初始化框架,才有了我們在config檔案中的簡單操作。

利用Provider Model實現記日誌元件

記日誌元件也已經是被炒了很多次的話題,既然都炒了那麼多次了,也不在乎我再炒一次吧? 其實.NET本身提供的一些事件記錄器也是基於Provider Model來實現的,既然我們學習了Provider Model,那就來自己動手實現一個簡單的日誌元件小小實戰一下吧。

我們要實現的功能很簡單,只有能夠寫入訊息到一個檔案就可以了,但是我們要求可以按不同的格式寫入,可以是純文字的,可以是XML的,當然你還可以擴充套件成任意其它你想要的格式。 對於呼叫者來說,它只需要輕鬆呼叫就可以了。並且我們要實現可以在config檔案中靈活配置使用哪一種格式來記錄我們的日誌。

我們的LogManager是對外暴露的唯一物件,裡面的方法和屬性都是靜態的,和Membership類一樣,它不負責具體的實現,所有的實現都是轉交給具體的Provider的。

 LogManager.Log的程式碼

初始化Provider的程式碼

  TextLogProvider.WriteLog的程式碼

XmlLogProvider我就不實現了,如果大家有興趣可以自己動手嘗試一下,還是老樣子附上整個專案的原始碼。Membership三步曲最後一篇,我們將實現自己的MembershipProvider利用EF來完成的使用者的管理。下週見!

下載原始碼:http://pan.baidu.com/s/1jGI9dhs  (覺得有用的同學就幫助點個推薦吧,您的滿意,我的動力!)

參考引用

相關文章