BlogEngine中的Extension设计的很好。下面就来说说如何实现类似的Extension以此给你的项目带来更好的扩展性。为了说明的方便,这里引入BlogEngine中的一个Extension来讲解。这个extension用来将每次文章发表的一些信息写入到日志文件中。下面是demo的项目图以便大家对其有个总体认识。
如果要实现Extension那么系统在设计的时候就应该留下许多扩展点给我们。那这个日志记录来说就是在发表文章的前后分别定义两个事件分别表示发表前和发表后的扩展点。Page类代码如下:
public class Page { public static event EventHandler PagePosting; public static event EventHandler PagePosted; public virtual void OnPagePosting(Page page, EventArgs e) { if (PagePosting != null) { PagePosting(page, e); } } public virtual void OnPagePosted(Page page, EventArgs e) { if (PagePosted != null) { PagePosted(page, e); } } public void PostPage() { OnPagePosting(this, null); //发表文章 OnPagePosted(this, null); } }
如此一来,我们只要在其他地方注册这两个事件,这样就能形成扩展了。那么在哪里注册这两个事件呢?在我们写的Extension中。Logger代码如下:
[ExtensionAttribute("www.cnblogs.com/qianlifeng")] public class Logger { static Logger() { Page.PagePosting += posting; } public static void posting(object o,EventArgs e) { StreamWriter sw = new StreamWriter("c:\\d.txt", true); sw.WriteLine(DateTime.Now + " posting"); sw.Close(); } }
在这个类中顶部标明了属性ExtensionAttribute属性(关于这个类的定义和作用下面再说)。该类中有个静态构造函数,大家都知道静态构造函数会在类初始化的时候执行一次,利用这个机会就可以将发表文章的事件注册到我们的方法posting中了。
问题又来了,那么这个Logger扩展到底什么时候被执行才好呢?答案是在整个网站启动时执行,即在Global.asax下的Application_Start来启动加载这些extension。因为是程序运行的时候是不知道你到底有哪些extension的,所以这里加载的时候使用反射来加载这些类(加载的时候这些静态构造函数就会被执行了)。所以,下面来看ExtensionManager类:
public class ExtensionManager { public static void LoadExtensions() { Assembly asy = Assembly.Load("App_Code"); Type[] type = asy.GetTypes(); foreach (var item in type) { object[] o = item.GetCustomAttributes(typeof(ExtensionAttribute), false); foreach (var i in o) { if (i.GetType().Name == "ExtensionAttribute") { asy.CreateInstance(item.FullName); } } } } }
在LoadExtension方法中,通过加载App_Code文件集来获得extension类,这里要说明的是因为我们是将所有的Extension类放在App_Code中的,所以asp.net动态编译后就会将App_Code中的类编译成一个App_Code.随机数的文件集。如果我们要获得其中的文件集可以通过”App_Code”或者”__Code”(两个下划线)的全名称来获得该程序集。然后在将这些类中的标明了ExtensionAttribute属性的类拿出来并创建实例。现在知道ExtensionAttribute是干嘛用的吧。说白了就是起到一个标识的作用。
最后我们再将整个过程梳理一遍。