Sunday, April 20, 2014

Problem with using resx files in App_GlobalResources and App_LocalResources in ASP.Net MVC web applications

In ASP.Net MVC applications it is technically possible to use old ASP.Net approach for storing resources when resx files are located in special folders App_GlobalResources for application-wide resources and App_LocalResources  for specific views. By using here I mean that only localizable strings will be stored in resx files, but in views they will be referenced by using strongly typed properties @MyResource.Foo, not via ASP.Net resource expressions <%$ Resources:Foo %> of course (the last one is against ASP.Net MVC spirit). In order to do this all resources should be embedded.

This approach will work with single language default resources. But when you will add culture-specific translation (e.g. en-us) then you may encounter with the following exception when will try to open your web application in browser:

Event code: 3006
Event message: A parser error has occurred.
Exception information:
Exception type: HttpParseException
Exception message: Access to the path 'foo.en-US.resx' is denied.
at System.Web.Compilation.AssemblyBuilder.AddBuildProvider(BuildProvider buildProvider)
at System.Web.Compilation.BuildProvidersCompiler.ProcessBuildProviders()
at System.Web.Compilation.BuildProvidersCompiler.PerformBuild()

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

Access to the path 'foo.en-US.resx' is denied.
at System.IO.__Error.WinIOError(Int32 errorCode, String maybeFullPath)
at System.IO.FileStream.Init(String path, FileMode mode, FileAccess access, Int32 rights, Boolean useRights, FileShare share, Int32 bufferSize, FileOptions options, SECURITY_ATTRIBUTES secAttrs, String msgPath, Boolean bFromProxy, Boolean useLongPath, Boolean checkHost)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share, Int32 bufferSize, FileOptions options, String msgPath, Boolean bFromProxy)
at System.IO.FileStream..ctor(String path, FileMode mode, FileAccess access, FileShare share)
at System.Web.Hosting.MapPathBasedVirtualFile.Open()
at System.Web.Compilation.BaseResourcesBuildProvider.GenerateCode(AssemblyBuilder assemblyBuilder)
at System.Web.Compilation.AssemblyBuilder.AddBuildProvider(BuildProvider buildProvider)

You may of course try to fix this problem by adding permissions to the appropriate user account (e.g. to IIS_IUSRS if your web app works in anonymous mode), but as error above shows, it will still try to compile generated .cs files for your resources in runtime, which is not what we expect, because in our case resources are embedded and we want to have all code precompiled.

In order to avoid runtime compilation of resources in ASP.Net MVC application instead of App_GlobalResources and App_LocalResources use other not-special folder names and still embed all resx files to your assemblies. In this case ASP.Net runtime won’t try to compile your resources and there won’t be problem shown above. The same conclusion was made in the following article Resx Files In App_GlobalResources, although it considered different issue.

1 comment:

  1. Alexey, if you need a tool recommendation for managing the software localization of projects using resx files, you should have a look at this app, it's collaborative and really well designed: https://poeditor.com
    Cheers!

    ReplyDelete