81 votes

Comment stocker des données localement en .NET (C#)

J'écris une application qui prend les données de l'utilisateur et les stocke localement pour les utiliser plus tard. L'application sera démarrée et arrêtée assez souvent, et j'aimerais qu'elle enregistre/charge les données au démarrage/à la fin de l'application.

Ce serait assez simple si j'utilisais des fichiers plats, car les données n'ont pas vraiment besoin d'être sécurisées (elles ne seront stockées que sur ce PC). Les options sont donc, je crois, les suivantes :

  • Fichiers plats
  • XML
  • BASE DE DONNÉES SQL

Les fichiers plats demandent un peu plus d'efforts de maintenance (pas de classes intégrées comme avec le XML), mais je n'ai jamais utilisé le XML auparavant, et le SQL me semble excessif pour cette tâche relativement facile.

Y a-t-il d'autres pistes à explorer ? Si non, laquelle est la meilleure solution ?


Edit : Pour ajouter un peu plus de données au problème, la seule chose que je voudrais stocker est un dictionnaire qui ressemble à ceci

Dictionary<string, List<Account>> 

où Compte est un autre type personnalisé.

Devrais-je sérialiser le dict comme xmlroot, puis le type de compte comme attributs ?


Mise à jour 2 :

Il est donc possible de sérialiser un dictionnaire. Ce qui rend la chose compliquée, c'est que la valeur de ce dict est un générique lui-même, qui est une liste de structures de données complexes de type Compte. Chaque compte est assez simple, c'est juste un tas de propriétés.

Si j'ai bien compris, l'objectif est de parvenir à ce résultat :

<Username1>
    <Account1>
        <Data1>data1</Data1>
        <Data2>data2</Data2>
    </Account1>
</Username1>
<Username2>
    <Account1>
        <Data1>data1</Data1>
        <Data2>data2</Data2>
    </Account1>
    <Account2>
        <Data1>data1</Data1>
        <Data2>data2</Data2>
    </Account2>
 </Username2>

Comme vous pouvez le voir, l'héritier est

  • Nom d'utilisateur (chaîne de caractères du dict) >
  • Compte (chaque compte dans la Liste) >
  • Les données du compte (c'est-à-dire les propriétés de la classe).

L'obtention de cette disposition à partir d'un Dictionary<Username, List<Account>> est le point délicat et l'essence de cette question.

Il y a beaucoup de réponses "comment faire" ici sur la sérialisation, ce qui est ma faute puisque je n'ai pas été plus clair au début, mais maintenant je cherche une solution définitive.

0 votes

Donnez plus de détails sur le type d'application, et les données stockées, ainsi que la taille attendue.

3 votes

Pour sérialiser un dictionnaire : stackoverflow.com/questions/1111724

29voto

zebrabox Points 3937

Je stocke le fichier comme JSON . Puisque vous stockez un dictionnaire qui n'est qu'une liste de paires nom/valeur, c'est à peu près ce pour quoi json a été conçu.
Il existe un certain nombre de bibliothèques .NET json décentes et gratuites. un mais vous pouvez trouver une liste complète sur le premier lien.

0 votes

Json est un peu inhabituel pour le stockage local, mais c'est définitivement une excellente solution. Surtout avec une bibliothèque comme Json.NET de Newtonsoft.

2 votes

Au lieu de m'appuyer sur une bibliothèque tierce, je stocke les données en utilisant le type de jeu de données intégré qui est très simple à écrire sur le disque (voir l'exemple de Tom Miller ci-dessous).

24voto

Adam Robinson Points 88472

Cela dépend vraiment de ce que vous stockez. S'il s'agit de données structurées, XML ou un SGBDR SQL très léger comme SQLite ou SQL Server Compact Edition vous conviendra parfaitement. La solution SQL devient particulièrement convaincante si les données dépassent une taille triviale.

Si vous stockez de gros morceaux de données relativement non structurées (des objets binaires comme des images, par exemple), il est évident que ni une base de données ni une solution XML ne sont appropriées, mais compte tenu de votre question, je suppose qu'il s'agit plutôt du premier cas que du second.

0 votes

Les fichiers de configuration XML doivent être structurés ?

1 votes

@Roboto : XML, par définition, est structuré. Cela ne signifie pas pour autant que vous devez les utiliser de manière très structurée.

18voto

GalacticJello Points 6127

Toutes les réponses ci-dessus sont bonnes et permettent généralement de résoudre le problème.

Si vous avez besoin d'un moyen simple et gratuit pour passer à des millions de données, essayez le projet ESENT Managed Interface sur GitHub ou de NuGet .

ESENT est un moteur de stockage de base de données intégrable (ISAM) qui fait partie de Windows. Il fournit un stockage de données fiable, transactionnel, simultané et à haute performance avec un verrouillage au niveau des lignes, une journalisation en écriture et une isolation des instantanés. Il s'agit d'un wrapper géré pour l'API ESENT Win32.

Il possède un objet PersistentDictionary qui est assez facile à utiliser. Considérez-le comme un objet Dictionary(), mais il est automatiquement chargé et enregistré sur le disque sans code supplémentaire.

Par exemple :

/// <summary>
/// Ask the user for their first name and see if we remember 
/// their last name.
/// </summary>
public static void Main()
{
    PersistentDictionary<string, string> dictionary = new PersistentDictionary<string, string>("Names");
    Console.WriteLine("What is your first name?");
    string firstName = Console.ReadLine();
    if (dictionary.ContainsKey(firstName))
    {
        Console.WriteLine("Welcome back {0} {1}", firstName, dictionary[firstName]);
    }
    else
    {
        Console.WriteLine("I don't know you, {0}. What is your last name?", firstName);
        dictionary[firstName] = Console.ReadLine();
    }

Pour répondre à la question de George :

Types de clés pris en charge

Seuls ces types sont pris en charge en tant que clés de dictionnaire :

Booléen Octet Int16 UInt16 Int32 UInt32 Int64 UInt64 Flottant Double Guid DateTime TimeSpan String

Types de valeurs prises en charge

Les valeurs du dictionnaire peuvent être n'importe lesquelles des des types de clés, des versions nullables des types de clés, Uri, IPAddress ou une structure sérialisable. Une structure est considérée comme sérialisable uniquement si elle répond à tous ces critères :

- La structure est marquée comme sérialisable - Chaque membre de la struct est soit : 1. Un type de données primitif (par exemple Int32) 2. Une chaîne de caractères, un Uri ou une adresse IPAddress 3. Une structure sérialisable.

Ou, pour le dire autrement, un structure sérialisable ne peut contenir aucune référence à un objet de classe. Ce Ceci est fait pour préserver la cohérence de l'API. L'ajout d'un objet à un PersistentDictionary crée une copie de l'objet l'objet par sérialisation. La modification de l'objet original ne modifiera pas la copie, ce qui entraînerait un comportement déroutant. Pour éviter ces problèmes, le PersistentDictionary n'acceptera que des types de valeurs comme valeurs.

Peut être sérialisé [Serializable] struct Good { public DateTime ? Reçue ; public string Name ; public Decimal Price ; public Uri Url ; }

Ne peut pas être sérialisé [Serializable] struct Bad { public byte[] Data ; // les tableaux ne sont pas pris en charge public Exception Error ; // objet de référence }

1 votes

Cette méthode consiste essentiellement à remplacer le générique intégré par un dictionnaire persistant. C'est une solution assez élégante, mais comment gère-t-elle les objets complexes comme dans l'exemple du PO ? Est-ce qu'elle stocke tout à l'intérieur du dict, ou seulement le dict lui-même ?

0 votes

Cela pourrait ne pas atteindre l'objectif ultime qui est de tenter de sauver les listes de type Account. Les clés seraient bien, mais rendre le générique sérialisable pourrait être difficile :/.

1 votes

Si quelqu'un d'autre met en œuvre ce système, il pourrait être utile de savoir que vous pouvez utiliser Nuget pour obtenir ManagedEsent. Vous devez ensuite référencer Esent.Collections.DLL et Esent.ISAM.DLL. Ajoutez ensuite "using Microsoft.Isam.Esent.Collections.Generic ;" pour obtenir le type PersistentDictionary. Il se peut que la DLL Collections doive être téléchargée à partir de l'option de téléchargement de l'onglet managedesent.codeplex.com

15voto

Cheeso Points 87022

XML est facile à utiliser, via la sérialisation. Utilisez Stockage isolé .

Voir aussi Comment décider où stocker l'état par utilisateur ? Le registre ? Données d'application ? Stockage isolé ?

public class UserDB 
{
    // actual data to be preserved for each user
    public int A; 
    public string Z; 

    // metadata        
    public DateTime LastSaved;
    public int eon;

    private string dbpath; 

    public static UserDB Load(string path)
    {
        UserDB udb;
        try
        {
            System.Xml.Serialization.XmlSerializer s=new System.Xml.Serialization.XmlSerializer(typeof(UserDB));
            using(System.IO.StreamReader reader= System.IO.File.OpenText(path))
            {
                udb= (UserDB) s.Deserialize(reader);
            }
        }
        catch
        {
            udb= new UserDB();
        }
        udb.dbpath= path; 

        return udb;
    }

    public void Save()
    {
        LastSaved= System.DateTime.Now;
        eon++;
        var s= new System.Xml.Serialization.XmlSerializer(typeof(UserDB));
        var ns= new System.Xml.Serialization.XmlSerializerNamespaces();
        ns.Add( "", "");
        System.IO.StreamWriter writer= System.IO.File.CreateText(dbpath);
        s.Serialize(writer, this, ns);
        writer.Close();
    }
}

1 votes

Ce n'est pas très portable ni très propre

3 votes

Ça ressemble à un code à copier-coller pour que vous puissiez être le premier à poster. Il aurait été préférable de s'en tenir à vos liens.

9 votes

Eh bien, oui, je l'ai coupé directement de l'application que j'ai écrite, qui fait ça. Roboto, c'est quoi ton problème ?

9voto

Je recommande la classe de lecteur/écriture XML pour les fichiers car elle est facilement sérialisée.

Sérialisation en C#

La sérialisation (connue sous le nom de pickling dans python) est un moyen simple de convertir un fichier objet en une représentation binaire qui binaire qui peut ensuite être écrite sur un disque ou être envoyé sur un fil.

Il est utile, par exemple, pour enregistrer facilement les paramètres dans un fichier.

Vous pouvez sérialiser vos propres classes si vous les marquez avec [Serializable] attribut. Cela permet de sérialiser tous les membres d'une classe, sauf ceux marqués comme [NonSerialized] .

Le code suivant vous montre comment procéder :

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;

namespace ConfigTest
{ [ Serializable() ]

    public class ConfigManager
    {
        private string windowTitle = "Corp";
        private string printTitle = "Inventory";

        public string WindowTitle
        {
            get
            {
                return windowTitle;
            }
            set
            {
                windowTitle = value;
            }
        }

        public string PrintTitle
        {
            get
            {
                return printTitle;
            }
            set
            {
                printTitle = value;
            }
        }
    }
}

Ensuite, dans un ConfigForm, vous appelez votre classe ConfigManager et vous la sérialisez !

public ConfigForm()
{
    InitializeComponent();
    cm = new ConfigManager();
    ser = new XmlSerializer(typeof(ConfigManager));
    LoadConfig();
}

private void LoadConfig()
{     
    try
    {
        if (File.Exists(filepath))
        {
            FileStream fs = new FileStream(filepath, FileMode.Open);
            cm = (ConfigManager)ser.Deserialize(fs);
            fs.Close();
        } 
        else
        {
            MessageBox.Show("Could not find User Configuration File\n\nCreating new file...", "User Config Not Found");
            FileStream fs = new FileStream(filepath, FileMode.CreateNew);
            TextWriter tw = new StreamWriter(fs);
            ser.Serialize(tw, cm);
            tw.Close();
            fs.Close();
        }    
        setupControlsFromConfig();
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}

Après avoir été sérialisé, vous pouvez alors appeler les paramètres de votre fichier de configuration en utilisant cm.WindowTitle, etc.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X