Thursday, August 30, 2012

Problem with resolving resource strings in Sharepoint via SPUtility.GetLocalizedString

Method SPUtility.GetLocalizedString can be used in order to get translated string from your resource file in runtime in Sharepoint web site. Resource files should be located in 14/Resources folder. We often use it during development. Recently we faced with strange situation: there was a farm with 2 WFEs. On this farm we updated wsp package which with other artifacts contained updates for provisioning resources, i.e. we added several new strings into resx files which are provisioned to 14/Resources. After wsp upgrade we ran iisreset on both WFE (SPUtility.GetLocalizedString caches results).

After that problem occurred: SPUtility.GetLocalizedString showed translated string from resx file only on one WFE, while on another it didn’t find it. We checked that resx files were updated correctly on problematic WFE, and that they are the same as on WFE where everything was working.

During investigating the problem in ULS I found the following errors:

Failed to look up string with key "key_name", keyfile MyResources
Localized resource for token 'key_name' could not be found for file with path: "(unavailable)"

And it was strange because as I said resx files were updated correctly. Also during investigation we tried to copy resx files into App_GlobalResources, but it didn’t help.

In the project we had the following provisioning resources:

  • MyResources.resx – default English culture
  • – Finnish resources
  • – Dutch resources

I created simple application layouts page which calls SPUtility.GetLocalizedString with 3 locale ids (English = 1033, Finnish = 1035 and Dutch = 1043) and opened it in context of site on problematic WFE. It successfully found Finnish and Dutch resources, but not default English resources. How it could be if MyResources.resx was the same as in working WFE?

I went to 14/Resources folder in order to check MyResources.resx more carefully, and found that there was another culture-specific resource file for English locale: MyResources.en-us.resx. It was added manually by someone for some testing and was not deleted after that. Of course it didn’t contain our new strings. But SPUtility.GetLocalizedString found this file and as it was culture-specific, it had more priority over default culture file MyResources.resx. When we deleted MyResources.en-us.resx from problematic WFE, it become working. Hope that this story will help you if you will face with the same problem.

Saturday, August 25, 2012

Use Developer dashboard with UpdatePanel in ajax-enabled Sharepoint sites

Developer dashboard is convenient tool for troubleshooting in Sharepoint. It allows you to see how many time each function is executed during request lifetime (by default it shows some standard functions. If you want to see your own custom functions you have to enclose their body within SPMonitoredScope). There is a lot of resources available in internet which show in details how to configure and us it. I won’t repeat it here. Instead I will show how to use Developer dashboard in ajax-enabled web applications, where most of communications with user occur inside UpdatePanel.

The common usage of Developer dashboard in this scenario is the following: on the masterpage there is DeveloperDashboard and DeveloperDashboardLauncher controls. Main content placeholder is located within UpdatePanel. Also it is possible that UpdatePanel is added on particular page or web part. And there is one problem with such configuration: Developer dashboard will show you information only about 1st http get request, while other asynchronous http post requests occurred from inside UpdatePanel won’t be traced.

Solution of this problem is quite simple: add DeveloperDashboard control into own separate UpdatePanel. The key element of understanding how it works is UpdatePanel.UpdateMode property. It may have 2 possible values:

  • Always (default)
  • Conditional

As documentation says when you use Always update mode which is default:

The content of the UpdatePanel control is updated for all postbacks that originate from the page. This includes asynchronous postbacks.

I.e. UpdatePanel with DeveloperDashboard will be updated even if requests will come from another UpdatePanels. If in your application you use Conditional mode, you have to explicitly call Update() method on the UpdatePanel with DeveloperDashboard when request came from other “basic” UpdatePanel. After that you will be able to trace all request, not only 1st http get request for the page. Hope this technique will help you.