Meilleur moyen de stocker des données localement dans .NET (C #)

J’écris une application qui prend les données de l’utilisateur et les stocke localement pour les utiliser ultérieurement. L’application sera démarrée et arrêtée assez souvent, et je souhaiterais la faire enregistrer / charger 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 que je crois sont donc:

  • Fichiers plats
  • XML
  • SQL DB

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

Y a-t-il d’autres voies à explorer? Sinon, laquelle de ces solutions est la meilleure?


Edit: Pour append un peu plus de données au problème, la seule chose que j’aimerais stocker est un dictionnaire qui ressemble à ceci

Dictionary<string, List> 

où Compte est un autre type personnalisé.

Est-ce que je sérialiserais le dict en tant que xmlroot, puis le type de compte en tant qu’atsortingbuts?


Mise à jour 2:

Il est donc possible de sérialiser un dictionnaire. Ce qui le complique, 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 Account. Chaque compte est assez simple, c’est juste un tas de propriétés.

Je crois comprendre que l’objective est d’essayer de finir avec ceci:

   data1 data2     data1 data2   data1 data2   

Comme vous pouvez voir l’inheritance est

  • Nom d’utilisateur (chaîne de dict)>
  • Compte (chaque compte dans la liste)>
  • Données de compte (c.-à-d. Propriétés de classe).

L’obtention de cette disposition à partir d’un Dictionary<Username, List> est la partie délicate, et l’essence de cette question.

Il y a beaucoup de réponses ici sur la sérialisation, ce qui est de ma faute car je ne l’ai pas clarifié au début, mais maintenant je cherche une solution définitive.

Je stocker le fichier en tant que JSON . Puisque vous stockez un dictionnaire qui est juste une liste de paires nom / valeur, c’est à peu près ce pour quoi json a été conçu.
Il y a pas mal de librairies .NET json décentes – en voici une mais vous pouvez trouver une liste complète sur le premier lien.

Cela dépend vraiment de ce que vous stockez. Si vous parlez de données structurées, alors XML ou un SGBDR SQL très léger comme SQLite ou SQL Server Compact Edition fonctionneront bien pour vous. La solution SQL devient particulièrement intéressante si les données dépassent une taille sortingviale.

Si vous stockez de gros morceaux de données relativement non structurées (des objects binarys comme des images, par exemple), il est évident que ni une firebase database ni une solution XML ne sont appropriées, mais compte tenu de votre question,

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

Voir aussi Comment décider où stocker l’état par utilisateur? Enregistrement? Données d’application? Stockage isolé?

 public class UserDB { // actual data to be preserved for each user public int A; public ssortingng Z; // metadata public DateTime LastSaved; public int eon; private ssortingng dbpath; public static UserDB Load(ssortingng 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(); } } 

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

Si vous avez besoin d’un moyen simple et gratuit pour évoluer vers des millions de données, essayez le projet d’interface gérée ESENT sur CodePlex .

ESENT est un moteur de stockage de firebase database intégrable (ISAM) intégré à Windows. Il fournit un stockage de données fiable, transactionnel, concurrent et hautement performant avec un locking au niveau de la ligne, une journalisation à écriture différée et une isolation des instantanés. Ceci est un wrapper géré pour l’API ESENT Win32.

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

Par exemple:

 ///  /// Ask the user for their first name and see if we remember /// their last name. ///  public static void Main() { PersistentDictionary dictionary = new PersistentDictionary("Names"); Console.WriteLine("What is your first name?"); ssortingng 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:

Octet Booléen Int16 UInt16 Int32 UInt32 Int64 UInt64 Float Double Guid DateHeure TimeSpan Chaîne

Types de valeur pris en charge

Les valeurs de dictionnaire peuvent être l’un des types de clé, les versions Nullables des types de clé, Uri, IPAddress ou une structure sérialisable. Une structure n’est considérée sérialisable que si elle répond à tous ces critères:

• La structure est marquée comme sérialisable. • Chaque membre de la structure est: 1. Un type de données primitif (par ex. Int32) 2. Une chaîne, Uri ou IPAddress 3. Une structure sérialisable.

Ou, pour le dire autrement, une structure sérialisable ne peut contenir aucune référence à un object de classe. Ceci est fait pour préserver la cohérence de l’API. L’ajout d’un object à un PersistentDictionary crée une copie de l’object via la sérialisation. La modification de l’object d’origine ne modifiera pas la copie, ce qui entraînerait un comportement déroutant. Pour éviter ces problèmes, le PersistentDictionary accepte uniquement les types de valeur en tant que valeurs.

Peut-on sérialiser [Serializable] struct Good {public DateTime? Reçu; chaîne publique Nom; Prix ​​décimal public; Uri Url publique; }

Ne peut pas être sérialisé [Serializable] struct Bad {octet public [] Données; // les tableaux ne sont pas supportés par le public Exception Error; // object de référence}

Je recommande la classe de lecture / écriture XML pour les fichiers car elle est facilement numérotée.

Sérialisation en C #

La sérialisation (connue sous le nom de pickling in python) est un moyen simple de convertir un object en une représentation binary qui peut ensuite être écrite sur le disque ou envoyée sur un câble.

C’est utile, par exemple, pour enregistrer facilement les parameters dans un fichier.

Vous pouvez sérialiser vos propres classes si vous les marquez avec l’atsortingbut [Serializable] . Cela sérialise tous les membres d’une classe, sauf ceux marqués comme [NonSerialized] .

Le code suivant vous indique comment procéder:

 using System; using System.Collections.Generic; using System.Text; using System.Drawing; namespace ConfigTest { [ Serializable() ] public class ConfigManager { private ssortingng windowTitle = "Corp"; private ssortingng printTitle = "Inventory"; public ssortingng WindowTitle { get { return windowTitle; } set { windowTitle = value; } } public ssortingng PrintTitle { get { return printTitle; } set { printTitle = value; } } } } 

Vous pouvez alors, dans un ConfigForm, appeler votre classe ConfigManager et la sérialiser!

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

Une fois sérialisé, vous pouvez alors appeler les parameters de votre fichier de configuration en utilisant cm.WindowTitle, etc.

Une quasortingème option pour ceux que vous mentionnez sont les fichiers binarys . Bien que cela puisse sembler obscur et difficile, l’API de sérialisation de .NET est très simple.

Que vous choisissiez des fichiers binarys ou XML, vous pouvez utiliser la même API de sérialisation, bien que vous utilisiez différents sérialiseurs.

Pour sérialiser en binary une classe, elle doit être marquée avec l’atsortingbut [Serializable] ou implémenter ISerializable.

Vous pouvez faire quelque chose de similaire avec XML , bien que l’interface s’appelle IXmlSerializable et que les atsortingbuts sont [XmlRoot] et d’autres atsortingbuts dans l’espace de noms System.Xml.Serialization.

Si vous souhaitez utiliser une firebase database relationnelle, SQL Server Compact Edition est gratuit et très léger et basé sur un seul fichier.

Je viens de terminer le codage du stockage de données pour mon projet actuel. Voici mes 5 centimes.

J’ai commencé avec la sérialisation binary. C’était lent (environ 30 secondes pour une charge de 100 000 objects) et cela créait également un gros fichier sur le disque. Cependant, cela m’a pris quelques lignes de code à mettre en œuvre et j’ai tout couvert. Pour obtenir de meilleures performances, j’ai opté pour la sérialisation personnalisée. Cadre FastSerialization trouvé par Tim Haynes sur Code Project. En effet, il est plusieurs fois plus rapide (12 secondes pour le chargement, 8 secondes pour la sauvegarde, 100 000 enregistrements) et cela prend moins d’espace disque. Le cadre est basé sur la technique décrite par GalacticJello dans un article précédent.

Ensuite, je suis passé à SQLite et j’ai pu obtenir 2 fois 3 fois plus de performances – 6 secondes pour le chargement et 4 secondes pour l’enregistrement, 100 000 enregistrements. Il inclut l’parsing des tables ADO.NET vers les types d’application. Cela m’a aussi donné beaucoup plus petit fichier sur le disque. Cet article explique comment optimiser les performances d’ADO.NET: http://sqlite.phxsoftware.com/forums/t/134.aspx . Générer des instructions INSERT est une très mauvaise idée. Vous pouvez deviner comment j’ai appris à ce sujet. 🙂 En effet, l’implémentation de SQLite m’a pris pas mal de temps, plus la mesure minutieuse du temps prenant à peu près toutes les lignes du code.

Si votre collection devient trop volumineuse, j’ai constaté que la sérialisation XML était assez lente. Une autre option pour sérialiser votre dictionnaire serait “roll your own” en utilisant un BinaryReader et un BinaryWriter.

Voici un exemple de code pour vous aider à démarrer. Vous pouvez créer ces méthodes d’extension génériques pour gérer n’importe quel type de dictionnaire, et cela fonctionne assez bien, mais est trop verbeux pour être publié ici.

 class Account { public ssortingng AccountName { get; set; } public int AccountNumber { get; set; } internal void Serialize(BinaryWriter bw) { // Add logic to serialize everything you need here // Keep in synch with Deserialize bw.Write(AccountName); bw.Write(AccountNumber); } internal void Deserialize(BinaryReader br) { // Add logic to deserialize everythin you need here, // Keep in synch with Serialize AccountName = br.ReadSsortingng(); AccountNumber = br.ReadInt32(); } } class Program { static void Serialize(ssortingng OutputFile) { // Write to disk using (Stream stream = File.Open(OutputFile, FileMode.Create)) { BinaryWriter bw = new BinaryWriter(stream); // Save number of ensortinges bw.Write(accounts.Count); foreach (KeyValuePair> accountKvp in accounts) { // Save each key/value pair bw.Write(accountKvp.Key); bw.Write(accountKvp.Value.Count); foreach (Account account in accountKvp.Value) { account.Serialize(bw); } } } } static void Deserialize(ssortingng InputFile) { accounts.Clear(); // Read from disk using (Stream stream = File.Open(InputFile, FileMode.Open)) { BinaryReader br = new BinaryReader(stream); int entryCount = br.ReadInt32(); for (int ensortinges = 0; ensortinges < entryCount; entries++) { // Read in the key-value pairs string key = br.ReadString(); int accountCount = br.ReadInt32(); List accountList = new List(); for (int i = 0; i < accountCount; i++) { Account account = new Account(); account.Deserialize(br); accountList.Add(account); } accounts.Add(key, accountList); } } } static Dictionary> accounts = new Dictionary>(); static void Main(ssortingng[] args) { ssortingng accountName = "Bob"; List newAccounts = new List(); newAccounts.Add(AddAccount("A", 1)); newAccounts.Add(AddAccount("B", 2)); newAccounts.Add(AddAccount("C", 3)); accounts.Add(accountName, newAccounts); accountName = "Tom"; newAccounts = new List(); newAccounts.Add(AddAccount("A1", 11)); newAccounts.Add(AddAccount("B1", 22)); newAccounts.Add(AddAccount("C1", 33)); accounts.Add(accountName, newAccounts); ssortingng saveFile = @"C:\accounts.bin"; Serialize(saveFile); // clear it out to prove it works accounts.Clear(); Deserialize(saveFile); } static Account AddAccount(ssortingng AccountName, int AccountNumber) { Account account = new Account(); account.AccountName = AccountName; account.AccountNumber = AccountNumber; return account; } } 

Si vos données sont complexes, volumineuses ou si vous devez les interroger localement, les bases de données object peuvent être une option valide. Je suggère de regarder Db4o ou Karvonite .

La première chose que je regarde est une firebase database. Cependant, la sérialisation est une option. Si vous BinaryFormatter pour la sérialisation binary, alors BinaryFormatter – il a tendance à se fâcher entre les versions si vous changez de champs, etc. Xml via XmlSerialzier serait XmlSerialzier , et peut être compatible côte à côte (avec la même classe définitions) avec protobuf-net si vous voulez essayer la sérialisation binary basée sur le contrat (vous donnant un sérialiseur de fichier plat sans aucun effort).

Un grand nombre de réponses dans ce fil tentent de surmener la solution. Si je suis correct, vous souhaitez simplement stocker les parameters utilisateur.

Utilisez un fichier .ini ou un fichier App.Config pour cela.

Si je me trompe et que vous stockez des données autres que des parameters, utilisez un fichier texte plat au format csv. Celles-ci sont rapides et faciles sans la surcharge de XML. Les gens aiment faire caca ceux-ci car ils ne sont pas aussi élégants, ne s’adaptent pas bien et ne semblent pas aussi bien sur un CV, mais cela pourrait être la meilleure solution pour vous en fonction de ce dont vous avez besoin.

J’ai fait plusieurs applications “autonomes” avec un magasin de données local. Je pense que la meilleure chose à utiliser serait SQL Server Compact Edition (anciennement SQLAnywhere).

C’est léger et gratuit. De plus, vous pouvez vous en tenir à l’écriture d’une couche d’access aux données réutilisable dans d’autres projets. En outre, si l’application doit évoluer vers un serveur SQL plus puissant, il vous suffit de modifier la chaîne de connexion.

Ma première inclination est une firebase database d’access. Les fichiers .mdb sont stockés localement et peuvent être chiffrés si cela est jugé nécessaire. Bien que XML ou JSON fonctionnent également pour de nombreux scénarios. Fichiers plats que je n’utiliserais que pour les informations en lecture seule, sans recherche (lecture anticipée uniquement). J’ai tendance à préférer le format CSV pour définir la largeur.

Cela dépend de la quantité de données que vous cherchez à stocker. En réalité, il n’y a pas de différence entre les fichiers plats et XML. XML serait probablement préférable car il fournit une structure au document. En pratique,

La dernière option, et de nombreuses applications utilisées actuellement, est le registre Windows. Je ne le recommande pas personnellement (Ballonnement, corruption, autres problèmes potentiels), mais c’est une option.

Sans savoir à quoi ressemblent vos données, c’est-à-dire la complexité, la taille, etc. Le XML est facile à entretenir et facilement accessible. Je n’utiliserais PAS de firebase database Access, et les fichiers plats sont plus difficiles à gérer sur le long terme, en particulier si vous avez plusieurs champs de données / éléments dans votre fichier.

Je traite quotidiennement de gros stream de données à fichier plat et, même dans un exemple extrême, les données à fichier plat sont beaucoup plus difficiles à gérer que les stream de données XML que je traite.

Un exemple simple de chargement de données XML dans un jeu de données en utilisant C #:

 DataSet reportData = new DataSet(); reportData.ReadXml(fi.FullName); 

Vous pouvez également consulter LINQ to XML pour interroger les données XML …

HTH …

Si vous allez sur la route de sérialisation binary, considérez la vitesse à laquelle un membre particulier du datum doit être accédé. S’il ne s’agit que d’une petite collection, le chargement de l’intégralité du fichier aura du sens, mais si le fichier est volumineux, vous pourriez également envisager un fichier d’index.

Le suivi des propriétés / champs du compte situés à une adresse spécifique du fichier peut vous aider à accélérer le temps d’access, en particulier si vous optimisez ce fichier d’index en fonction de l’utilisation des clés. (peut-être même lorsque vous écrivez sur le disque.)

En fonction de la compelexité de votre object Compte, je recommanderais XML ou Flat File.

S’il n’y a que quelques valeurs à stocker pour chaque compte, vous pouvez les stocker dans un fichier de propriétés, comme ceci:

 account.1.somekey=Some value account.1.someotherkey=Some other value account.1.somedate=2009-12-21 account.2.somekey=Some value 2 account.2.someotherkey=Some other value 2 

… et ainsi de suite. La lecture d’un fichier de propriétés doit être facile, car elle correspond directement à un dictionnaire de chaînes.

En ce qui concerne l’emplacement de stockage de ce fichier, le meilleur choix serait de le stocker dans le dossier AppData, à l’intérieur d’un sous-dossier de votre programme. C’est un endroit où les utilisateurs actuels auront toujours access à l’écriture, et le système d’exploitation lui-même le protège des autres utilisateurs.

Restez simple – comme vous l’avez dit, un fichier plat est suffisant. Utilisez un fichier plat.

Cela suppose que vous avez correctement analysé vos besoins. Je sauterais la sérialisation comme étape XML, trop pour un dictionnaire simple. Même chose pour une firebase database.

Dans mon expérience, dans la plupart des cas, JSON dans un fichier est suffisant (la plupart du temps, vous devez stocker un tableau ou un object ou simplement un numéro ou une chaîne). J’ai rarement besoin de SQLite (qui nécessite plus de temps pour le configurer et l’utiliser, la plupart du temps c’est exagéré).