web-dev-qa-db-fra.com

Enregistrer le dictionnaire <chaîne, chaîne> dans les paramètres de l'application

J'ai un dictionnaire de chaînes dont je veux que l'utilisateur puisse ajouter/supprimer des informations puis le stocker pour lui afin qu'il puisse y accéder la prochaine fois que le programme redémarre

Je ne sais pas comment je peux stocker un dictionnaire comme paramètre. Je vois que sous system.collections.special, il y a une chose appelée stringdictionary mais ive lu que SD est obsolète et ne devrait pas être utilisé.

aussi à l'avenir, j'aurai peut-être besoin de stocker un dictionnaire qui n'est pas uniquement des chaînes (chaîne int)

comment stockeriez-vous un dictionnaire dans le fichier de paramètres d'une application .net?

34
Crash893

La réponse la plus simple serait d'utiliser un délimiteur de ligne et de colonne pour convertir votre dictionnaire en une seule chaîne. Il vous suffit ensuite de stocker 1 chaîne dans le fichier de paramètres.

17
David

Vous pouvez utiliser cette classe dérivée de StringDictionary. Pour être utile pour les paramètres d'application, il implémente IXmlSerializable. Ou vous pouvez utiliser une approche similaire pour implémenter votre propre classe 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();
        }
    }
}

Le fragment XML résultant ressemblera à:

...
<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>
...
44

Si vous n'avez pas besoin d'utiliser le concepteur de paramètres ou de modifier vos paramètres avec un éditeur de texte, vous pouvez créer une classe simple qui dérive 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;
            }
        }
    }
}

L'astuce réelle est l'attribut [SettingsSerializeAs (SettingsSerializeAs.Binary)] . La plupart (toutes?) Des classes peuvent être sérialisées de cette façon où SettingsSerializeAs.String ou SettingsSerializeAs.Xml ne fonctionnera pas pour un dictionnaire.

Utilisez ceci dans votre code comme vous le feriez pour les paramètres normaux:

// 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 vous souhaitez que le dictionnaire soit sérialisé en quelque chose modifiable par l'utilisateur, vous devez dériver du dictionnaire et jouer avec TypeConverter (voir tilisation de classes personnalisées avec les paramètres d'application ).

4
mheyman

Avez-vous envisagé d'utiliser XML pour stocker votre dictionnaire? Cela fournirait une certaine extensibilité si, à l'avenir, vous décidiez de pouvoir stocker d'autres types de dictionnaires. Vous pourriez faire quelque chose comme:

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

Peut-être exagéré, mais vous seriez également prêt dans le cas où vous vouliez stocker des données plus complexes, comme des objets personnalisés.

3
Ender

Autre que de faire quelque chose comme David suggère , je chercherais un stockage alternatif pour le dictionnaire. En fin de compte, l'objet Paramètres sérialise sur le disque.

3
Erik Forbes

Vous pouvez également utiliser un System.Collections.Specialized.StringCollection en mettant la clé sur l'index pair et les valeurs sur l'index impair.

/// <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);
}
2
Olivier de Rivoyre

Vous pouvez stocker un StringCollection. C'est similaire à cette solution .

J'ai fait 2 méthodes d'extension pour convertir entre StringCollection et un Dictionary. C'est la façon la plus simple à laquelle je pouvais penser.

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

Edit: Cela retournera un Hashtable (pour une raison quelconque, bien qu'il soit un 'DictionarySectionHandler'). Cependant, étant donné que les tables de hachage et les dictionnaires sont si similaires, cela ne devrait pas être un gros problème (même si je me rends compte que les dictionnaires sont plus récents, paramétrés, etc.; j'aurais préféré les dicitonaries moi-même, mais c'est ce que .NET nous donne).


La meilleure réponse que je viens de trouver est ici . Il renvoie une collection de typesafe sans aucune confusion dans le code pour le transformer, et vous créez une collection évidente (et simple) dans votre fichier .config. J'utilise cela et c'est assez simple pour tout futur programmeur (y compris vous-même). Il permet un typage plus fort et plus de flexibilité, sans analyse trop compliquée et inutile.

1
James

Vous pouvez créer une classe personnalisée qui expose un dictionnaire en tant que propriété publique. Vous pouvez ensuite spécifier ce type personnalisé comme type pour votre paramètre.

Éditer:

Je viens de lire que, pour une raison quelconque, un dictionnaire générique ne peut pas être sérialisé en XML, donc ma solution ne fonctionnera probablement pas (je ne l'ai pas testée cependant ...). C'est étrange, car une liste générique peut être sérialisée sans aucun problème.

Vous pouvez toujours créer une classe personnalisée qui peut être définie comme paramètre utilisateur, mais vous devrez avoir une liste exposée en tant que propriété au lieu d'un dictionnaire.

1
Meta-Knight