還記得我在部落格園第一篇部落格就是學習委託和事件的學習筆記,呵呵,轉眼已經在部落格園待了一年多了,學到的東西確實是很多,感謝部落格園裡面辛勤寫博的各位,給我們帶來了寶貴的學習資料,傳遞著分享知識和技術的快樂。
入正題,一直以來我都在尋求基於asp.net的外掛程式的實現方式,有試過動態載入使用者控制項實現,感覺有些沉重,苦思很久,偶然之間想到了第一篇筆記,原來苦苦得尋求,卻在燈火闌珊處,呵呵。其實外掛程式主要是提供可拓展點,其實現實中的需求的外掛程式所要實現的好像也只是拓展操作之前或者操作之後,有點像AOP,那如果程式能提供這些拓展點,外掛程式功能就可以基本實現了!代碼實現思路如下:
1namespace MyPlug
2{
3 public class ArticleReadingArg : EventArgs
4 {
5 public Article TheArticle { get;set;}
6 }
7 public class ArticlePage : System.Web.UI.Page
8 {
9 public static event EventHandler<ArticleReadingArg> OnReading;
10 protected override void OnInit(EventArgs e)
11 {
12 if (OnReading != null)
13 {
14 OnReading(EventArgs.Empty, new ArticleReadingArg(GetArticle(WebRequest.GetValue("ID"))));
15 }
16 base.OnInit(e);
17 }
18 }
首先是提供一個拓展點,假設我們要實現新聞管理系統的新聞訪問統計外掛程式,那我們要先提供一個可拓展點 代碼如下這是一個頁面的基類,他曝露出了一個靜態事件OnReading提供拓展者訂閱頁面瀏覽前的事件,當然,其實Page類也有很多事件可以訂閱,不過並沒有公開給其他類訂閱或調用。好了,弄好這一步,基本條件已經成熟啦,因為外部類可以訂閱到Onreading這個事件,並且可以時間的參數是可以訪問到相對應的新聞實體,控制權就轉移到訂閱者手中了。
1 public class CounterPlug
2 {
3 static CounterPlug()
4 {
5 if(this.Enable)ArticlePage.OnReading += AddCount;
6 }
7 public static void AddCount(object sender, ArticleReadingArg e)
8 {
9 e.TheArticle.Hit += 1;
10 }
11 }
如果你用的asp.net模型是WebSite模型,那麼很方便,利用本身帶來的動態編譯,可以很方便得實現後續的使用和管理,如果不是,可以編譯成獨立的dll,再利用反射建立執行個體,這裡利用了.net靜態初始化函數的特點-只在該類型的某個靜態成員第一次被訪問或該類型第一次執行個體化時執行,具體請查看msdn。我們這裡用建立執行個體的形式啟用這個靜態函數,以達到事件的訂閱。我們注意到外掛程式載入是有條件的,這個條件可以根據設定檔,也可以根據資料庫資料擷取。下面是啟用的簡單代碼,也就是你點擊啟用之後執行的
Activator.CreateInstance<MyPlug.CounterPlug>();
因為建立了一個執行個體,所以靜態函數執行了,事件也被訂閱了!所以外掛程式的功能就會執行!
靜態變數的生命週期是隨著應用程式的結束而結束的,所以應該不會冒冒然就消失了的。好啦,到這裡就實現了外掛程式的啟用了,外掛程式的卸載也挺取巧的,本來想到應用程式定義域這個概念,想想還是不妥,這玩意要一定的信任層級,最後想到webConfig每次修改都會重啟應用程式,嘻嘻,這個本來很討厭的特性,在這裡還有得用,關閉外掛程式時修改配置記錄問Disenable之外,修改下WebConfig的最後修改時間
string configFile=HttpContext.Current.Server.MapPath("~/Web.config");
System.IO.File.SetLastAccessTimeUtc(configFile,DateTime.UtcNow);
可以達到重啟的功能,這樣實現也有不好的地方,每次外掛程式卸載都要都會引起重新編譯,確實不太好,不過系統也不是老是在載入卸載外掛程式,主要是在不修改原始碼的前提下進行拓展,目的確實實現了。如果你有更好的外掛程式實現方式,不妨分享一下,期待。