Azure Role does not start, Could not load file or assembly 'System.Web.Mvc, Version=4.0.0.0
The sub title of this post is also; how to do pre-web.config assembly binding for Windows Azure.
So I upgraded my ASP.NET MVC 4 Web Application to MVC 5 and published to my Windows Azure Web Role/Worker Role.
What might a little unusual is that I have my web application and the worker role in the same project/role, since the workload is currently very little. In fact, I have a Web Role that spawn background tasks using classes that inherits from RoleEntryPoint.
After upgrade and publish, my worker role did not start. And even though I had enabled all the logging I could think of, the only error I got was that WaIISHost.exe has crashed with an Exception. No stack trace. Bummer.
So I had to remote desktop into one of the roles, firing up DebugView.exe and I deployed. This is what I got:
[3464] Microsoft.WindowsAzure.ServiceRuntime Critical: 201 :
[3464] Role entrypoint could not be created:
[3464] System.TypeLoadException: Unable to load the role entry point due to the following exceptions:
[3464] -- System.IO.FileLoadException: Could not load file or assembly 'System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
[3464] File name: 'System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
[3464]
[3464] WRN: Assembly binding logging is turned OFF.
[3464] To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1.
[3464] Note: There is some performance penalty associated with assembly bind failure logging.
[3464] To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].
[3464]
[3464] ---> System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
[3464] at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
[3464] at System.Reflection.RuntimeModule.GetTypes()
[3464] at System.Reflection.Assembly.GetTypes()
[3464] at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetRoleEntryPoint(Assembly entryPointAssembly)
[3464] --- End of inner exception stack trace ---
[3464] at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetRoleEntryPoint(Assembly entryPointAssembly)
[3464] at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.CreateRoleEntryPoint(RoleType roleTypeEnum)
[3464] at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.InitializeRoleInternal(RoleType roleTypeEnum)
Hmm.. thats odd, since when I upgraded to MVC 5, I also has these assembly bindings in my web.config:
1
2
3
4
<dependentAssembly>
<assemblyIdentity name="System.Web.Mvc" publicKeyToken="31BF3856AD364E35" culture="neutral"/>
<bindingRedirect oldVersion="0.0.0.0-5.0.0.0" newVersion="5.0.0.0"/>
</dependentAssembly>
So let’s enable Fusion logging. It gave me a little bit more details:
[1988] Microsoft.WindowsAzure.ServiceRuntime Critical: 201 :
[1988] Role entrypoint could not be created:
[1988] System.TypeLoadException: Unable to load the role entry point due to the following exceptions:
[1988] -- System.IO.FileLoadException: Could not load file or assembly 'System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35' or one of its dependencies. The located assembly's manifest definition does not match the assembly reference. (Exception from HRESULT: 0x80131040)
[1988] File name: 'System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
[1988]
[1988] Assembly manager loaded from: D:\Windows\Microsoft.NET\Framework64\v4.0.30319\clr.dll
[1988] Running under executable F:\base\x64\WaIISHost.exe
[1988] --- A detailed error log follows.
[1988]
[1988] === Pre-bind state information ===
[1988] LOG: DisplayName = System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
[1988] (Fully-specified)
[1988] LOG: Appbase = file:///F:/approot/bin
[1988] LOG: Initial PrivatePath = F:\approot\bin
[1988] Calling assembly : Mvc.Mailer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null.
[1988] ===
[1988] LOG: This bind starts in default load context.
[1988] LOG: Using application configuration file: F:\base\x64\WaIISHost.exe.Config
[1988] LOG: Using host configuration file:
[1988] LOG: Using machine configuration file from D:\Windows\Microsoft.NET\Framework64\v4.0.30319\config\machine.config.
[1988] LOG: Post-policy reference: System.Web.Mvc, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
[1988] LOG: Attempting download of new URL file:///F:/approot/bin/System.Web.Mvc.DLL.
[1988] WRN: Comparing the assembly name resulted in the mismatch: Major Version
[1988] ERR: Failed to complete setup of assembly (hr = 0x80131040). Probing terminated.
[1988]
[1988] ---> System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
[1988] at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
[1988] at System.Reflection.RuntimeModule.GetTypes()
[1988] at System.Reflection.Assembly.GetTypes()
[1988] at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetRoleEntryPoint(Assembly entryPointAssembly)
[1988] --- End of inner exception stack trace ---
[1988] at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetRoleEntryPoint(Assembly entryPointAssembly)
[1988] at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.CreateRoleEntryPoint(RoleType roleTypeEnum)
[1988] at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.InitializeRoleInternal(RoleType roleTypeEnum)
Ok, so now I know which DLL tries to load the older MVC 4 assemblies, but why isn’t my binding taking care of that?
As it turns out, since I am using my combined codebase for the worker role, essentially my Application.Web.dll, the web.config is of course not used when the WaIISHost.exe is loading that assembly. So what I had to do is to create a Application.Web.dll.config in my project root, set it to Content and Copy Always(or Copy If Newer), and copy paste in all my Assemlby Binding elements.
Now, the AZure WAIIHost.exe picks it up correctly, and my role is started correctly.