2009-05-28 13 views
30

Tengo un diccionario de cadenas que quiero que el usuario pueda agregar/eliminar información de luego almacenarlo para que puedan acceder a él la próxima vez que se reinicie el programaDiccionario de la tienda <cadena, cadena> en la configuración de la aplicación

No tengo claro cómo puedo almacenar un diccionario como configuración. Veo que en system.collections.special hay una cosa llamada stringdictionary pero he leído que las SD están desactualizadas y no deberían usarse.

también en el futuro puedo tener necesidad de almacenar un diccionario que no es solamente cadenas (string int)

¿Cómo te almacenar un diccionario en el archivo de configuración para una aplicación .NET?

+0

Relacionado: http://stackoverflow.com/questions/2890271/how-to-save-a-liststring-on-settings-default – BrunoLM

Respuesta

13

La respuesta más simple sería usar un delimitador de columna de la fila & para convertir el diccionario a una sola cadena. Entonces solo necesita almacenar 1 cadena en el archivo de configuración.

+5

Buena idea: utilice algo así como la serialización JSON para que este proceso sea relativamente sencillo. –

+0

¿podría elborar un poco sobre eso? – Crash893

+1

La serialización de JSON tiene el formato {"objeto": {"clave": "valor", "clave": "valor"}} –

3

Aparte de hacer algo como David's suggests, buscaría en el almacenamiento alternativo para el Diccionario. En última instancia, el objeto Configuración se serializa en el disco.

1

Puede crear una clase personalizada que expone un diccionario como una propiedad pública. Luego puede especificar este tipo personalizado como el tipo para su configuración.

Editar:

Acabo de leer que, por alguna razón, un diccionario genérico no puede ser XML serializado, así que mi solución, probablemente, no va a funcionar (no he probado aunque ...). Eso es extraño, porque una lista genérica se puede serializar sin ningún problema.

Aún puede crear una clase personalizada que se puede configurar como una configuración de usuario, pero deberá tener una lista expuesta como una propiedad en lugar de un diccionario.

+0

No estoy seguro de seguir. crear una clase que contenga un diccionario y luego guardar esa clase? – Crash893

+0

Exactamente, pero dado que el diccionario genérico no es serializable (ver edición) necesitaría ser una lista. –

3

¿Ha considerado usar XML para almacenar su diccionario? Eso proporcionaría una cierta cantidad de extensibilidad si en el futuro decide que desea poder almacenar otros tipos de diccionarios. Es posible hacer algo como:

<dictionary> 
    <entry key="myKey"> 
     [whatever data you like] 
    </entry> 
</dictionary> 

Podría ser una exageración, pero también estaría preparado en el caso de que usted quiere almacenar datos más complejos, como objetos personalizados.

1

También puede usar System.Collections.Specialized.StringCollection poniendo la clave en el índice par y los valores en el índice impar.

/// <summary> 
/// Emulate a Dictionary (Serialization pb) 
/// </summary> 
private static string getValue(System.Collections.Specialized.StringCollection list, string key) 
{ 
    for (int i = 0; i * 2 < list.Count; i++) 
    { 
     if (list[i] == key) 
     { 
      return list[i + 1]; 
     } 
    } 
    return null; 
} 

/// <summary> 
/// Emulate a Dictionary (Serialization pb) 
/// </summary>  
private static void setValue(System.Collections.Specialized.StringCollection list, string key, string value) 
{ 
    for (int i = 0; i * 2 < list.Count; i++) 
    { 
     if (list[i] == key) 
     { 
      list[i + 1] = value; 
      return; 
     } 
    } 
    list.Add(key); 
    list.Add(value); 
} 
1

Editar: Esto devolverá una tabla hash (por cualquier razón, a pesar de ser un 'DictionarySectionHandler'). Sin embargo, dado que los Hashtables y los Diccionarios son muy similares, no debería ser un gran problema (aunque me doy cuenta de que los Diccionarios son más nuevos, están parametrizados, etc., habría preferido dicitonarios, pero esto es lo que nos proporciona .NET).


La mejor respuesta que acabo de encontrar para esto es here. Devuelve una colección segura sin ningún tipo de confusión en el código para transformarla, y usted crea una colección obvia (y simple) en su archivo .config. Estoy usando esto y es bastante sencillo para cualquier programador futuro (incluyéndote a ti). Permite un tipado más fuerte y más flexibilidad, sin ningún tipo de análisis excesivamente complicado e innecesario.

1

Puede almacenar un StringCollection.Es similar to this solution.

Hice 2 métodos de extensión para convertir entre StringCollection y Dictionary. Esta es la forma más fácil que se me ocurre.

public static class Extender 
{ 
    public static Dictionary<string, string> ToDictionary(this StringCollection sc) 
    { 
     if (sc.Count % 2 != 0) throw new InvalidDataException("Broken dictionary"); 

     var dic = new Dictionary<string, string>(); 
     for (var i = 0; i < sc.Count; i += 2) 
     { 
      dic.Add(sc[i], sc[i + 1]); 
     } 
     return dic; 
    } 

