前天剛發過文章介紹了KudyStudio.Web.Activating,今天再次作了修改,並提供源碼給大家。
KudyStudio.Web.Activating下提供了兩個屬性分別是ActivationAttribute、ActivationMethodAttribute,利用它們可以靈活地隨時在你的程序集中註冊一個或多個Appilcation_Start()前/後觸發和Appilcation_End()前觸發的處理事件。 KudyStudio文章目錄
下載源碼KudyStudio.Web.Activating.rar(.Net4.0)
可觸發的函數目標定義如下:
/// <summary> /// Specifies the targets to use for invoking activation methods. /// </summary> [Serializable] public enum ActivationMethodTarget { /// <summary> /// Provides expanded support for ASP.NET application pre-start. /// </summary> PreApplicationStart = 0x0, /// <summary> /// Provides expanded support for ASP.NET application post-start. /// </summary> PostApplicationStart, /// <summary> /// Provides expanded support for before ASP.NET application shutdown. /// </summary> PreApplicationEnd }
先介紹ActivationMethodAttribute,這個屬性允許你以靜態方法方式來向程序集註冊一個或多個觸發函數,並指定觸發函數的目標與執行順序,它的定義如下:
/// <summary> /// Provides expanded support for application activation. /// </summary> [AttributeUsage(AttributeTargets.Assembly, AllowMultiple=true)] public sealed class ActivationMethodAttribute : OrderableAttribute { #region Constructors /// <summary> /// Initializes a new instance of the ActivationMethodAttribute class. /// </summary> /// <param name="type">An object that describes the type of the activation method.</param> /// <param name="methodName">An empty parameter signature that has no return value.</param> public ActivationMethodAttribute(Type type, string methodName) :this(type, methodName, ActivationMethodTarget.PreApplicationStart) { } /// <summary> /// Initializes a new instance of the ActivationMethodAttribute class. /// </summary> /// <param name="type">An object that describes the type of the activation method.</param> /// <param name="methodName">An empty parameter signature that has no return value.</param> /// <param name="methodTarget">The method target for the associated activation method</param> public ActivationMethodAttribute(Type type, string methodName, ActivationMethodTarget methodTarget) { type.ThrowsIfNull("type"); methodName.ThrowsIfNullOrEmpty("methodName"); if (!Enum.IsDefined(typeof(ActivationMethodTarget), methodTarget)) { throw new ArgumentException("The methodTarget is undefined."); } this.Type = type; this.MethodName = methodName; this.MethodTarget = methodTarget; } #endregion #region Properties /// <summary> /// Gets the type that is returned by the associated activation method. /// </summary> public Type Type { get; private set; } /// <summary> /// Gets the associated activation method. /// </summary> public string MethodName { get; private set; } /// <summary> /// Gets or sets the method target for the associated activation method. /// </summary> public ActivationMethodTarget MethodTarget { get; set; } #endregion }
有了ActivationMethodAttribute屬性其實可以滿足基本需求了,但是ActivationAttribute屬性能讓你的代碼組織更清晰,怎麼說?因爲註冊觸發函數時不需要傳入函數名稱,而且方便每個程式元件建立自己的實現類(實現IPreApplicationStart、IPostApplicationStart、IPreApplicationEnd中的一個或多個接口)而不和其它程式元件的註冊事務混在一起。ActivationAttribute屬性這個允許你以實現接口方式來向程序集註冊一個或多個觸發函數,並指定觸發函數的執行順序,定義如下:
/// <summary> /// Provides expanded support for application activation(s). /// </summary> [AttributeUsage(AttributeTargets.Assembly, AllowMultiple=true)] public sealed class ActivationAttribute : OrderableAttribute { #region Constructors /// <summary> /// Initializes a new instance of the ActivationAttribute class. /// </summary> /// <param name="type"> /// An object that implements any activation interface (<see cref="IPreApplicationStart"/>, /// <see cref="IPostApplicationStart"/>, <see cref="IPreApplicationEnd"/>). /// </param> public ActivationAttribute(Type type) { type.ThrowsIfNull("type"); this.Type = type; } #endregion #region Properties /// <summary> /// Gets the type that implements any activation interface. /// </summary> public Type Type { get; private set; } #endregion }
重點的實現就是ActivationManager類的InvokeActivationMethods方法,代碼如下:
private static void InvokeActivationMethods(ActivationMethodTarget target) { List<OrderableAttribute> allAttributes = new List<OrderableAttribute>(); // get all attributes foreach (Assembly assembly in AppAssemblies.Concat(GetCodeAssemblies(target))) { allAttributes.AddRange(GetAttributes<ActivationMethodAttribute>(assembly).Cast<OrderableAttribute>()); allAttributes.AddRange(GetAttributes<ActivationAttribute>(assembly).Cast<OrderableAttribute>()); } // handle all ordered attributes foreach (OrderableAttribute attribute in allAttributes.OrderBy(attr => attr.Order)) { // invokes static method activations ActivationMethodAttribute methodAttribute = attribute as ActivationMethodAttribute; if (methodAttribute != null && methodAttribute.MethodTarget == target) { MethodInfo method = methodAttribute.Type.GetMethod(methodAttribute.MethodName, StaticMethodBindingFlags); if (method != null) { method.Invoke(null, null); continue; // continue next target } // method not found throw new ApplicationException(string.Format("The type {0} doesn't have a static method named {1}.", methodAttribute.Type, methodAttribute.MethodName)); } // try next case: // invokes activations of activation classes that implements any activation interface ActivationAttribute classAttribute = attribute as ActivationAttribute; if (classAttribute != null) { Type type = classAttribute.Type; object activation = null; if (IsValidActivationType(type)) { try { activation = Activator.CreateInstance(type); } catch(Exception ex) { throw new ApplicationException(string.Format("Fail to create instance of type {0}.", methodAttribute.Type), ex); } } else { // invalid activation class throw new ApplicationException(string.Format("The type {0} is not a valid activation class.", type.FullName)); } if (activation != null) { if (target == ActivationMethodTarget.PreApplicationStart && (activation is IPreApplicationStart)) { (activation as IPreApplicationStart).PreApplicationStart(); } else if (target == ActivationMethodTarget.PostApplicationStart && (activation is IPostApplicationStart)) { (activation as IPostApplicationStart).PostApplicationStart(); } else if (target == ActivationMethodTarget.PreApplicationEnd && (activation is IPreApplicationEnd)) { (activation as IPreApplicationEnd).PreApplicationEnd(); } } } } }
.NET 4.0版本新增了一個 PreApplicationStartMethodAttribute 類只提供了一個開始的前觸發支持,那怎麼實現開始後觸發和結束前觸發呢?.Net4.0中提供了一個叫DynamicModuleUtility的類(位於Microsoft.Web.Infrastructure.dll程序集),裏面只有一個方法RegisterModule(Type moduleType),利用它可以動態的添加HttpModule。WebActivator的作者就是巧妙的利用了動態註冊HttpModule。在第一個處理觸發事件的HttpModule初始化時觸發PostApplicationStart,最後一個HttpModule銷燬時觸發PreApplicationEnd,核心原理就這麼兩點,比較簡單,KudyStudio.Web.Activating裏的源碼並沒有作優化,因爲在應用程序週期只觸發一次各個事件,沒必要刻意的去代碼加載速度什麼的。另外提醒一下,WebActivator1.5版本里的觸發順序只在每個獨立程序集有效(不知道是不是作者故意的),KudyStudio.Web.Activating的觸發順序是在整個應用程序的全部程序集都有效的(有效是指排序的範圍),感興趣的朋友請下載源碼分析。
下載KudyStudio.Web.Activating.rar(.Net4.0)
在此再附上測試實例說明吧,ActivatingWeb網站項目中ActivatingTest.cs裏寫了觸發函數的實現與註冊,Global.asax是用於輔助測試的。運行網站的結果如下:
所有觸發函數都按指定的順序執行了,打開Web.config後點擊保存,查看網站根目錄下的ActivatingTest.txt內容爲:
可以看到PreApplicationEnd觸發函數也正常的按順序執行了。
文章到此結束了,如果本文對你有幫助請點擊推薦表示支持,謝謝,轉載時請務必寫明出處。