3

I encountered a strange issue with the startup of web roles. The full repro is quite complex, but I managed to find the root cause so I will put the simplified steps.

My webrole project depends on AssmeblyA which in turn depends on AssemblyB, Version=1.0. The webrole also depends directly on AssemblyB, but Version=2.0. (this all comes from different NuGet packages over which I don't have any control).

In the end, AssemblyB, Version=2.0 is getting copied to the \Bin folder. The website itself is working correctly because in the Web.config there is a binding redirect for AssemblyB to Version=2.0 (which is automatically being put there by nuget client).

However, when webrole is deployed to Azure, it fails to start because AssemblyB, Version=1.0 cannot be loaded.

I suspect this is because web role is first loaded from the Approot directory where web.config has no effect. I also found that I can workaround this problem by generating an app.config for the web-role as well and duplicating all binding redirects there. While this works, it's not very convenient to maintain such setup.

Does anyone know if this is a known issue with Azure web roles?

Tried with Azure SDk 2.4 and 2.5.1, all azure nuget packages up to date, no custom startup code whatsoever.

Update: Here is the exception obtained through IntelliTrace:

Unable to load the role entry point due to the following exceptions: -- System.IO.FileLoadException:

Could not load file or assembly 'Microsoft.Owin, Version=3.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) File name: 'Microsoft.Owin, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'

And here is the stack trace:

CommonLanguageRuntimeLibrary!System.Reflection.RuntimeModule.GetTypes() 
CommonLanguageRuntimeLibrary!System.Reflection.Assembly.GetTypes()  
Microsoft.WindowsAzure.ServiceRuntime.dll!Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetRoleEntryPoint(System.Reflection.Assembly entryPointAssembly = {System.Reflection.RuntimeAssembly})  
Microsoft.WindowsAzure.ServiceRuntime.dll!Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.CreateRoleEntryPoint(Microsoft.WindowsAzure.ServiceRuntime.Implementation.Loader.RoleType roleTypeEnum = IISWeb)    
Microsoft.WindowsAzure.ServiceRuntime.dll!Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.InitializeRoleInternal(Microsoft.WindowsAzure.ServiceRuntime.Implementation.Loader.RoleType roleTypeEnum = IISWeb)  
Microsoft.WindowsAzure.ServiceRuntime.dll!Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.InitializeRole(Microsoft.WindowsAzure.ServiceRuntime.Implementation.Loader.RoleType roleType = IISWeb)  
Microsoft.WindowsAzure.ServiceRuntime.dll!Microsoft.WindowsAzure.ServiceRuntime.Implementation.Loader.RoleRuntimeBridge.AnonymousMethod()   
CommonLanguageRuntimeLibrary!System.Threading.ExecutionContext.RunInternal(System.Threading.ExecutionContext executionContext = {unknown}, System.Threading.ContextCallback callback = {unknown}, object state = {unknown}, bool preserveSyncCtx = {unknown})   
CommonLanguageRuntimeLibrary!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext = {unknown}, System.Threading.ContextCallback callback = {unknown}, object state = {unknown}, bool preserveSyncCtx = {unknown})   
CommonLanguageRuntimeLibrary!System.Threading.ExecutionContext.Run(System.Threading.ExecutionContext executionContext = {unknown}, System.Threading.ContextCallback callback = {unknown}, object state = {unknown}) 
CommonLanguageRuntimeLibrary!System.Threading.ThreadHelper.ThreadStart()

The error occurs because my web role project depends on Owin Version=3.0.1.0, but another DLL (which I get through nuget) depends on Owin Version=3.0.0.0. Hence the manifest mismatch.

So it seems like service runtime is still loading my assemblies in WaIISHost.exe just to check whether I do have RoleEntryPoint or not (I don't). Just as before, as soon as I put myassembly.dll.config with binding redirects copied from web.config into E:\approot\bin, everything starts correctly.

dennis
  • 562
  • 6
  • 21

2 Answers2

1

Can you clarify what exactly you are looking for? You provide the answer in your question (add the redirects to app.config), and you provide a link to the explanation in one of your comments (http://blogs.msdn.com/b/avkashchauhan/archive/2011/01/24/dissection-of-a-windows-azure-sdk-1-3-based-asp-net-web-role-in-full-iis-mode-amp-hwc.aspx).

I can confirm that what you are seeing is the expected behavior and your solution of adding the binding redirects to app.config is correct.

The other potential solution would be to change your RoleEntrypoint code (ie. WebRole.cs) so that it does not depend on AssemblyB, Version=1.0. Whatever you are doing in the RoleEntrypoint class, can it be done from Application_Start so that it runs within IIS?

kwill
  • 10,867
  • 1
  • 28
  • 26
  • He mentions that there is no custom startup code in the WebRole so I guess he can do that. That's what I was trying to find out with the steps provided as well, if it's a role or IIS issue. I guess I should have taken for granted that "failure to startup" really means "my role doesn't start at all". My bad. Thx for jumping in Kevin. I'll remove my answer as it points to the wrong direction. – Panos Mar 29 '15 at 15:54
  • Well, this is not really an answer (not a solution). This is just a dirty workaround. I added a few more details - detailed error message and stack trace. Perhaps, now I am interested in why service runtime does not automatically pick up binding redirects from web.config? I have never seen any recommendation on maintaining both app.config and web.config for web role projects and duplicating all binding redirects in both places. This just seems unnecessary. – dennis Mar 30 '15 at 12:49
  • I meant, of course, that my own answer is not an answer :) – dennis Mar 30 '15 at 20:24
  • The ServiceRuntime loads your role entry point DLL and reflects all classes until it finds one that derives from RoleEntryPoint. If you aren't doing anything in your role entry point then you can just create a dummy entry point that doesn't have the references to AssemblyB. Then you won't need a binding redirect in app.config since Azure will load your dummy entry point into WaIISHost.exe. – kwill Mar 30 '15 at 20:43
  • Not sure how this should work. If you are suggesting to create a dummy entry point _in the same_ web role project, then it wouldn't help because there is still a reference to AssemblyB in that dll. If you mean a dummy entry point in another project, then, well, it will be another role and my initial role will still fail to load. I cannot remove reference to AssemblyB,V=2.0 because it also comes as a nuget dependency. – dennis Mar 31 '15 at 08:49
  • Now that I look again at this issue, don't you think this is a bug in Azure? Having dependencies to different versions of assemblies and using binding redirects is now a _very_ common case because of nuget. Not all authors update their packages regularly so it's easy to get a binding redirect. Then you are in a situation where everything works locally under full IIS, then you deploy a web role to Azure and it fails to start with no good reason. I think when you guys search for entry points in web roles, you should look into web.config for binding redirects. – dennis Mar 31 '15 at 08:51
0

Azure support team provide AzureTools for help this kind of problem. AzureTools have Fusion Logging on/off and easy to collect azure logs.

Once you enable Fusion Logging then you can get more information about missing assembly.

http://blogs.msdn.com/b/kwill/archive/2013/08/26/azuretools-the-diagnostic-utility-used-by-the-windows-azure-developer-support-team.aspx

Takekazu Omi
  • 39
  • 1
  • 3
  • Thanks, I think I understand how the error happens. My question is rather why azure service runtime loads my dlls if I don't have role entry point and even if it does load it, why doesn't it use binding redirects from web.config. – dennis Mar 30 '15 at 12:45