Frage

Ich schreibe eine Anwendung, die später von Benutzerdaten und speichert sie lokal für den Gebrauch nimmt. Die Anwendung wird gestartet und gestoppt werden ziemlich oft, und ich möchte es machen Speichern / Laden der Daten auf Anfrage Start / Ende.

Es wäre ziemlich einfach, wenn ich flache Dateien verwendet, da die Daten müssen nicht wirklich gesichert werden (es wird nur auf diesem Rechner gespeichert werden). Die Optionen, die ich glaube, sind also:

  • Flat-Dateien
  • XML
  • SQL DB

Flat-Dateien erfordern etwas mehr Aufwand (keine in Klassen wie mit XML gebaut) zu halten, jedoch habe ich verwendet, um XML nicht vor, und SQL scheint übertrieben für diese relativ leichte Aufgabe.

Gibt es noch andere Möglichkeiten zu erkunden lohnt? Wenn nicht, welche davon die beste Lösung?


Edit: Um ein wenig mehr Daten zu dem Problem hinzufügen, im Grunde das einzige, was ich zu speichern möchte ein Wörterbuch ist, dass wie folgt aussieht

Dictionary<string, List<Account>> 

wo Konto ist ein weiterer benutzerdefinierter Typ.

Serialisierung würde ich die dict als xmlroot und dann den Kontotypen als Attribut?


Update 2:

So ist es möglich, ein Wörterbuch serialisieren. Was es kompliziert macht, ist, dass der Wert für diese dict ein generischen selbst, das eine Liste von komplexen Datenstrukturen vom Typ Konto ist. Jedes Konto ist ziemlich einfach, es ist nur ein Bündel von Eigenschaften.

Es ist mein Verständnis, dass das Ziel hier ist zu versuchen, und mit diesem am Ende:

<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 die heirachy ist

  • Benutzername (string von dict)>
  • Konto (jedes Konto in der Liste)>
  • Kontodaten (dh Klasse Eigenschaften).

Beziehen dieses Layouts aus einem Dictionary<Username, List<Account>> ist der schwierige Bit, und das Wesen dieser Frage.

Es gibt viele ‚wie‘ Antworten hier auf der Serialisierung, die meine Schuld ist, da ich nicht machte es klarer früh, aber jetzt für eine bestimmte Lösung, die ich bin auf der Suche.

War es hilfreich?

Lösung

Ich würde speichern Sie die Datei als JSON . Da Sie ein Wörterbuch sind zu speichern, die nur ein Name / Wert-Paar-Liste ist, dann ist dies ziemlich viel, was json entworfen wurde.
Es gibt eine ziemlich viele anständige, freie .NET json Bibliotheken - hier ein aber Sie finden Sie eine vollständige Liste auf dem ersten Link.

Andere Tipps

Es hängt wirklich davon ab, was Sie speichern. Wenn Sie sich über strukturierte Daten sprechen, dann entweder XML oder ein sehr leichtes SQL RDBMS wie SQLite oder SQL Server Compact Edition wird auch für Sie arbeiten. Die SQL-Lösung wird besonders überzeugend, wenn die Daten bewegt sich über eine triviale Größe.

Wenn Sie große Stücke relativ unstrukturierten Daten (binäre Objekte wie Bilder, zum Beispiel) sind Speicher dann offensichtlich weder eine Datenbank noch XML-Lösung angemessen sind, aber angesichts Ihre Frage Ich vermute, es ist eher die ehemaligen ist als die letztere .

XML ist einfach zu bedienen, über die Serialisierung. Verwenden Sie Isolierte Speicherung .

Siehe auch Wie zu entscheiden, wo Staat pro Benutzer speichern? Registry? Anwendungsdaten? Isolated Storage?

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

Alle oben genannten sind gute Antworten, und in der Regel das Problem lösen.

Wenn Sie eine einfache, kostenlose Möglichkeit, maßstab, um Millionen von Stücken von Daten benötigen, probieren Sie die ESENT Managed Interface-Projekt auf CodePlex .

  

ESENT ist eine integrierbare Datenbank-Speicher-Engine (ISAM), die Teil von Windows ist. Es bietet eine zuverlässige, abgewickelt, gleichzeitig, Hochleistungsdatenspeicher mit Sperren auf Zeilenebene, Wal-Prinzip und die Snapshot-Isolation. Dies ist ein verwaltetes Wrapper für den ESENT Win32-API.

