一、關於動態注冊的問題
很多人看過湯姆大叔的MVC之前的那點事兒系列(6):動態注冊HttpModule
,其實湯姆大叔沒有發現httpmodule動態注冊的根本機制在哪里.
亦即:怎么動態注冊?為什么能夠動態注冊?
湯姆大叔給了如下開篇
通過前面的章節,我們知道HttpApplication在初始化的時候會初始化所有配置文件里注冊的HttpModules,那么有一個疑問,能否初始化之前動態加載HttpModule,而不是只從Web.config里讀取?
答案是肯定的, ASP.NET MVC3發布的時候提供了一個Microsoft.Web.Infrastructure.dll文件,這個文件就是提供了動態注冊HttpModule的功能,那么它是如何以注冊的呢?我們先去MVC3的源碼看看該DLL的源代碼。
其實httpmodule動態注冊,是ASP.NET框架內部自己提供的機制,和MVC沒有關系,也就是說有沒有MVC,ASP.NET自己都會提供這個機制(沒有研究其他
.NET版本,至少在.NET 4.5下是如此的,這是不含MVC框架的情況下)
二、關於httpmodule的初始化
接着前面的章節,我們開始論述,以下面的代碼回顧
// System.Web.HttpApplication internal void InitInternal(HttpContext context, HttpApplicationState state, MethodInfo[] handlers) { this._state = state; PerfCounters.IncrementCounter(AppPerfCounter.PIPELINES); try { try { this._initContext = context; this._initContext.ApplicationInstance = this; context.ConfigurationPath = context.Request.ApplicationPathObject; using (new DisposableHttpContextWrapper(context)) { if (HttpRuntime.UseIntegratedPipeline) { try { context.HideRequestResponse = true; this._hideRequestResponse = true; this.InitIntegratedModules(); goto IL_6B; } finally { context.HideRequestResponse = false; this._hideRequestResponse = false; } } this.InitModules();//注意這里,這里是初始化所有的module,其中包括了配置文件中的和動態注冊的 IL_6B: if (handlers != null) { this.HookupEventHandlersForApplicationAndModules(handlers); } this._context = context; if (HttpRuntime.UseIntegratedPipeline && this._context != null) { this._context.HideRequestResponse = true; } this._hideRequestResponse = true; try { this.Init(); } catch (Exception error) { this.RecordError(error); } } if (HttpRuntime.UseIntegratedPipeline && this._context != null) { this._context.HideRequestResponse = false; } this._hideRequestResponse = false; this._context = null; this._resumeStepsWaitCallback = new WaitCallback(this.ResumeStepsWaitCallback); if (HttpRuntime.UseIntegratedPipeline) { this._stepManager = new HttpApplication.PipelineStepManager(this); } else { this._stepManager = new HttpApplication.ApplicationStepManager(this); } this._stepManager.BuildSteps(this._resumeStepsWaitCallback); } finally { this._initInternalCompleted = true; context.ConfigurationPath = null; this._initContext.ApplicationInstance = null; this._initContext = null; } } catch { throw; } }
private void InitModules() { HttpModulesSection httpModules = RuntimeConfig.GetAppConfig().HttpModules;//配置文件中的 HttpModuleCollection httpModuleCollection = httpModules.CreateModules();//動態注冊的 HttpModuleCollection other = this.CreateDynamicModules(); httpModuleCollection.AppendCollection(other); this._moduleCollection = httpModuleCollection; this.InitModulesCommon(); }
private HttpModuleCollection CreateDynamicModules() { HttpModuleCollection httpModuleCollection = new HttpModuleCollection(); foreach (DynamicModuleRegistryEntry current in HttpApplication._dynamicModuleRegistry.LockAndFetchList()) { HttpModuleAction httpModuleAction = new HttpModuleAction(current.Name, current.Type);
//初始化module原來就是在這里 httpModuleCollection.AddModule(httpModuleAction.Entry.ModuleName, httpModuleAction.Entry.Create()); } return httpModuleCollection;//最終都給了this._moduleCollection }
可以想象:最后某個地方調用this._moduleCollection就能得到初始化操作.其實就是這里進行各個module初始化的操作的:
private void InitModulesCommon() { int count = this._moduleCollection.Count; for (int i = 0; i < count; i++) { this._currentModuleCollectionKey = this._moduleCollection.GetKey(i); this._moduleCollection[i].Init(this);//這里其實就是調用了各個httpmodule的初始化方法 } this._currentModuleCollectionKey = null; this.InitAppLevelCulture(); }
但是,這個
this._moduleCollection
集合的數據也是來至於
HttpApplication._dynamicModuleRegistry.LockAndFetchList()
public ICollection<DynamicModuleRegistryEntry> LockAndFetchList(),如果這里的數據中加入我們動態注冊的module,
那么就達到了動態注冊的目的
public ICollection<DynamicModuleRegistryEntry> LockAndFetchList() { ICollection<DynamicModuleRegistryEntry> entries; lock (this._lockObj) { this._entriesReadonly = true; entries = this._entries;//即:我們往這個里面_entries加入我們需要的module就達到效果 } return entries; }
恰巧在httpapplication中,有一個注冊module的方法,估計很多人都沒有用過
public static void RegisterModule(Type moduleType) { RuntimeConfig appConfig = RuntimeConfig.GetAppConfig(); HttpRuntimeSection httpRuntime = appConfig.HttpRuntime; if (httpRuntime.AllowDynamicModuleRegistration) { HttpApplication.RegisterModuleInternal(moduleType); return; } throw new InvalidOperationException(SR.GetString("DynamicModuleRegistrationOff")); }
internal static void RegisterModuleInternal(Type moduleType) { HttpApplication._dynamicModuleRegistry.Add(moduleType);//從這里,我們可以看出該方法恰巧就可以動態注冊. }
public void Add(Type moduleType) { if (moduleType == null) { throw new ArgumentNullException("moduleType"); } if (!typeof(IHttpModule).IsAssignableFrom(moduleType)) { string message = string.Format(CultureInfo.CurrentCulture, SR.GetString("DynamicModuleRegistry_TypeIsNotIHttpModule"), new object[] { moduleType }); throw new ArgumentException(message, "moduleType"); } lock (this._lockObj) { if (this._entriesReadonly) { throw new InvalidOperationException(SR.GetString("DynamicModuleRegistry_ModulesAlreadyInitialized")); } this._entries.Add(new DynamicModuleRegistryEntry(DynamicModuleRegistry.MakeUniqueModuleName(moduleType), moduleType.AssemblyQualifiedName)); } }
恰巧其機制就重合了,也就是動態注冊的效果來源於這里.
this._entries.Add
但我們實際中會發現,我們如果純粹地調用httpapplication的RegisterModule方法是達不到目的,例如我們在Global文件中加入以下代碼
public Global() { //DynamicModuleRegistry HttpApplication.RegisterModule(typeof(CustomModule)); InitializeComponent(); }
系統拋出異常:
行 26: { 行 27: //DynamicModuleRegistry 行 28: HttpApplication.RegisterModule(typeof(CustomModule)); 行 29: InitializeComponent(); 行 30:
源文件: c:\Users\qscq\Documents\SharpDevelop Projects\ASPNET_ST_1\ASPNET_ST_1\Global.asax.cs 行: 28
這又是為什么呢?
其實異常的來源在於
在 System.Web.DynamicModuleRegistry.Add(Type moduleType)
public void Add(Type moduleType) { if (moduleType == null) { throw new ArgumentNullException("moduleType"); } if (!typeof(IHttpModule).IsAssignableFrom(moduleType)) { string message = string.Format(CultureInfo.CurrentCulture, SR.GetString("DynamicModuleRegistry_TypeIsNotIHttpModule"), new object[] { moduleType }); throw new ArgumentException(message, "moduleType"); } lock (this._lockObj) { if (this._entriesReadonly) { throw new InvalidOperationException(SR.GetString("DynamicModuleRegistry_ModulesAlreadyInitialized")); } this._entries.Add(new DynamicModuleRegistryEntry(DynamicModuleRegistry.MakeUniqueModuleName(moduleType), moduleType.AssemblyQualifiedName)); } }
也就是說,_entriesReadonly此刻已經是true了, 也就是在其true之前加入module就沒有問題了.那什么時候變成true的呢?其實就是前面講到的httpapplication自己初始化的時候,即InitModules方法中.也就是說,我們要搶在系統調用該方法前,調用
HttpApplication.RegisterModule方法就可以了.
三、總結
如上面說的那樣,系統自己調用了HttpApplication.RegisterModule,在調用此方法前,我們能夠動態注冊module即可.
湯姆大叔給出了
[assembly: PreApplicationStartMethod(typeof(WebApplication1.Test.PreApplicationStartCode), "PreStart")]
該方法顯然可以達到目標.但我們可以直接來得更簡單些,直接Global加一個靜態構造函數
即:
static Global(){ HttpApplication.RegisterModule(typeof(CustomModule)); }
四、感謝
感謝幾日來大家關注,我會繼續寫好,兄台,求推薦、關注.
大贈送的東西,大家可以好好看看,算是回饋大家的支持