2009-11-21 3 views
5

Dans .NET, vous pouvez sélectionner une hashtable comme type pour un utilisateur. Cependant quand je l'enregistre et le récupère de cette façon, il ne semble pas l'avoir sauvegardé du tout. Pourquoi ne le sérialise-t-il pas dans les paramètres utilisateurs, alors que je peux clairement sélectionner ce type dans Visual Studio? Je comprendrais si c'était le cas avec un type non listé comme dictionnaire, mais Hashtable est listé. Comment puis-je résoudre ce problème?
La simplicité et l'efficacité dans cet ordre ont la plus haute priorité pour moi.Comment stocker un HashTable dans les paramètres utilisateurs?

Merci beaucoup, Kave


mise à jour:

@Joao, Merci beaucoup la solution binaire. Je trouve ça assez intéressant, c'est propre. Un inconvénient avec la sérialisation en tant que binaire pourrait être le fait que vous ne pouvez plus rien changer dans le fichier usersetting manuellement. mais je pense que cela sera fait très rarement de toute façon, donc c'est une bonne solution. Je pensais à une approche différente pour créer un champ "XMLSetting" de type chaîne dans la portée de l'utilisateur et utiliser ce code pour stocker et récupérer les valeurs sous la forme d'un fichier XMl sérialisé dans une table de hachage. Mais je suis sûr que ce n'est pas le meilleur moyen, quelqu'un sait-il un meilleur moyen de sérialiser un hashtable/dictionnaire comme xml dans les paramètres utilisateurs, autre que ce que je fais ci-dessous?

if(string.IsNullOrEmpty(Properties.Settings.Default.XMLSetting)) 
      { 
       Console.WriteLine("Usersettings is empty. Initializing XML file..."); 
       XmlDocument doc = new XmlDocument(); 
       XmlElement hashtable = doc.CreateElement("HashTable"); 
       doc.AppendChild(hashtable); 

       GenerateValues(doc, hashtable, "1", "Foo"); 
       GenerateValues(doc, hashtable, "2", "Bar"); 

       Properties.Settings.Default.XMLSetting = doc.OuterXml; 
       Properties.Settings.Default.Save(); 
      } 
      else 
      { 
       Console.WriteLine("Retrieving existing user settings..."); 
       XmlDocument doc = new XmlDocument(); 
       doc.LoadXml(Properties.Settings.Default.XMLSetting); 

       Hashtable hashtable = new Hashtable(); 

       foreach (XmlNode entry in doc.DocumentElement.ChildNodes) 
       { 
        hashtable.Add(int.Parse(entry.FirstChild.InnerText), entry.FirstChild.NextSibling.InnerText); 
       } 

       foreach (DictionaryEntry entry in hashtable) 
       { 
        Console.WriteLine(entry.Key + " " + entry.Value); 
       } 
      } 

private static void GenerateValues(XmlDocument doc, XmlElement hashtable, string skey, string svalue) 
     { 
      XmlElement entry = doc.CreateElement("entry"); 
      XmlElement key = doc.CreateElement("Key"); 
      XmlElement value = doc.CreateElement("Value"); 
      entry.AppendChild(key); 
      entry.AppendChild(value); 

      key.AppendChild(doc.CreateTextNode(skey)); 
      value.AppendChild(doc.CreateTextNode(svalue)); 

      hashtable.AppendChild(entry); 
     } 
+0

Je viens de tester la persistance d'une Hashtable en tant que paramètre utilisateur et j'ai détecté aucun problème. Si vous ne l'avez pas déjà fait, essayez de le faire à partir de zéro dans un nouveau projet de test. –

+0

Salut, je viens de l'essayer à nouveau. Il continue à dire que le paramètre Properties.Settings.Default.Setting reste nul, même après avoir sauvegardé et redémarré l'application. Cela fonctionne avec d'autres types tels que la chaîne. Comment l'avez-vous fait fonctionner? Pouvez-vous poster votre solution en quelque sorte? – Houman

+0

Je devais hâter mes tests. Le Hashtable n'est pas conservé entre les exécutions d'application avec la classe de paramètres par défaut. Cependant, s'il est vital pour vous d'avoir une table de hachage et que cela ne vous dérange pas d'un travail manuel, vous pouvez vérifier ma réponse. –

Répondre

12

La table de hachage ne prend pas en charge la sérialisation au format XML et je ne crois pas en une chaîne simple. Ce sont les deux options de sérialisation disponibles lorsque vous utilisez un fichier Settings.settings et la classe générée automatiquement associée.

Toutefois, si vous créez vous-même la classe de paramètres et que vous gérez également la section App.Config, vous pouvez conserver Hastable en utilisant la sérialisation binaire.

Voir l'exemple suivant. Il est une application console avec les fichiers suivants:

App.config

<?xml version="1.0" encoding="utf-8" ?> 
<configuration> 
    <configSections> 
    <sectionGroup 
     name="userSettings" 
     type="System.Configuration.UserSettingsGroup, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" > 
     <section 
     name="ConsoleApplication1.MyCustomSettings" 
     type="System.Configuration.ClientSettingsSection, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" 
     allowExeDefinition="MachineToLocalUser" 
     requirePermission="false" /> 
    </sectionGroup> 
    </configSections> 
    <userSettings> 
    <ConsoleApplication1.MyCustomSettings> 
     <setting name="MyHashtable" serializeAs="Binary"> 
     <value></value> 
     </setting> 
     <setting name="MyBackColor" serializeAs="String"> 
     <value>Silver</value> 
     </setting> 
    </ConsoleApplication1.MyCustomSettings> 
    </userSettings> 