Es hat ein PersistentDictionary Objekt, das ganz einfach zu bedienen ist. Betrachten Sie es als ein Dictionary () Objekt, aber es wird automatisch geladen, von und auf die Festplatte ohne zusätzlichen Code 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 George Frage zu beantworten:

  

Unterstützte Schlüsseltypen

     

Nur diese Typen unterstützt als   Wörterbuch Tasten:

     

Boolean Byte Int16 UInt16 Int32 UInt32   Int64 UInt64 Float   Doppel Guid Datetime Timespan String

     

Unterstützte Wertetypen

     

Wörterbuch Werte können Sie eine der sein   Schlüsseltypen, Nullable Versionen der   Schlüsseltypen, Uri, IPAddress oder ein   serializable Struktur. Eine Struktur ist   wenn es nur serializable betrachtet   erfüllt all diese Kriterien:

     

• Die Struktur ist gekennzeichnet als   serializable • Jedes Mitglied der   Struktur ist entweder:   1. Eine primitive Datentypen (z.B. Int32)   2. Ein String, Uri oder IPAddress   3. Eine serializable Struktur.

     

Oder, um es anders ausgedrückt, ein   serializable Struktur nicht enthalten   alle Verweise auf ein Klassenobjekt. Diese   erfolgt API Konsistenz zu erhalten.   Hinzufügen eines Objekts zu einem   PersistentDictionary erstellt eine Kopie   das Objekt, wenn die Serialisierung.   das ursprüngliche Objekt ändern wird nicht   Ändern Sie die Kopie, die dazu führen würde,   verwirrendes Verhalten. Um zu vermeiden, diejenigen,   Probleme der PersistentDictionary Wille   nur Werttypen als Wert annehmen.

     

Kann serialisierende [Serializable] struct Gut {       public Datetime? Empfangen;       public string Name;       public Dezimal Preis;       Öffentlichkeit Uri URL; }

     

