你必須知道ASP.NET知識------關於動態注冊httpmodule(對不起湯姆大叔)


一、關於動態注冊的問題

  很多人看過湯姆大叔的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));
         
}

   

四、感謝

  感謝幾日來大家關注,我會繼續寫好,兄台,求推薦、關注.

大贈送的東西,大家可以好好看看,算是回饋大家的支持


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM