I have been thinking to start writing blog since long. So here it goes!

Recently I got a chance to work with a customer who was trying to deploy an ASP.NET web site using WDP (Web Deployment Project which was shipped with Visual Studio 2008 SP1). This web site had WCF (.NET 3.5) services as a part of it as well.  For deployment we were using below options in WDP -

o    "Merge all output to single Assembly" was selected

o    “Allow this precompiled site to be updatable” wasn't selected

After deploying the site we started getting below exception while trying to browse the .svc files –

Server Error in '/MyWebSite' Application.


Could not load file or assembly 'App_global.asax, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.

 

Exception Details: System.IO.FileNotFoundException: Could not load file or assembly 'App_global.asax, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified

An unhandled exception was generated during the execution of the current web request. Information regarding the origin and location of the exception can be identified using the exception stack trace below.

 

Assembly Load Trace: The following information can be helpful to determine why the assembly 'App_global.asax, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' could not be loaded.

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].

Stack Trace:

[FileNotFoundException: Could not load file or assembly 'App_global.asax, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified.]

System.Reflection.Assembly._nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) +0

System.Reflection.Assembly.nLoad(AssemblyName fileName, String codeBase, Evidence assemblySecurity, Assembly locationHint, StackCrawlMark& stackMark, Boolean throwOnFileNotFound, Boolean forIntrospection) +43

System.Reflection.Assembly.InternalLoad(AssemblyName assemblyRef, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +127

System.Reflection.Assembly.InternalLoad(String assemblyString, Evidence assemblySecurity, StackCrawlMark& stackMark, Boolean forIntrospection) +142

System.Reflection.Assembly.Load(String assemblyString) +28

System.ServiceModel.Activation.ServiceHostFactory.CreateServiceHost(String constructorString, Uri[] baseAddresses) +162

System.ServiceModel.HostingManager.CreateService(String normalizedVirtualPath) +11656092

System.ServiceModel.HostingManager.ActivateService(String normalizedVirtualPath) +42

System.ServiceModel.HostingManager.EnsureServiceAvailable(String normalizedVirtualPath) +479

[ServiceActivationException: The service '/MyWebSite/service.svc' cannot be activated due to an exception during compilation.  The exception message is: Could not load file or assembly 'App_global.asax, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null' or one of its dependencies. The system cannot find the file specified..]

System.ServiceModel.AsyncResult.End(IAsyncResult result) +11527290

System.ServiceModel.Activation.HostedHttpRequestAsyncResult.End(IAsyncResult result) +194

System.ServiceModel.Activation.HostedHttpRequestAsyncResult.ExecuteSynchronous(HttpApplication context, Boolean flowContext) +176

System.ServiceModel.Activation.HttpModule.ProcessRequest(Object sender, EventArgs e) +278

System.Web.SyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() +68

System.Web.HttpApplication.ExecuteStep(IExecutionStep step, Boolean& completedSynchronously) +75


Version Information: Microsoft .NET Framework Version:2.0.50727.3053; ASP.NET Version:2.0.50727.3053

I was able to reproduce the same issue with sample application as well. After digging in further, I found that after the compilation step, WCF stores the list of referenced assemblies into the customString attribute in the build result (service.svc.cdcab7d2.compiled), including App_Global. It seems there is an incorrect assumption here that those assemblies will always be there, which is not necessarily the case in Web Deployment Projects (aspnet_merge) where assemblies will be merged. After the merge step, the assemblies are actually all merged into a single assembly (let us say MyWebSite.dll) as we selected that option in WDP. ASP.NET only updates the .compiled files it knows about, so App_Global.asax.compiled actually has a correct reference to MyWebSite_Deploy.dll instead of App_Global.dll. Original assemblies are removed after the merge step. WCF reads the list of assemblies previously stored, and throws when it cannot find App_Global.

This behavior is something which by design and getting around it there are few options as below –

1. Manually update .complied file for WCF and remove the following reference like below –

'App_global.asax, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
'App_ GlobalResources.asax, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
'App_ WebReferences.asax, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
'App_ Code.asax, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null

OR

2. Add fully qualified name for the service/factory in .svc file.  The .svc buildprovider adds the list of referenced assemblies to the asp.net temporary file (as CompiledCustomString) during compilation. In the case of not-assembly-fully-qualified name is used, WCF hosting layer will use the compiled custom string to load assemblies and resolve the type of service/factory.

OR

3. In the properties of Web Deployment project, check ‘Allow this precompiled site to be updatable’ option. With this setting, the assembly App_Global.dll will not be built during the build/merge step.

OR                                                 

4. In the properties of Web Deployment project, choose merge option OTHER than “Merge All outputs to a single assembly”. The difference is that App_Global will not be merged and removed for the other options. And to get the assembly versions as needed, use compilerOptions in addition to this. Refer http://msdn.microsoft.com/en-us/library/ms228042.aspx

Hope this helps!!!