The ASP. NET mvc3 framework contains a Microsoft. Web. InfrastructureProgramSet, which contains a dynamicmoduleutility object and its registermodule method. it is used to dynamically register the ihttpmodule in the program. generally, the module must be registered before the program starts. Therefore, the program that calls this method will generally mark the preapplicationstartmethod at the beginning, for example:
Using System; Using System. Web; Using Microsoft. Web. Infrastructure. dynamicmodulehelper; [Assembly: preapplicationstartmethod ( Typeof (Myappstart ), " Start " )] Public Class Coolmodule: ihttpmodule { // Implementation not important // Imagine something cool here } Public Static Class Myappstart { Public Static Void Start () {dynamicmoduleutility. registermodule ( Typeof (Coolmodule ));}}
Therefore, if you directly write this call in the application_start method of the global. asax. CS file, it will report"ThisMethod cannot be called during the application'S pre-start initialization stage
"Exception, as shown below:
However, the actual situation is more complicated than the above descriptions. I have a setCode, Developed at the same time in the company and at home. the registration code is directly written in the application_start method, and can be executed normally on the company's computer, that is, no error is reported, and all modules can be correctly registered! This leaves me puzzled. because the code is the same, the referenced class library has never changed, which makes me feel whether it is caused by the execution environment. my company's computer is 64-bit win7, installed vs08, 10, 12, the home is 32-bit win7, only installed 10. so I asked my colleagues to find a 64-bit computer and re-deploy and run it. An error is still reported. just as I had no clue, I suddenly remembered an article from Dudu.ArticleIt is a series of problems encountered after the garden is upgraded from. Net 4.0 to. Net 4.5. Will this be the reason? So I installed 2012 in my home machine and ran it again. Now the answer is clear: Compatibility Between. Net 4.0 and. net.
Before analyzing the problem in detail, you should have some preliminary knowledge.
1. CLR directory structure
After installing a specific version of. net, these dll will be deployed in three locations.
One is located in % WINDOWS % \ Microsoft. net \ framework, which is installed in a different directory than the data version. It is also called the CSC directory. by default, the CSC command is used to compile the program. The path to the Assembly referenced by the program is the root directory, CSC directory, and GAC directory of the program.
From. net 3.0 ,. net installer will deploy the same assembly at % Program Files % \ reference assemblies \ Microsoft \ framework. visual Studio will first reference the Assembly from here. therefore, when vs is used to compile a program, the search path of the Assembly referenced by the program is: the root directory of the program, the reference assemblies directory, and the GAC directory.
The last one is located in % WINDOWS % \ Microsoft. net \ Assembly, also known as the GAC directory, is essentially a directory with multi-level sub-directories. It can deploy different versions of DLL with the same file name at the same time, which is mainly used for program running. by default, when the program is running, the path of the Assembly referenced by the program is the root directory of the program and the GAC directory.
2.. Net version correspondence
We generally call. net2.0, 3.0, 3.5, 4.0, 4.5, or refers to the package version when it is released. It actually consists of the class library, compiler, and runtime, as shown in the following table:
. Net package version |
1 |
1.1 |
2 |
3 |
3.5 |
4 |
4.5 |
Class Library version |
1 |
1.1 |
2 |
3 |
3.5 |
4 |
4.5 |
C # compiler version |
1 |
1.1 |
2 |
2 |
3 |
4 |
4 |
CLR version |
1 |
1.1 |
2 |
2 |
2 |
4 |
4 |
3.. Net update policy
So far,. Net has used two update policies.
4.0 and earlier versions use side-by-side updates. all versions are stored in their respective directories. note that ,. net2.0, 3.0, and 3.5 are incremental updates, that is, they have not modified the existing assembly, but only added some features. for the public part, they use the same assembly.
4.5 uses in-place update, which overwrites all of its files to the 4.0 folder. That is to say, once updated to 4.5, it will certainly run in the 4.5 environment.
Next, let's take a closer look at why the registermodule method of the dynamicmoduleutility object reports an error in the 4.0 environment, and can be executed normally in the 4.5 environment.
First, The Decompilation Method
Public Static VoidRegistermodule (type moduletype ){If(Dynamicmodulereflectionutil. fx45registermoduledelegate! =Null) Dynamicmodulereflectionutil. fx45registermoduledelegate (moduletype );ElseLegacymoduleregistrar. registermodule (moduletype );}
We can also guess from the variable name that this method is actually different for different execution environments. Let's take a look at how dynamicmodulereflectionutil. fx45registermoduledelegate is generated.
Public Static Readonly Action <type> fx45registermoduledelegate =Getfx45registermoduledelegate (); Private Static Action <type> Getfx45registermoduledelegate () {methodinfo Method = Typeof (Httpapplication). getmethod ( " Registermodule " , Bindingflags. Public | bindingflags. Static, Null , New Type [] { Typeof (Type )}, Null ); If (Method = Null ) Return Null ; Return (Action <type>) Delegate. createdelegate ( Typeof (Action <type> ), Method );}
In this way, the original program checks whether the httpapplication object has the registermodule method through reflection. If so, use this method directly.
Next we will view the httpapplication of. Net 4.0 and the httpapplication of 4.5 respectively.
4.0 is as follows
4.5 is as follows
Now I understand that two APIs are added in 4.5. Native supports dynamic registration of ihttpmodule and can be successfully registered in application_start () method.
Now let's look back at how the program handles the 4.0 environment. He calledRegistermodule method of the internal class of legacymoduleregistrar
Private Static ReadonlyDynamicmodulereflectionutil _ reflectionutil =Dynamicmodulereflectionutil. instance;Public Static VoidRegistermodule (type moduletype) {verifyparameters (moduletype );If(_ Reflectionutil! =Null){Lock(_ Lockobj) {_ reflectionutil. throwifpreappstartnotrunning. Invoke (); addmoduletoclassicpipeline (moduletype); addmoduletointegratedpipeline (moduletype );}}}
Haha, you can see from the name. It seems that if you do not register before the program runs, an exception will be thrown. Is that true? Watch againDynamicmodulereflectionutilClass
[Compilergenerated] Private Action <throwifpreappstartnotrunning> K _ backingfield; Public Action throwifpreappstartnotrunning {[compilergenerated] Get { Return This . <Throwifpreappstartnotrunning> K _ backingfield;} [compilergenerated] Private Set { This . <Throwifpreappstartnotrunning> K _ backingfield = Value ;}} Public Static Readonly Dynamicmodulereflectionutil instance = Getinstance (); Private Static Dynamicmodulereflectionutil getinstance (){ Try { If (Fx45registermoduledelegate! = Null ) Return Null ; Dynamicmodulereflectionutil util = New Dynamicmodulereflectionutil (); methodinfo Method = Typeof (Buildmanager). getmethod ( " Throwifpreappstartnotrunning " , Bindingflags. nonpublic | bindingflags. Static, Null , Type. emptytypes, Null ); Util. throwifpreappstartnotrunning = Commonreflectionutil. makedelegate <action> (Method); commonreflectionutil. Assert (util. throwifpreappstartnotrunning ! = Null );...... Return Util ;} Catch { Return Null ;}}
It setsThrowifpreappstartnotrunning is delegated to the throwifpreappstartnotrunning method of the buildmanager class.
Internal Static VoidThrowifpreappstartnotrunning (){If(Prestartinitstage! = Prestartinitstage. duringprestartinit)Throw NewInvalidoperationexception (Sr. getstring ("Method_can_only_be_called_during_pre_start_init"));}
Originally, if the program is not in the pre-running status, an exception will be thrown!
Now, the entire running logic is clear at a glance. if it is running in the 4.5 environment, this method will be delegated to the new API, which supports dynamic addition of ihttpmodule during runtime. if it is running in the 4.0 environment, it will check which stage is registered. if it is registered at runtime, an exception is thrown!
PS: It seems that the coverage update of 4.5 is not very stable. Why cannot we manually choose the running environment?
References:
A strange problem in a hundred years: When ie encounters. NET Framework 4.5
Kindly remind Dudu and other students who intend to upgrade to. NET Framework 4.5
What's new in ASP. NET 4.5 and Visual Studio 2012
Missing referenced assemblies folder for. Net 4.0
C: \ Program Files \ reference assemblies for assemblies to reference in your code
New Reference assemblies location
CLR: GAC directory Construction
. NET Framework Version Parsing