    public static StringCollection ToStringCollection(this Dictionary<string, string> dic) 
    { 
     var sc = new StringCollection(); 
     foreach (var d in dic) 
     { 
      sc.Add(d.Key); 
      sc.Add(d.Value); 
     } 
     return sc; 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     //var sc = new StringCollection(); 
     //sc.Add("Key01"); 
     //sc.Add("Val01"); 
     //sc.Add("Key02"); 
     //sc.Add("Val02"); 

     var sc = Settings.Default.SC; 

     var dic = sc.ToDictionary(); 
     var sc2 = dic.ToStringCollection(); 

     Settings.Default.SC = sc2; 
     Settings.Default.Save(); 
    } 
} 
40

Puede utilizar esta clase derivada de StringDictionary. Para ser útil para la configuración de la aplicación, implementa IXmlSerializable. O puede usar un enfoque similar para implementar su propia clase XmlSerializable.

public class SerializableStringDictionary : StringDictionary, IXmlSerializable 
{ 
    public XmlSchema GetSchema() 
    { 
     return null; 
    } 

    public void ReadXml(XmlReader reader) 
    { 
     while (reader.Read() && 
      !(reader.NodeType == XmlNodeType.EndElement && reader.LocalName == this.GetType().Name)) 
     { 
      var name = reader["Name"]; 
      if (name == null) 
       throw new FormatException(); 

      var value = reader["Value"]; 
      this[name] = value; 
     } 
    } 

    public void WriteXml(XmlWriter writer) 
    { 
     foreach (DictionaryEntry entry in this) 
     { 
      writer.WriteStartElement("Pair"); 
      writer.WriteAttributeString("Name", (string)entry.Key); 
      writer.WriteAttributeString("Value", (string)entry.Value); 
      writer.WriteEndElement(); 
     } 
    } 
} 

fragmento de XML resultante será similar a:

... 
<setting name="PluginSettings" serializeAs="Xml"> 
    <value> 
     <SerializableStringDictionary> 
      <Pair Name="property1" Value="True" /> 
      <Pair Name="property2" Value="05/01/2011 0:00:00" /> 
     </SerializableStringDictionary> 
    </value> 
</setting> 
... 
+3

No sé por qué no se acepta esta respuesta. Muy útil, gracias! – hazzik

+3

Funcionó para mí, pero agregué la declaración 'this.Clear();' al principio del método ReadXml, solo para asegurarme de que no haya elementos obsoletos en el diccionario. –

+0

¿Cómo se podría extender esto a un diccionario genérico? – Will

3

Si no es necesario utilizar el diseñador de la configuración o editar la configuración con un editor de texto, puede crear una clase simple que deriva de ApplicationSettingsBase:

namespace MyNamespace 
{ 
    using System.Collections.Generic; 
    using System.Configuration; 

    /// <summary> 
    /// Persistent store for my parameters. 
    /// </summary> 
    public class MySettings : ApplicationSettingsBase 
    { 
     /// <summary> 
     /// The instance lock. 
     /// </summary> 
     private static readonly object InstanceLock = new object(); 

     /// <summary> 
     /// The instance. 
     /// </summary> 
     private static MySettings instance; 

     /// <summary> 
     /// Prevents a default instance of the <see cref="MySettings"/> class 
     /// from being created. 
     /// </summary> 
     private MySettings() 
     { 
      // don't need to do anything 
     } 

     /// <summary> 
     /// Gets the singleton. 
     /// </summary> 
     public static MySettings Instance 
     { 
      get 
      { 
       lock (InstanceLock) 
       { 
        if (instance == null) 
        { 
         instance = new MySettings(); 
        } 
       } 

       return instance; 
      } 
     } 

     /// <summary> 
     /// Gets or sets the parameters. 
     /// </summary> 
     [UserScopedSetting] 
     [SettingsSerializeAs(SettingsSerializeAs.Binary)] 
     public Dictionary<string, string> Parameters 
     { 
      get 
      { 
       return (Dictionary<string, string>)this["Parameters"]; 
      } 

      set 
      { 
       this["Parameters"] = value; 
      } 
     } 
    } 
} 

el verdadero truco es el [SettingsSerializeAs (SettingsSerializeAs.Binary)] attribute. La mayoría de las clases (¿todas?) Pueden ser serializadas de esta manera donde ConfiguracionesSerializar.String o ConfiguracionesSerializarAs.Xml no funcionará para un Diccionario.

utilizar esto en su código a medida que la configuración normal sería:

// this code untested... 
MySettings.Instance.Parameters["foo"] = "bar"; 
MySettings.Instance.Parameters.Save(); 
MySettings.Instance.Parameters.Reload(); 
string bar; 
if (!MySettings.Instance.Parameters.TryGetValue("foo", out bar)) 
{ 
    throw new Exception("Foobar"); 
} 

Si desea que el diccionario para serializar en algo editable por el usuario, se debe proceder de diccionario y jugar con TypeConverter (ver Using Custom Classes with Application Settings).

Cuestiones relacionadas