2009-11-21 18 views
5

In .NET è possibile selezionare una tabella come tipo per un utente. Tuttavia, quando lo salvo e lo recupero in questo modo, non sembra averlo salvato affatto.Come memorizzare un HashTable nelle impostazioni utente?

Hashtable t = new Hashtable(); 
t.Add(1,"Foo"); 
t.Add(2,"Bar"); 
Properties.Settings.Default.Setting = t; 
Properties.Settings.Default.Save(); 

if(Properties.Settings.Default.Setting != null) 
     foreach (DictionaryEntry entry in Properties.Settings.Default.Setting) 
     { 
      MessageBox.Show(entry.Key + " " + entry.Value); 
     } 

Perché non lo serializza nelle impostazioni utente, quando posso selezionare chiaramente quel tipo in Visual Studio? Capirei se questo fosse il caso con un tipo non elencato come il dizionario, ma Hashtable è elencato. Come risolvo questo problema?
La semplicità e l'efficienza in questo ordine hanno per me la massima priorità.

Molte grazie, Kave


aggiornamento:

@Joao, molte grazie la soluzione binaria. Lo trovo piuttosto interessante, è pulito. Uno svantaggio con serializzazione come binario potrebbe essere il fatto che non si può più modificare nulla nel file di utenti manualmente. ma penso che sarà fatto molto raramente, quindi è una buona soluzione.

Stavo pensando ad un diverso approccio per creare un campo "XMLSetting" di tipo stringa nello scope utente e utilizzare questo codice per archiviare e recuperare i valori come un file XMl serializzato in una tabella hash. Ma sono sicuro che questo non è il modo migliore, qualcuno sa un modo migliore per serializzare un hashtable/dizionario come xml nelle impostazioni degli utenti, diverso da quello che sto facendo qui sotto?

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

Ho appena testato la permanenza di un Hashtable come impostazione utente e non ho rilevato alcun problema. Se non lo hai già fatto, prova a farlo da zero in un nuovo progetto di test. –

+0

Ciao, ho appena provato di nuovo. Continua a specificare Properties.Settings.Default.Setting rimane nullo, anche dopo aver salvato e riavviato l'applicazione. Funziona comunque con altri tipi di tale stringa. Come hai fatto a farlo funzionare? Puoi pubblicare la tua soluzione in qualche modo? – Houman

+0

Avevo fretta nelle mie prove. Hashtable non viene mantenuto tra le esecuzioni dell'applicazione con la classe delle impostazioni di default. Tuttavia, se è fondamentale per te avere un Hashtable e non ti preoccupare del lavoro manuale, puoi controllare la mia risposta. –

risposta

12

Il Hashtable non supporta la serializzazione in XML né io credo ad una semplice stringa. Queste sono le due opzioni di serializzazione disponibili quando si utilizza un file Settings.settings e la relativa classe generata automaticamente.

Tuttavia, se si crea la classe delle impostazioni da soli e si gestisce anche la sezione App.Config, è possibile mantenere un hastable utilizzando la serializzazione binaria.

Vedere l'esempio seguente. Si tratta di un'applicazione console con i seguenti file:

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> 

Impostazioni personalizzate classe creata manualmente:

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

Aggiornamento: Se vuoi una lettura umana in grado di rappresentare i dati, l'approccio che stai utilizzando sembra ragionevole. Tuttavia, puoi anche provare un approccio diverso che meglio incapsula la logica della conversione in stringa (XML) e da stringa (XML).

Questo approccio consente di utilizzare il supporto IDE per il file Settings.settings, eliminando la necessità di generare una classe di impostazione personalizzata o di creare problemi con App.config.

Devi solo implementare una classe personalizzata che manterrà i tuoi dati, nel mio esempio erediterò questa classe da StringDictionary e implementerò anche un TypeConverter che il sistema di impostazioni userà per mantenere i dati in formato stringa.

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

Ora è sufficiente utilizzare la classe MyStringDictionary come tipo di dati delle impostazioni. A causa di Visual Studio che non visualizza le classi utente nei tipi di dati disponibili per un'impostazione utente, è necessario eseguire una soluzione temporanea che consiste nell'aprire il file Settings.settings con l'editor XML (tasto destro e Larghezza aperta) e specificare manualmente il tipo di impostazione dell'utente come nome completo di MyStringDictionary.

Spero che questo aiuti.

+0

Questo funziona ora molte grazie. Ho aggiunto anche una nuova soluzione a questo problema. Cosa ne pensi di questo? Qualche suggerimento per l'ottimizzazione? – Houman

+0

Controlla il mio aggiornamento ... –

+0

Grazie mille. Anche questo ha funzionato. Ora abbiamo tre soluzioni al problema. : o) – Houman

Problemi correlati