</configuration> 

Réglages personnalisés classe créée manuellement:

public class MyCustomSettings : ApplicationSettingsBase 
{ 
    private static MyCustomSettings defaultInstance = (
     (MyCustomSettings) 
     (ApplicationSettingsBase.Synchronized(new MyCustomSettings()))); 

    public static MyCustomSettings Default 
    { 
     get { return defaultInstance; } 
    } 

    [UserScopedSettingAttribute()] 
    [DebuggerNonUserCodeAttribute()] 
    [DefaultSettingValueAttribute("Silver")] 
    public Color MyBackColor 
    { 
     get { return ((Color)(this["MyBackColor"])); } 
     set { this["MyBackColor"] = value; } 
    } 

    [UserScopedSettingAttribute()] 
    [DebuggerNonUserCodeAttribute()] 
    [SettingsSerializeAs(SettingsSerializeAs.Binary)] 
    public Hashtable MyHashtable 
    { 
     get { return ((Hashtable)(this["MyHashtable"])); } 
     set { this["MyHashtable"] = value; } 
    } 
} 

Program.cs

class Program 
{ 
    static void Main(string[] args) 
    { 
     // For the first time no Hastable will exist. 
     // Create one with the default values 
     if (MyCustomSettings.Default.MyHashtable == null) 
     { 
      Console.WriteLine("Initializing Hashtable..."); 

      MyCustomSettings.Default.MyHashtable = new Hashtable(); 

      MyCustomSettings.Default.MyHashtable.Add(1, "foo"); 
      MyCustomSettings.Default.MyHashtable.Add(2, "bar"); 

      MyCustomSettings.Default.Save(); 
     } 

     foreach (DictionaryEntry entry in MyCustomSettings.Default.MyHashtable) 
     { 
      Console.WriteLine(entry.Key + ": " + entry.Value); 
     } 

     Console.ReadKey(); 
    } 
} 

Mise à jour: Si vous voulez une lecture humaine représentation des données, l'approche que vous utilisez semble raisonnable. Néanmoins, vous pouvez également essayer une approche différente qui encapsule mieux la logique de conversion en chaîne (XML) et de chaîne (XML).

Cette approche vous permet d'utiliser la prise en charge de l'EDI pour le fichier Settings.settings en supprimant le besoin de générer une classe de paramètres personnalisés ou en jouant avec App.config. Dans mon exemple, je vais hériter de cette classe d'un StringDictionary et implémenter un TypeConverter que le système de paramètres utilisera pour conserver les données au format chaîne.

[TypeConverter(typeof(StringDictionaryTypeConverter))] 
public class MyStringDictionary : StringDictionary 
{ 
} 

public class StringDictionaryTypeConverter : TypeConverter 
{ 
    public override bool CanConvertFrom(
     ITypeDescriptorContext context, 
     Type sourceType) 
    { 
     if (sourceType.Equals(typeof(string))) 
     { 
      return true; 
     } 

     return base.CanConvertFrom(context, sourceType); 
    } 

    public override bool CanConvertTo(
     ITypeDescriptorContext context, 
     Type destinationType) 
    { 
     if (destinationType.Equals(typeof(string))) 
     { 
      return true; 
     } 

     return base.CanConvertTo(context, destinationType); 
    } 

    public override object ConvertFrom(
     ITypeDescriptorContext context, 
     CultureInfo culture, 
     object value) 
    { 
     if (value is string) 
     { 
      MyStringDictionary sd = new MyStringDictionary(); 

      XDocument xs = XDocument.Load(new StringReader(value as string)); 

      foreach (var item in xs.Descendants("entry")) 
      { 
       sd.Add(item.Element("key").Value, item.Element("value").Value); 
      } 

      return sd; 
     } 

     return base.ConvertFrom(context, culture, value); 
    } 

    public override object ConvertTo(
     ITypeDescriptorContext context, 
     CultureInfo culture, 
     object value, 
     Type destinationType) 
    { 
     if (destinationType.Equals(typeof(string))) 
     { 
      MyStringDictionary sd = value as MyStringDictionary; 

      StringBuilder sb = new StringBuilder(); 

      sb.Append("<entries>"); 
      foreach (DictionaryEntry item in sd) 
      { 
       sb.AppendFormat(
        "<entry><key>{0}</key><value>{1}</value></entry>", 
        item.Key, 
        item.Value); 
      } 
      sb.Append("</entries>"); 

      return sb.ToString(); 
     } 

     return base.ConvertTo(context, culture, value, destinationType); 
    } 
} 

Vous n'avez plus qu'à utiliser la classe MyStringDictionary comme type de données de vos paramètres. Étant donné que Visual Studio n'affiche pas les classes d'utilisateurs dans les types de données disponibles pour un paramètre utilisateur, vous devez effectuer une solution de contournement qui consiste à ouvrir le fichier Settings.settings avec l'éditeur XML (clic droit et Open Width) et spécifier manuellement type du paramètre utilisateur en tant que nom complet de MyStringDictionary.

Espérons que cela aide.

+0

Cela fonctionne maintenant beaucoup de mercis. J'ai ajouté aussi une nouvelle solution à ce problème. Qu'est ce que tu penses de ça? Des suggestions d'optimisation? – Houman

+0

Vérifier ma mise à jour ... –

+0

Merci beaucoup. Cela a fonctionné aussi bien. Maintenant, nous avons trois solutions au problème. : o) – Houman

Questions connexes