Can not Be Serialized [Serializable] struct Bad {       public byte [] Daten; // Arrays werden nicht unterstützt       public Ausnahmefehler; // Referenzobjekt}

Ich empfehle XML-Leser / Schreiber-Klasse für Dateien, weil es leicht ist serialisiert.

Serialisierung in C #

  

Serialisierung (bekannt als Beize in   Python) ist eine einfache Möglichkeit, ein konvertieren   Objekt in eine binäre Darstellung, dass   kann dann z.B. auf die Platte geschrieben oder   gesendet über einen Draht.

     

Es ist nützlich, zum Beispiel zum einfachen Speichern der Einstellungen in eine Datei.

     

Sie können Ihre eigenen Klassen serialisiert werden, wenn   Sie markieren sie mit [Serializable]   Attribut. Diese serialisiert alle Mitglieder   eine Klasse, mit Ausnahme der gekennzeichneten   [NonSerialized].

Im Folgenden ist Code, den Sie zu zeigen, wie dies zu tun:

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, dann vielleicht in einem ConfigForm, rufen Sie Ihre ConfigManager Klasse und Serialize it!

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

Nachdem es serialisiert wurde, können Sie die Parameter Ihrer Konfigurationsdatei aufrufen cm.WindowTitle verwenden, etc.

Eine vierte Option zu denen, die Sie erwähnen, sind Binärdateien . Obwohl das klingt Arkan und schwer, es ist wirklich einfach mit dem Serialisierung-API in .NET.

Ob Sie Binär- oder XML-Dateien auswählen, können Sie den gleichen Serialisierung-API verwenden, obwohl Sie verschiedene Serializer verwenden würden.

Um binäre serialize eine Klasse, muss es mit dem Attribut [Serializable] markiert werden oder ISerializable implementieren.

Sie können mit etwas Ähnliches tun XML , obwohl es die Schnittstelle aufgerufen wird IXmlSerializable, und die Attribute [XmlRoot] und andere Attribute im System.Xml.Serialization Namespace.

Wenn Sie eine relationale Datenbank verwenden möchten, SQL Server Compact Edition ist kostenlos und sehr leicht und auf der Grundlage einer einzigen Datei.

Wenn Sie Ihre Sammlung zu groß wird, habe ich festgestellt, dass die XML-Serialisierung ziemlich langsam bekommt. Eine weitere Möglichkeit, Ihren Wörterbuch zu serialisiert wäre „roll your own“ mit einem Binary und Binary.

Hier ist ein Beispielcode nur Sie, um loszulegen. Sie können diese generischen Erweiterungsmethoden machen jede Art von Wörterbuch zu handhaben, und es funktioniert ganz gut, aber ist zu ausführlich hier zu posten.

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

Gerade fertig Datencodierung Speicherung für ein aktuelles Projekt. Hier ist mein 5 Cent.

Ich begann mit binärer Serialisierung. Es war langsam (ca. 30 sec für Belastung von 100.000 Objekten) und es war eine ziemlich große Datei auf der Festplatte als auch zu schaffen. Allerdings dauerte es ein paar Zeilen Code zu implementieren, und ich habe meine alle Speicheranforderungen abgedeckt. Um eine bessere Leistung zog ich auf benutzerdefinierte Serialisierung. Gefunden FastSerialization Rahmen von Tim Haynes auf Code Project. Tatsächlich ist es ein paar Mal schneller (bekam 12 Sekunden für Last, 8 Sek speichern, 100K Datensätze) und es dauert weniger Speicherplatz. Der Rahmen ist auf der von GalacticJello in einem frühen Beitrag skizzierte Technik gebaut.

Dann zog ich nach SQLite und konnte 2 mal 3 mal schnelle Leistung erhalten - 6 Sekunden für Last und 4 sec für speichern, 100K Datensätze. Es enthält Tabellen Anwendungstypen ADO.NET Parsen. Es gab auch mir viele kleinere Datei auf der Festplatte. Dieser Artikel beschreibt, wie die beste Leistung von ADO.NET bekommen: http: //sqlite.phxsoftware .com / Foren / t / 134.aspx . INSERT-Anweisungen zu erzeugen ist eine sehr schlechte Idee. Sie können erraten, wie ich dazu kam, dass wissen. :) Tatsächlich nahm SQLite Implementierung mich ziemlich viel Zeit und eine sorgfältige Messung der Zeit Einnahme von so ziemlich jeder Zeile des Codes.

Wenn Ihre Daten sind komplex, hoch in der Menge oder Sie müssen es abfragen lokal dann Objektdatenbanken möglicherweise eine gültige Option sein. Ich würde vorschlagen, Blick auf Db4o oder Karvonite .

Das erste, was ich mir anschaue, würde eine Datenbank. Allerdings Serialisierung ist eine Option. Wenn Sie für binäre Serialisierung gehen, dann würde ich vermeiden BinaryFormatter - es hat eine Tendenz, wütend zwischen den Versionen zu erhalten, wenn Sie Felder ändern usw. Xml über XmlSerialzier wäre in Ordnung, und Side-by-side sein kompatibel (dh mit den gleichen Klassendefinitionen) mit protobuf-net, wenn Sie vertragsbasierte binäre Serialisierung (geben Sie eine Flat-File-Serializer ohne Anstrengung) versuchen wollen.

Viele der Antworten in diesem Thread Versuch, die Lösung zu overengineer. Wenn ich richtig bin, wollen Sie nur zum Speichern von Benutzereinstellungen.

Verwenden Sie eine INI-Datei oder App.Config Datei für diese.

Wenn ich falsch, und Sie Daten zu speichern, die mehr als nur Einstellungen ist, eine flache Textdatei im CSV-Format verwenden. Diese sind schnell und einfach ohne den Overhead von XML. Leute wie zu poo poo diese, da sie nicht so elegant sind, skalieren nicht schön und sehen nicht so gut auf einem Lebenslauf, aber es könnte die beste Lösung für Sie je nach dem, was Sie benötigen.

Ich habe mehrere „stand alone“ getan Anwendungen, die einen lokalen Datenspeicher haben. Ich denke, das Beste, was zu verwenden würde SQL Server Compact Edition (wie SQLAnywhere früher bekannt) sein.

Es ist leicht und frei. Darüber hinaus können Sie halten eine Datenzugriffsschicht zu schreiben, die in anderen Projekten wiederverwendet werden kann und wenn die App immer maßstäblich etwas größer wie Blas- SQL Server benötigt, müssen Sie nur die Verbindungszeichenfolge ändern.

Meine erste Neigung ist eine Access-Datenbank. Die MDB-Dateien werden lokal gespeichert und können verschlüsselt werden, wenn dies für notwendig erachtet wird. Obwohl XML oder JSON würde auch für viele Szenarien arbeiten. Flache Dateien, die ich nur für Lese verwenden würde nur, nicht-Suche (vorwärts nur lesen) Informationen. Ich neige dazu, csv-Format Schränkungsweite bevorzugen.

Es hängt von der Menge der Daten, die Sie speichern suchen. In Wirklichkeit gibt es keinen Unterschied zwischen dem flachen Dateien und XML. XML wäre wahrscheinlich zu bevorzugen, da sie eine Struktur auf das Dokument zur Verfügung stellt. In der Praxis

Die letzte Option, und eine Vielzahl von Anwendungen verwenden jetzt die Windows-Registry ist. Ich weiß nicht persönlich empfehle es (Registry aufblasen, Korruption, andere potenzielle Probleme), aber es ist eine Option.

Ohne zu wissen, was Ihre Daten aussieht, das heißt, die Komplexität, Größe, etc ... XML ist leicht zu pflegen und leicht zugänglich. Ich würde eine Access-Datenbank verwenden, und flache Dateien sind schwieriger, die Langstrecke zu halten über, vor allem, wenn Sie mit mehr als ein Datenfeld / Elemente in der Datei handeln.

Ich habe mit großem Flat-File-Daten-Feeds in guten Mengen täglich, und auch wenn ein extremes Beispiel, Flat-File-Daten sind viel schwieriger zu halten als die XML-Daten-Feeds ich verarbeiten.

Ein einfaches Beispiel für Laden von XML-Daten in einen Datensatz mit C #:

DataSet reportData = new DataSet();

reportData.ReadXml(fi.FullName);

Sie können auch als Option LINQ to XML-Check-out für die Abfrage der XML-Daten ...

HTH ...

Wenn Sie die binäre Serialisierung Weg zu gehen, sollten Sie die Geschwindigkeit, mit der ein bestimmtes Mitglied des Datums zugegriffen werden muss. Wenn es nur eine kleine Sammlung, die gesamte Datei geladen wird Sinn machen, aber wenn es groß ist, können Sie auch eine Indexdatei prüfen.

Tracking-Account-Eigenschaften / Felder, die in der Datei an einer bestimmten Adresse befinden, können Sie Zugriffszeit beschleunigen helfen, vor allem, wenn Sie diese Indexdatei basierend auf Schlüsselverwendung zu optimieren. (Möglicherweise auch dann, wenn Sie auf die Festplatte schreiben.)

Je nach compelexity Ihres Kontos Objekt, würde ich entweder XML oder Flat File empfehlen.

Wenn es nur ein paar Werte zu speichern für jedes Konto sind, können Sie sie auf eine Properties-Datei speichern können, wie folgt aus:

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 Properties-Datei sollte einfach sein, da es direkt in einem String-Wörterbuch abbildet.

Wie, wo diese Datei zu speichern, die beste Wahl zu speichern in AppData-Ordner sein würde, in einem Unterordner für Ihr Programm. Dies ist ein Ort, an den aktuellen Benutzer immer Zugriff auf Schreib haben wird, und es wird sicher verwahrt von anderen Benutzern durch das Betriebssystem selbst.

Halten Sie es einfach - wie Sie gesagt haben, eine flache Datei ausreichend ist. Verwenden Sie eine flache Datei.

Dies wird vorausgesetzt, dass Sie Ihre Anforderungen richtig analysiert haben. Ich würde die Serialisierung als XML-Schritt, Overkill für ein einfaches Wörterbuch überspringen. Das Gleiche gilt für eine Datenbank.

Nach meiner Erfahrung in den meisten Fällen JSON in einer Datei genug ist (meistens müssen Sie einen Array oder ein Objekt speichern oder einfach nur eine einzelne Zahl oder eine Zeichenkette). Ich brauche selten SQLite (die für die Einstellung es mehr Zeit braucht und Verwendung es, die meisten der Zeit ist es viel des Guten).

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top