2009-11-21 16 views
5

En .NET puede seleccionar una tabla hash como tipo para un conjunto de usuarios. Sin embargo, cuando lo guardo y lo recupero de esta manera, parece que no lo ha guardado.¿Cómo almacenar una HashTable en la configuración de usuario?

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

¿Por qué no lo serializa en el conjunto de usuarios, cuando puedo seleccionar claramente ese tipo en Visual Studio? Yo entendería si este fuera el caso con un tipo no listado como el diccionario, pero Hashtable aparece en la lista. ¿Cómo resuelvo este problema?
La simplicidad y la eficiencia en este orden tienen la más alta prioridad para mí.

Muchas gracias, Kave


actualización:

@Joao, Muchas Gracias la solución binaria. Lo encuentro bastante interesante, está limpio. Un inconveniente de serializarlo como binario podría ser el hecho de que ya no puede cambiar nada en el archivo de configuración de usuarios manualmente. pero creo que eso se hará muy raramente de todos modos, así que es una buena solución.

Estaba pensando en un enfoque diferente para crear un campo "de configuración de XML" de tipo cadena en el ámbito del usuario y usar este código para almacenar y recuperar los valores como un archivo XMl serializado en una tabla hash. Pero estoy seguro de que esta no es la mejor manera, ¿alguien sabe una mejor manera de serializar un hashtable/dictionary como xml en la configuración de los usuarios, aparte de lo que estoy haciendo a continuación?

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

Acabo de probar persistiendo una Hashtable como configuración de usuario y no he detectado ningún problema. Si aún no lo ha hecho, trate de hacerlo desde cero en un nuevo proyecto de prueba. –

+0

Hola, acabo de intentarlo de nuevo. Sigue mostrando las Propiedades. Configuración. La configuración predeterminada permanece nula, incluso después de haber guardado y reiniciado la aplicación. Sin embargo, funciona con otros tipos de cadena. ¿Cómo lo hiciste funcionar? ¿Puedes publicar tu solución de alguna manera? – Houman

+0

Tenía que precipitarme en mis pruebas. La Hashtable no persiste entre las ejecuciones de la aplicación con la clase de configuración predeterminada. Sin embargo, si es vital que tenga un Hashtable y no le moleste el trabajo manual, puede verificar mi respuesta. –

Respuesta

12

La Hashtable no admite la serialización en XML ni creo en una cadena simple. Estas son las dos opciones de serialización disponibles cuando utiliza un archivo Settings.settings y la clase asociada autogenerada.

Sin embargo, si crea la clase de configuración usted mismo y también administra la sección App.Config puede persistir una instancia de Hastable utilizando la serialización binaria.

Vea el siguiente ejemplo. Es una aplicación de consola con los archivos siguientes:

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> 

parámetros personalizados Clase creado 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(); 
    } 
} 

Actualización: Si quieres una lectura humana representación de los datos, el enfoque que está utilizando parece razonable. No obstante, también puede probar un enfoque diferente que mejor encapsule la lógica de convertir a cadena (XML) y a partir de cadena (XML).

Este enfoque le permite utilizar el soporte IDE para el archivo Settings.settings, eliminando la necesidad de generar una clase de configuración personalizada o jugar con App.config.

Solo necesita implementar una clase personalizada que contendrá sus datos, en mi ejemplo voy a heredar esta clase de un StringDictionary y también implementaré un TypeConverter que el sistema de configuración usará para conservar los datos en formato de cadena.

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

Ahora solo necesita utilizar la clase MyStringDictionary como el tipo de datos de su configuración. Debido a que Visual Studio no muestra las clases de usuario en los tipos de datos disponibles para una configuración de usuario, debe realizar una solución temporal que consiste en abrir el archivo Settings.settings con el editor XML (clic derecho y ancho abierto) y especificar manualmente el tipo de configuración de usuario como el nombre completo de MyStringDictionary.

Espero que esto ayude.

+0

Esto funciona ahora muchas gracias. También he agregado una nueva solución a este problema. ¿Qué piensas sobre eso? ¿Alguna sugerencia de optimización? – Houman

+0

Compruebe mi actualización ... –

+0

Muchas gracias. Esto funcionó bien. Ahora tenemos tres soluciones para el problema. : o) – Houman

Cuestiones relacionadas