Ich schreibe eine Anwendung, die Benutzerdaten aufnimmt und lokal zur späteren Verwendung speichert. Die Anwendung wird ziemlich oft gestartet und gestoppt, und ich möchte, dass die Daten beim Start / Ende der Anwendung gespeichert / geladen werden.
Es wäre ziemlich einfach, wenn ich flache Dateien verwenden würde, da die Daten nicht wirklich gesichert werden müssen (sie werden nur auf diesem PC gespeichert). Die Optionen, die ich glaube, sind also:
- Flat Files
- XML
- SQL DB
Die Pflege von Flatfiles erfordert etwas mehr Aufwand (keine integrierten Klassen wie bei XML), ich habe jedoch noch kein XML verwendet, und SQL scheint für diese relativ einfache Aufgabe ein Overkill zu sein.
Gibt es noch andere Möglichkeiten, die es wert sind, erkundet zu werden? Wenn nicht, welche davon ist die beste Lösung?
Bearbeiten: Um dem Problem ein wenig mehr Daten hinzuzufügen, möchte ich im Grunde nur ein Wörterbuch speichern, das so aussieht
Dictionary<string, List<Account>>
Dabei ist Konto ein anderer benutzerdefinierter Typ.
Würde ich das Diktat als XML-Wurzel und dann den Kontotyp als Attribute serialisieren?
Update 2:
So ist es möglich, ein Wörterbuch zu serialisieren. Was es kompliziert macht, ist, dass der Wert für dieses Diktat selbst ein Generikum ist, bei dem es sich um eine Liste komplexer Datenstrukturen vom Typ Konto handelt. Jedes Konto ist ziemlich einfach, es sind nur eine Reihe von Eigenschaften.
Nach meinem Verständnis besteht das Ziel hier darin, Folgendes zu erreichen:
<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>
Wie Sie sehen können, ist die Erbschaft
- Benutzername (Zeichenfolge)>
- Konto (jedes Konto in der Liste)>
- Kontodaten (dh Klasseneigenschaften).
Dieses Layout von a zu erhalten, Dictionary<Username, List<Account>>
ist das Knifflige und das Wesentliche dieser Frage.
Hier gibt es viele Anleitungen zur Serialisierung, was meine Schuld ist, da ich es nicht frühzeitig klarer gemacht habe, aber jetzt suche ich nach einer definitiven Lösung.
quelle
Antworten:
Ich würde die Datei als JSON speichern . Da Sie ein Wörterbuch speichern, das nur eine Name / Wert-Paarliste ist, ist dies ziemlich genau das, wofür json entwickelt wurde.
Es gibt einige anständige, kostenlose .NET json-Bibliotheken - hier ist eine, aber eine vollständige Liste finden Sie unter dem ersten Link.
quelle
Es hängt wirklich davon ab, was Sie speichern. Wenn Sie über strukturierte Daten sprechen, eignet sich entweder XML oder ein sehr leichtes SQL-RDBMS wie SQLite oder SQL Server Compact Edition gut für Sie. Die SQL-Lösung wird besonders überzeugend, wenn die Daten eine triviale Größe überschreiten.
Wenn Sie große Teile relativ unstrukturierter Daten speichern (z. B. binäre Objekte wie Bilder), sind natürlich weder eine Datenbank noch eine XML-Lösung geeignet, aber angesichts Ihrer Frage schätze ich, dass es sich eher um die erstere als um die letztere handelt.
quelle
Alle oben genannten sind gute Antworten und lösen im Allgemeinen das Problem.
Wenn Sie eine einfache und kostenlose Möglichkeit zur Skalierung auf Millionen von Daten benötigen, probieren Sie das ESENT Managed Interface-Projekt auf GitHub oder in NuGet aus .
Es hat ein PersistentDictionary-Objekt, das recht einfach zu verwenden ist. Stellen Sie sich das als Dictionary () - Objekt vor, aber es wird automatisch von der Festplatte geladen und ohne zusätzlichen Code auf der Festplatte gespeichert.
Zum Beispiel:
/// <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(); }
Um Georges Frage zu beantworten:
quelle
XML ist über die Serialisierung einfach zu verwenden. Verwenden Sie isolierten Speicher .
Siehe auch So entscheiden Sie, wo der Status pro Benutzer gespeichert werden soll. Registrierung? Anwendungsdaten? Isolierte Lagerung?
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(); } }
quelle
Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"{AppName}\{FileName}.XML");
Ich empfehle die XML-Lese- / Schreibklasse für Dateien, da sie leicht serialisiert werden kann.
Serialisierung in C #
Der folgende Code zeigt Ihnen, wie das geht:
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; } } } }
Sie rufen dann in einer ConfigForm Ihre ConfigManager-Klasse auf und serialisieren sie!
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); } }
Nach der Serialisierung können Sie die Parameter Ihrer Konfigurationsdatei mit cm.WindowTitle usw. aufrufen.
quelle
Wenn Ihre Sammlung zu groß wird, habe ich festgestellt, dass die XML-Serialisierung ziemlich langsam wird. Eine weitere Option zum Serialisieren Ihres Wörterbuchs wäre "Roll Your Own" mit einem BinaryReader und BinaryWriter.
Hier ist ein Beispielcode, um Ihnen den Einstieg zu erleichtern. Sie können diese generischen Erweiterungsmethoden für alle Arten von Wörterbüchern verwenden. Sie funktionieren recht gut, sind jedoch zu ausführlich, um sie hier zu veröffentlichen.
class Account { public string 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.ReadString(); AccountNumber = br.ReadInt32(); } } class Program { static void Serialize(string OutputFile) { // Write to disk using (Stream stream = File.Open(OutputFile, FileMode.Create)) { BinaryWriter bw = new BinaryWriter(stream); // Save number of entries bw.Write(accounts.Count); foreach (KeyValuePair<string, List<Account>> 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(string 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 entries = 0; entries < entryCount; entries++) { // Read in the key-value pairs string key = br.ReadString(); int accountCount = br.ReadInt32(); List<Account> accountList = new List<Account>(); for (int i = 0; i < accountCount; i++) { Account account = new Account(); account.Deserialize(br); accountList.Add(account); } accounts.Add(key, accountList); } } } static Dictionary<string, List<Account>> accounts = new Dictionary<string, List<Account>>(); static void Main(string[] args) { string accountName = "Bob"; List<Account> newAccounts = new List<Account>(); newAccounts.Add(AddAccount("A", 1)); newAccounts.Add(AddAccount("B", 2)); newAccounts.Add(AddAccount("C", 3)); accounts.Add(accountName, newAccounts); accountName = "Tom"; newAccounts = new List<Account>(); newAccounts.Add(AddAccount("A1", 11)); newAccounts.Add(AddAccount("B1", 22)); newAccounts.Add(AddAccount("C1", 33)); accounts.Add(accountName, newAccounts); string saveFile = @"C:\accounts.bin"; Serialize(saveFile); // clear it out to prove it works accounts.Clear(); Deserialize(saveFile); } static Account AddAccount(string AccountName, int AccountNumber) { Account account = new Account(); account.AccountName = AccountName; account.AccountNumber = AccountNumber; return account; } }
quelle
Eine vierte Option zu den von Ihnen erwähnten sind Binärdateien . Obwohl das geheimnisvoll und schwierig klingt, ist es mit der Serialisierungs-API in .NET wirklich einfach.
Unabhängig davon, ob Sie Binär- oder XML-Dateien auswählen, können Sie dieselbe Serialisierungs-API verwenden, obwohl Sie unterschiedliche Serialisierer verwenden würden.
Um eine Klasse binär zu serialisieren, muss sie mit dem Attribut [Serializable] gekennzeichnet sein oder ISerializable implementieren.
Sie können mit XML etwas Ähnliches tun , obwohl die Schnittstelle dort IXmlSerializable heißt und die Attribute [XmlRoot] und andere Attribute im System.Xml.Serialization-Namespace sind.
Wenn Sie eine relationale Datenbank verwenden möchten, ist SQL Server Compact Edition kostenlos und sehr leicht und basiert auf einer einzelnen Datei.
quelle
Ich habe gerade die Datenspeicherung für mein aktuelles Projekt beendet. Hier sind meine 5 Cent.
Ich habe mit der binären Serialisierung begonnen. Es war langsam (ungefähr 30 Sekunden beim Laden von 100.000 Objekten) und es wurde auch eine ziemlich große Datei auf der Festplatte erstellt. Die Implementierung dauerte jedoch einige Codezeilen und ich konnte alle Speicheranforderungen abdecken. Um eine bessere Leistung zu erzielen, habe ich die benutzerdefinierte Serialisierung übernommen. FastSerialization Framework von Tim Haynes für Code Project gefunden. In der Tat ist es ein paar Mal schneller (12 Sekunden zum Laden, 8 Sekunden zum Speichern, 100.000 Datensätze) und es benötigt weniger Speicherplatz. Das Framework basiert auf der Technik, die GalacticJello in einem früheren Beitrag beschrieben hat.
Dann bin ich zu SQLite gewechselt und konnte 2 mal 3 mal schnellere Leistung erzielen - 6 Sekunden für das Laden und 4 Sekunden für das Speichern von 100K-Datensätzen. Es umfasst das Parsen von ADO.NET-Tabellen nach Anwendungstypen. Es gab mir auch viel kleinere Datei auf der Festplatte. In diesem Artikel wird erläutert, wie Sie die beste Leistung von ADO.NET erzielen: http://sqlite.phxsoftware.com/forums/t/134.aspx . Das Generieren von INSERT-Anweisungen ist eine sehr schlechte Idee. Sie können sich vorstellen, wie ich davon erfahren habe. :) In der Tat hat mich die Implementierung von SQLite ziemlich viel Zeit gekostet und die Zeit, die so ziemlich jede Zeile des Codes benötigt, sorgfältig gemessen.
quelle
Das erste, was ich mir ansehen würde, ist eine Datenbank. Die Serialisierung ist jedoch eine Option. Wenn Sie sich für die binäre Serialisierung entscheiden, würde ich dies vermeiden
BinaryFormatter
- es besteht die Tendenz, dass Sie zwischen den Versionen wütend werden, wenn Sie Felder usw. ändern. XML viaXmlSerialzier
wäre in Ordnung und kann nebeneinander kompatibel sein (dh mit denselben Klassendefinitionen). mit protobuf-net, wenn Sie eine vertragsbasierte binäre Serialisierung ausprobieren möchten (ohne Aufwand einen Flatfile-Serializer).quelle
Wenn Ihre Daten komplex und quantitativ sind oder lokal abgefragt werden müssen, sind Objektdatenbanken möglicherweise eine gültige Option. Ich würde vorschlagen , auf der Suche Db4o oder Karvonite .
quelle
Viele der Antworten in diesem Thread versuchen, die Lösung zu überarbeiten. Wenn ich richtig bin, möchten Sie nur die Benutzereinstellungen speichern.
Verwenden Sie dazu eine INI-Datei oder eine App.Config-Datei.
Wenn ich falsch liege und Sie Daten speichern, die mehr als nur Einstellungen sind, verwenden Sie eine flache Textdatei im CSV-Format. Diese sind schnell und einfach ohne den Aufwand von XML. Leute mögen es, diese zu poo poo, da sie nicht so elegant sind, nicht gut skalieren und in einem Lebenslauf nicht so gut aussehen, aber es könnte die beste Lösung für Sie sein, je nachdem, was Sie brauchen.
quelle
Ohne zu wissen, wie Ihre Daten aussehen, dh die Komplexität, Größe usw. XML ist einfach zu warten und leicht zugänglich. Ich würde KEINE Access-Datenbank verwenden, und Flatfiles sind auf lange Sicht schwieriger zu pflegen, insbesondere wenn Sie mit mehr als einem Datenfeld / -element in Ihrer Datei arbeiten.
Ich beschäftige mich täglich mit großen Flatfile-Datenfeeds in guten Mengen, und obwohl ein extremes Beispiel ist, sind Flatfile-Daten viel schwieriger zu pflegen als die von mir verarbeiteten XML-Datenfeeds.
Ein einfaches Beispiel für das Laden von XML-Daten in ein Dataset mit C #:
DataSet reportData = new DataSet(); reportData.ReadXml(fi.FullName);
Sie können auch LINQ to XML als Option zum Abfragen der XML-Daten auschecken ...
HTH ...
quelle
Ich habe mehrere "eigenständige" Apps erstellt, die über einen lokalen Datenspeicher verfügen. Ich denke, die beste Verwendung wäre SQL Server Compact Edition (früher bekannt als SQLAnywhere).
Es ist leicht und kostenlos. Darüber hinaus können Sie beim Schreiben einer Datenzugriffsschicht bleiben, die in anderen Projekten wiederverwendbar ist. Wenn die App jemals auf einen größeren SQL-Server skaliert werden muss, müssen Sie nur die Verbindungszeichenfolge ändern.
quelle
Abhängig von der Komplexität Ihres Kontoobjekts würde ich entweder XML- oder Flatfile empfehlen.
Wenn für jedes Konto nur einige Werte gespeichert werden müssen, können Sie diese in einer Eigenschaftendatei wie folgt speichern:
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
... und so weiter. Das Lesen aus einer Eigenschaftendatei sollte einfach sein, da es direkt einem Zeichenfolgenwörterbuch zugeordnet ist.
Der Speicherort dieser Datei ist am besten im AppData-Ordner in einem Unterordner für Ihr Programm zu speichern. Dies ist ein Ort, an dem aktuelle Benutzer immer Zugriff auf das Schreiben haben und der vom Betriebssystem selbst vor anderen Benutzern geschützt wird.
quelle
Meine erste Neigung ist eine Zugangsdatenbank. Die MDB-Dateien werden lokal gespeichert und können bei Bedarf verschlüsselt werden. XML oder JSON würden jedoch auch für viele Szenarien funktionieren. Flatfiles würde ich nur für schreibgeschützte, nicht suchende (schreibgeschützte) Informationen verwenden. Ich bevorzuge das CSV-Format, um die Breite einzustellen.
quelle
Dies hängt von der Datenmenge ab, die Sie speichern möchten. In Wirklichkeit gibt es keinen Unterschied zwischen Flatfiles und XML. XML wäre wahrscheinlich vorzuziehen, da es dem Dokument eine Struktur verleiht. In der Praxis,
Die letzte Option, die derzeit von vielen Anwendungen verwendet wird, ist die Windows-Registrierung. Ich persönlich empfehle es nicht (Registry Bloat, Korruption, andere potenzielle Probleme), aber es ist eine Option.
quelle
Wenn Sie die binäre Serialisierungsroute wählen, berücksichtigen Sie die Geschwindigkeit, mit der auf ein bestimmtes Mitglied des Datums zugegriffen werden muss. Wenn es sich nur um eine kleine Sammlung handelt, ist das Laden der gesamten Datei sinnvoll. Wenn sie jedoch groß ist, können Sie auch eine Indexdatei in Betracht ziehen.
Das Verfolgen von Kontoeigenschaften / Feldern, die sich an einer bestimmten Adresse in der Datei befinden, kann Ihnen dabei helfen, die Zugriffszeit zu verkürzen, insbesondere wenn Sie diese Indexdatei basierend auf der Schlüsselverwendung optimieren. (Möglicherweise sogar, wenn Sie auf die Festplatte schreiben.)
quelle
Halten Sie es einfach - wie Sie sagten, ist eine flache Datei ausreichend. Verwenden Sie eine flache Datei.
Dies setzt voraus, dass Sie Ihre Anforderungen korrekt analysiert haben. Ich würde die Serialisierung als XML-Schritt überspringen, übertrieben für ein einfaches Wörterbuch. Gleiches gilt für eine Datenbank.
quelle
Nach meiner Erfahrung reicht JSON in den meisten Fällen in einer Datei aus (meistens müssen Sie ein Array oder ein Objekt oder nur eine einzelne Zahl oder Zeichenfolge speichern). Ich brauche selten SQLite (das mehr Zeit benötigt, um es einzurichten und zu verwenden, meistens ist es übertrieben).
quelle