Roles de Cloud Services se reciclando e apresentando o erro “System.IO.FileLoadException: Could not load file or assembly”
Por: Leonardo Villar
Você pode se encontrar enfrentando um problema onde, logo após um novo deploy para o seu Cloud Service, as suas roles permaneçam em estado de “iniciando” ou “reciclando”. Nesse caso, o troubleshooting inicial é de acessar remotamente a instancia (RDP) e verificar os logs de evento para procurar possíveis pistas do que possa estar acontecendo. Um problema comum que pode estar acontecendo, é a seguinte exceção:
The description for Event ID 1007 from source Windows Azure Runtime 2.6.0.0 cannot be found. Either the component that raises this event is not installed on your local computer or the installation is corrupted. You can install or repair the component on the local computer.
If the event originated on another computer, the display information had to be saved with the event.
The following information was included with the event:
1044
WaIISHost
Role entrypoint could not be created: System.TypeLoadException: Unable to load the role entry point due to the following exceptions:
-- System.IO.FileLoadException: Could not load file or assembly 'System.Web.Mvc, 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: 'System.Web.Mvc, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35'
WRN: Assembly binding logging is turned OFF.
To enable assembly bind failure logging, set the registry value [HKLM\Software\Microsoft\Fusion!EnableLog] (DWORD) to 1.
Note: There is some performance penalty associated with assembly bind failure logging.
To turn this feature off, remove the registry value [HKLM\Software\Microsoft\Fusion!EnableLog].
---> System.Reflection.ReflectionTypeLoadException: Unable to load one or more of the requested types. Retrieve the LoaderExceptions property for more information.
at System.Reflection.RuntimeModule.GetTypes(RuntimeModule module)
at System.Reflection.RuntimeModule.GetTypes()
at System.Reflection.Assembly.GetTypes()
at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetRoleEntryPoint(Assembly entryPointAssembly)
--- End of inner exception stack trace ---
at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.GetRoleEntryPoint(Assembly entryPointAssembly)
at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.CreateRoleEntryPoint(RoleType roleTypeEnum)
at Microsoft.WindowsAzure.ServiceRuntime.RoleEnvironment.InitializeRoleInternal(RoleType roleTypeEnum)
the message resource is present but the message is not found in the string/message table
Essa exceção, normalmente é gravada nos logs de evento Windows Azure (veja imagem) e quer dizer que algo no seu projeto está fazendo referência à uma versão diferente da versão atual do assembly. Nesse caso acima, o problema aconteceu com o assembly “System.Web.Mvc” onde a versão atual não era a 3.0.0.0 na qual está sendo referenciada, então, é onde acontece a exceção.
A melhor maneira de resolver o problema seria de consertar as referências, para que a versão que você está referenciando seja a mesma que está sendo usada no deploy. Porém, consertar as referências pode tomar muito tempo, então a forma mais rápida é de usar a configuração de bindingRedirect.
Normalmente, quando um novo assembly é adicionado ao seu projeto, é criada automaticamente uma configuração de bindingRedirect no seu web.config (Web Role) ou app.config (Worker Role) para evitar esse tipo de problema.
Entretanto, em Azure Cloud Services, essa configuração não funciona, tendo em vista que os processos WaIISHost (Web Role) e WaWorkerHost (Worker Role) não leem os arquivos de configuração das aplicações (web.config e/ou app.config), ao invés, esses dois processos leem o arquivo <nome da role>.dll.config e é nesse arquivo de configuração que as configurações de Binding Redirect tem que estar. Veja esse artigo para mais detalhes https://azure.microsoft.com/blog/2010/12/02/new-full-iis-capabilities-differences-from-hosted-web-core/
O problema é que nem sempre, esse arquivo de configuração não está no projeto, ou se está, não tem as configurações de binding redirect assim como nos web.config e app.config.
Se você se encontra nessa situação, siga os passos a seguir:
Solution:
1) Abra o arquivo <nome da role>.dll.config que se encontra na pasta “bin” do seu projeto.
2) Verifique se esse arquivo de configuração tem a configuração de BindingRedirect que você precisa. Caso não, siga uma das opções:
a) Copie o conteúdo do web.config ou app.config (tendo em vista que esses nesses arquivos contém a configuração que você precisa) e cole dentro do <nome da role>.dll.config. Esses arquivos podem ter exatamente o mesmo conteúdo.
b) Crie uma entrada com as configurações de Binding Redirect manualmente:
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
<bindingRedirect oldVersion="0.0.0.0-6.0.0.0" newVersion="6.0.0.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
Nota: Para conseguir descobrir o publicKeyToken execute o seguinte comando no PowerShell:
PS C:\Windows\Syetem32> ([system.reflection.assembly]::loadfile( "dll full path" )). FullName
Onde “dll full path” é o caminho onde esta a dll. Por exemplo:
PS C:\WINDOWS\system32> ([system.reflection.assembly]::loadfile("C:\logs\Newtonsoft.Json.dll")).FullName
Você terá o seguinte output:
Newtonsoft.Json, Version=6.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed
3) Adicione o <nome da role>.dll.config ao seu projeto (mesmo nível do web.config ou app.config) e configure o “Copy to Output Directory” como “Copy Always”
4) Faça um novo deploy.
Nota: Como um teste rápido caso não possa fazer um novo deploy, você pode copiar o arquivo <nome da role>.dll.config (já com a nova configuração de assembly binding) e colar dentro na instancia no diretório <driver de aplicação>:\approot\bin e esperar até que o WaHostBootstrapper.exe reinicie o processo WaIISHost.exe ou WaWorkerhost.exe e a role deverá iniciar normalmente. Mas não esqueça de fazer um novo deploy, pois essa configuração manual pode ser perdida.