My code:
System.Resources.ResourceManager resourceManager = GetResourceManager();
string str = resourceManager.GetString("delete", new CultureInfo(1033));
In current project compiled under .NET 2.0 everything works as excepted. Variable str contains resource string for LCID 1033 - Delete, this is ok.
We are now upgrading to .NET 4.0, recompiled project under target framework .NET 4.0. Now compiled as .NET 4开发者_StackOverflow社区.0 assemblies, it throws exception System.ArgumentNullException with message Value cannot be null..Stack trace:
at System.Threading.Monitor.Enter(Object obj)
at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo requestedCulture, Boolean createIfNotExists, Boolean tryParents, StackCrawlMark& stackMark)
at System.Resources.ResourceManager.InternalGetResourceSet(CultureInfo culture, Boolean createIfNotExists, Boolean tryParents)
at System.Resources.ResourceManager.GetString(String name, CultureInfo culture)
Interesting here is stacktrace, where it points to internal framework method in ResourceManager.InternalGetResourceSet which causes to call Monitor.Enter with null object. But i call method GetString with not null parameters GetString("delete", new CultureInfo(1033)).
This bug seems to be similar to System.ArgumentNullException in System.Threading.Monitor.Enter. Maybe some bug in Monitor.Enter, or something else?
Update:
When i look in debugger at object resourceManager.ResourceSets.Items[2].Value.Table["delete"]
then it contains string value "Delete". property Items[2] here pointing to LCID 1033. This means that resource manager already contains localized string for resource key delete in language 1033. Does anybody knows where can be error?
I also think that you should declare and use your own dictionary for storage since ResourceSets is marked as Obsolete under .NET 4.0
I found answer myself. Here are details: We have custom implementation of ResourceManager like this:
public class DatabaseResourceManager : System.Resources.ResourceManager
{
public DatabaseResourceManager(int applicationID, string bundle)
{
foreach (int languageID in ResourceProvider.Provider.GetLanguages(applicationID))
{
DatabaseResourceReader r = new DatabaseResourceReader(applicationID, bundle, languageID);
ResourceSets.Add(new CultureInfo(languageID), new ResourceSet(r));
}
}
In .NET 2.0 it works well, but in .NET 4.0 something has changed in implementation of ResourceManager.I think that problem is in parameterless constructor which in .NET 2.0 instantiate private field this._resourceSets (which is later used in InternalGetResourceSet for Monitor.Enter). But in .NET 4.0 parameterless constructor does not instantiate private field this._resourceSets and thus it fails later (as described abobe).
I must rewrite my custom resource manager as this to work out:
public class DatabaseResourceManager : System.Resources.ResourceManager
{
public DatabaseResourceManager(int applicationID, string bundle)
{
ResourceSets = new Hashtable();
this.applicationID = applicationID;
this.bundle = bundle;
}
protected override ResourceSet InternalGetResourceSet(CultureInfo culture, bool createIfNotExists, bool tryParents)
{
if (this.ResourceSets.Contains(culture.Name))
return this.ResourceSets[culture.Name] as ResourceSet;
lock (syncLock)
{
if (this.ResourceSets.Contains(culture.Name))
return this.ResourceSets[culture.Name] as ResourceSet;
DatabaseResourceReader r = new DatabaseResourceReader(applicationID, bundle, culture.LCID);
ResourceSet rs = new ResourceSet(r);
this.ResourceSets.Add(culture.Name, rs);
return rs;
}
}
}
"Magic" here is that i must overwrite method InternalGetResourceSet to load my resources from custom storage (db) and return back "ResourceSet" for specified culture. Now it works like a charm.
精彩评论