2010-12-01 27 views
1

Mon Fonctionnement interne Code:System.ArgumentNullException dans ResourceManager.GetString

System.Resources.ResourceManager resourceManager = GetResourceManager(); 
string str = resourceManager.GetString("delete", new CultureInfo(1033)); 

dans le projet actuel compilé dans tout .NET 2.0 fonctionne comme exceptés. La variable str contient la chaîne de ressource pour LCID 1033 - Supprimez, c'est correct.

Nous mettons actuellement à niveau vers .NET 4.0, projet recompilé sous le framework cible .NET 4.0. Maintenant compilé en tant qu'assemblages .NET 4.0, il lève l'exception System.ArgumentNullException avec le message La valeur ne peut pas être nulle. trace .Stack:

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) 

intéressante ici est stacktrace, où il pointe méthode du cadre interne dans ResourceManager.InternalGetResourceSet qui provoque appeler Monitor.Enter avec un objet nul. Mais j'appelle la méthode GetString avec des paramètres non null GetString ("delete", nouveau CultureInfo (1033)).

Ce bug semble être similaire à System.ArgumentNullException in System.Threading.Monitor.Enter. Peut-être un bug dans Monitor.Enter, ou autre chose?

Mise à jour: Lorsque je regarde dans le débogueur à l'objet resourceManager.ResourceSets.Items[2].Value.Table["delete"] alors il contient la valeur de chaîne "Supprimer". Les éléments de propriété [2] pointent ici sur LCID 1033. Cela signifie que le gestionnaire de ressources contient déjà la chaîne localisée pour la clé de ressource supprimer dans le langage . Est-ce que quelqu'un sait où peut être une erreur?

+0

Très intéressant. J'ai essayé d'explorer la classe ResourceManager dans Reflector mais je n'ai que mscorlib 2.0. Si c'est un bug, je le rechercherais dans la méthode ResourceManager.InternalGetResourceSet. Utilisez-vous .NET ou Mono?Je voudrais être d'aide –

+0

J'utilise .NET sur Windows, FW 4.0, juste décompilé avec Reflector, mais n'a pas pu trouver de choses inhabituelles dans la méthode interne. Jetez un oeil à cette méthode décompilée ici - http://paste.org/pastebin/view/25636. – psulek

+0

Maintenant, j'utilise .NET Reflector addin dans Visual Studio pour déboguer mscorlib, et découvre qu'il tombe dans la ligne 14 - lock (localResourceSets) (sur l'échantillon collé) – psulek

Répondre

1

Je pense aussi que vous devez déclarer et utiliser votre propre dictionnaire pour le stockage depuis ResourceSets est marqué comme obsolètes dans la section 4.0 .NET

+0

C'est ce que je fais enfin. – psulek

1

J'ai trouvé la réponse moi-même. Voici les détails: Nous avons mise en œuvre personnalisée de ResourceManager comme ceci:

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)); 
    } 
} 

Dans .NET 2.0, il fonctionne bien, mais dans .NET 4.0 quelque chose a changé dans la mise en œuvre de ResourceManager.I pense que problème est dans le constructeur parameterless qui dans .NET 2.0 instancie le champ privé this._resourceSets (qui est ensuite utilisé dans InternalGetResourceSet pour Monitor.Enter). Mais dans .NET 4.0, le constructeur sans paramètre n'instancie pas le champ privé this._resourceSets et ainsi il échoue plus tard (comme décrit ci-dessus).

Je dois réécrire mon gestionnaire de ressources personnalisé que cela fonctionne sur:

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 » est ici que je dois remplacer la méthode InternalGetResourceSet pour charger mes ressources de stockage sur mesure (db) et revenir en arrière "ResourceSet" pour la culture spécifiée. Maintenant, cela fonctionne comme un charme.