一、关于动态注册的问题
很多人看过汤姆大叔的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
那么就达到了动态注册的目的
public ICollectionLockAndFetchList() { ICollection 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)); }
四、感谢
感谢几日来大家关注,我会继续写好,兄台,求推荐、关注.
大赠送的东西,大家可以好好看看,算是回馈大家的支持