it-swarm.com.de

Am besten speichern Sie Daten lokal in .NET (C #)

Ich schreibe eine Anwendung, die Benutzerdaten verwendet 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.

Wenn ich flache Dateien verwende, wäre das ziemlich einfach, da die Daten nicht wirklich gesichert werden müssen (sie werden nur auf diesem PC gespeichert). Die Optionen, die ich glaube, sind daher:

  • Flache Dateien
  • XML
  • SQL-Datenbank

Flat Files erfordern etwas mehr Aufwand für die Wartung (keine integrierten Klassen wie bei XML), allerdings habe ich XML noch nicht verwendet, und SQL scheint für diese relativ einfache Aufgabe ein Overkill zu sein.

Gibt es andere Möglichkeiten, die es zu erkunden gilt? 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 xmlroot und dann den Kontotyp als Attribute serialisieren?


Update 2:

Es ist also möglich, ein Wörterbuch zu serialisieren. Was es kompliziert macht, ist, dass der Wert dieses Diktos selbst ein Generikum ist, das eine Liste komplexer Datenstrukturen vom Typ Account ist. Jedes Konto ist ziemlich einfach, es ist nur eine Reihe von Eigenschaften.

Ich verstehe, dass es das Ziel ist, dies zu versuchen:

<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 Vererbung 

  • Benutzername (Zeichenfolge des Diktats)> 
  • Konto (jedes Konto in der Liste)> 
  • Kontodaten (dh Klasseneigenschaften). 

Dieses Layout aus einem Dictionary<Username, List<Account>> zu erhalten, ist das knifflige Bit und das Wesen dieser Frage.

Es gibt viele Antworten, wie man auf die Serialisierung reagiert, was meine Schuld ist, da ich es nicht früh klarer formuliert habe, aber jetzt suche ich nach einer definitiven Lösung.

64
George

Ich würde die Datei als JSON speichern. Da Sie ein Wörterbuch speichern, das nur aus einer Liste von Namen und Werten besteht, ist dies genau das, wofür Json entwickelt wurde.
Es gibt einige anständige, kostenlose .NET-Json-Bibliotheken - hier ist eine , aber Sie können eine vollständige Liste unter dem ersten Link finden.

22
zebrabox

Es hängt wirklich davon ab, was Sie speichern. Wenn Sie von strukturierten Daten sprechen, ist entweder XML oder ein sehr einfaches SQL-RDBMS wie SQLite oder SQL Server Compact Edition für Sie gut geeignet. Die SQL-Lösung wird besonders interessant, 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 offensichtlich weder eine Datenbank noch eine XML-Lösung geeignet. Bei Ihrer Frage kann ich jedoch davon ausgehen, dass es sich eher um die erstere als um die letztere handelt.

23
Adam Robinson

XML ist durch Serialisierung einfach zu verwenden. Verwenden Sie Isolierte Lagerung

Siehe auch Wie kann ich entscheiden, wo der Status eines Benutzers 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();
    }
}
14
Cheeso

All dies sind gute Antworten und lösen das Problem im Allgemeinen.

Wenn Sie eine einfache und kostenlose Möglichkeit benötigen, um Millionen von Daten zu skalieren, probieren Sie das ESENT Managed Interface-Projekt unter CodePlex aus.

ESENT ist eine eingebettete Datenbankspeicherungs-Engine (ISAM), die Teil von Windows ist. Es bietet zuverlässigen, transaktionsfähigen, gleichzeitigen und leistungsstarken Datenspeicher mit Sperren auf Zeilenebene, Write-Ahead-Protokollierung und Snapshot-Isolation. Dies ist ein verwalteter Wrapper für die ESENT Win32-API.

Es verfügt über ein PersistentDictionary-Objekt, das sehr einfach zu verwenden ist. Stellen Sie sich dies als ein Dictionary () - Objekt vor, das jedoch ohne zusätzlichen Code automatisch von der Festplatte geladen und gespeichert wird.

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's Frage zu beantworten:

Unterstützte Schlüsseltypen

Nur diese Typen werden als .__ unterstützt. Wörterbuchschlüssel:

Boolesches Byte Int16 UInt16 Int32 UInt32 Int64 UInt64 Float Double Guid DateTime TimeSpan-Zeichenfolge 

Unterstützte Werttypen

Wörterbuchwerte können beliebige der .__ sein. Schlüsseltypen, Nullable-Versionen von Schlüsseltypen, Uri, IP-Adresse oder eine serialisierbare Struktur. Eine Struktur ist nur als serialisierbar betrachtet, wenn es erfüllt alle diese Kriterien:

• Die Struktur wird als .__ markiert. serialisierbar • Jedes Mitglied der struct ist entweder: 1. Ein primitiver Datentyp (z. B. Int32) 2. Eine Zeichenfolge, ein URI oder eine IP-Adresse 3. Eine serialisierbare Struktur.

Oder anders ausgedrückt: eine serialisierbare Struktur kann nicht .__ enthalten. alle Verweise auf ein Klassenobjekt. Diese wird gemacht, um die API-Konsistenz zu erhalten . Hinzufügen eines Objekts zu einer PersistentDictionary erstellt eine Kopie von das Objekt durch Serialisierung . Das Ändern des ursprünglichen Objekts wird nicht Ändern Sie die Kopie, was zu .__ führen würde. verwirrendes Verhalten. Um diese zu vermeiden Probleme wird das PersistentDictionary Akzeptiere nur Werttypen als Werte.

Serialisierbar [Serialisierbar] struct Good { öffentliche DateTime? Empfangen; öffentliche Zeichenfolge Name; öffentlicher Dezimalpreis; öffentliche Uri-URL; } 

Kann nicht serialisiert werden [serialisierbar] struct bad { öffentliches Byte [] Daten; // Arrays werden nicht unterstützt öffentlicher Ausnahmefehler; // Referenzobjekt}

11
GalacticJello

Ich empfehle die XML Reader/Writer-Klasse für Dateien, da sie leicht serialisiert werden kann. 

Serialisierung in c #

Die Serialisierung (bekannt als Beizen in Python) ist eine einfache Möglichkeit, ein .__ zu konvertieren. Objekt zu einer binären Darstellung, dass kann dann z.B. auf die Festplatte geschrieben oder über einen Draht geschickt.

Es ist nützlich, z. zum einfachen Speichern von Einstellungen in einer Datei.

Sie können Ihre eigenen Klassen serialisieren, wenn Sie markieren sie mit [Serializable] Attribut. Dies serialisiert alle Mitglieder einer Klasse, mit Ausnahme der als [NonSerialized].

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 möglicherweise 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.

8
user195488

Eine vierte Option für die, die Sie erwähnen, sind Binärdateien . Das klingt zwar nicht einfach und schwierig, aber mit der Serialisierungs-API in .NET ist es wirklich einfach.

Unabhängig davon, ob Sie sich für Binär- oder XML-Dateien entscheiden, können Sie dieselbe Serialisierungs-API verwenden, obwohl Sie andere Serialisierer verwenden würden.

Um eine Klasse binär zu serialisieren, muss sie mit dem Attribut [Serializable] gekennzeichnet oder ISerializable implementiert werden.

Ähnliches kann mitXMLgemacht werden, obwohl die Schnittstelle dort IXmlSerializable heißt und die Attribute [XmlRoot] und andere Attribute im Namespace System.Xml.Serialization sind.

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

7
Mark Seemann

Wenn Ihre Sammlung zu groß wird, habe ich festgestellt, dass die XML-Serialisierung ziemlich langsam wird. Eine andere Option zur Serialisierung Ihres Wörterbuchs wäre "Rollen Sie Ihre eigenen" mit einem BinaryReader und BinaryWriter. 

Hier ist ein Beispielcode, um Ihnen den Einstieg zu erleichtern. Sie können diese generischen Erweiterungsmethoden so erstellen, dass sie mit jeder Art von Wörterbuch umgehen können, und es funktioniert ziemlich gut, ist jedoch zu ausführlich, um 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;
    }
}
6
GalacticJello

Die Codierung der Daten für mein aktuelles Projekt wurde gerade abgeschlossen. Hier sind meine 5 Cent.

Ich begann mit der binären Serialisierung. Es war langsam (ca. 30 Sekunden für das Laden von 100.000 Objekten) und es wurde auch eine ziemlich große Datei auf der Festplatte erstellt. Ich habe jedoch ein paar Zeilen Code für die Implementierung benötigt und meine Speicheranforderungen wurden vollständig abgedeckt. Um eine bessere Leistung zu erzielen, habe ich die benutzerdefinierte Serialisierung geändert. FastSerialization-Framework von Tim Haynes über das Code-Projekt gefunden. Tatsächlich ist es ein paar Mal schneller (12 Sekunden beim Laden, 8 Sekunden beim Speichern, 100.000 Datensätze) und benötigt weniger Festplattenspeicher. Das Framework basiert auf der von GalacticJello in einem früheren Beitrag beschriebenen Technik. 

Dann wechselte ich zu SQLite und konnte 2 mal 3-mal schnellere Leistung erzielen - 6 Sekunden für das Laden und 4 Sekunden für das Speichern, 100.000 Datensätze. Es umfasst das Analysieren von ADO.NET-Tabellen in Anwendungstypen. Es gab mir auch viel kleinere Dateien auf der Festplatte. In diesem Artikel wird erläutert, wie Sie ADO.NET optimal nutzen können: http://sqlite.phxsoftware.com/forums/t/134.aspx . Das Generieren von INSERT-Anweisungen ist eine sehr schlechte Idee. Sie können erraten, wie ich davon erfahren habe. :) Die Implementierung von SQLite hat mir ziemlich viel Zeit gekostet und die Zeit wurde durch fast jede Zeile des Codes sorgfältig gemessen.

5
ACH

Wenn Ihre Daten komplex sind, eine hohe Menge haben oder lokal abgefragt werden müssen, sind Objektdatenbanken möglicherweise eine gültige Option. Ich würde vorschlagen, auf Db4o oder Karvonite zu schauen.

4
Goran

Das erste, was ich mir anschauen würde, ist eine Datenbank. Die Serialisierung ist jedoch eine Option. Wenn Sie sich für die binäre Serialisierung entscheiden, würde ich vermeiden BinaryFormatter - es neigt dazu, sich zwischen den Versionen zu ärgern, wenn Sie Felder usw. ändern. Xml über XmlSerialzier wäre gut und kann nebeneinander kompatibel sein (dh mit den gleichen Klassendefinitionen) mit protobuf-net, wenn Sie die vertragsbasierte binäre Serialisierung ausprobieren möchten (wodurch Sie ohne Aufwand einen Flat-File-Serializer erhalten).

4
Marc Gravell

Viele Antworten in diesem Thread versuchen, die Lösung zu überdenken. Wenn ich korrekt 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 ohne XML-Aufwand schnell und einfach. 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.

3
James

Ich habe mehrere "Stand-alone" -Anwendungen mit lokalem Datenspeicher erstellt. Ich denke, das Beste 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 Datenzugriffsebene bleiben, die in anderen Projekten wiederverwendbar ist. Wenn die App jedoch auf einen größeren Maßstab skaliert werden muss wie ein voll ausgebildeter SQL-Server, müssen Sie nur die Verbindungszeichenfolge ändern.

2
HitLikeAHammer

Wenn Sie die binäre Serialisierungsroute wählen, berücksichtigen Sie die Geschwindigkeit, mit der auf ein bestimmtes Element des Datums zugegriffen werden muss. Wenn es sich nur um eine kleine Sammlung handelt, ist das Laden der gesamten Datei sinnvoll. Wenn sie groß ist, können Sie auch eine Indexdatei in Betracht ziehen. 

Nachverfolgen von Kontoeigenschaften/-feldern, die sich an einer bestimmten Adresse in der Datei befinden, können die Zugriffszeit beschleunigen, insbesondere wenn Sie die Indexdatei basierend auf der Schlüsselverwendung optimieren. (möglicherweise sogar, wenn Sie auf die Festplatte schreiben.)

0
Todd Richardson

Halten Sie es einfach: Wie Sie sagten, reicht eine flache Datei aus. Verwenden Sie eine flache Datei.

Dies setzt voraus, dass Sie Ihre Anforderungen richtig analysiert haben. Ich würde die Serialisierung als XML-Schritt überspringen, für ein einfaches Wörterbuch zu viel. Gleiches für eine Datenbank.

0
Larry Watanabe

Abhängig von der Kompelexität Ihres Account-Objekts würde ich entweder XML- oder Flat-Dateien empfehlen.

Wenn für jedes Konto nur ein paar Werte gespeichert werden sollen, können Sie sie 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 wird.

Der Speicherort für diese Datei ist die beste Wahl im AppData-Ordner in einem Unterordner für Ihr Programm. Dies ist ein Ort, an dem aktuelle Benutzer immer Schreibrechte haben können und der vom Betriebssystem selbst vor anderen Benutzern geschützt wird.

0
Pablo Venturino

Meine erste Neigung ist eine Zugangsdatenbank. Die .mdb-Dateien werden lokal gespeichert und können bei Bedarf verschlüsselt werden. Obwohl XML oder JSON für viele Szenarien funktionieren würde. Flache Dateien würde ich nur für schreibgeschützte, nicht durchsuchbare Informationen verwenden. Ich neige dazu, das csv-Format zu bevorzugen, um die Breite einzustellen.

0
Matthew Vines

Dies hängt von der Datenmenge ab, die Sie speichern möchten. In der Realität gibt es keinen Unterschied zwischen flachen Dateien und XML. XML wäre wahrscheinlich vorzuziehen, da es dem Dokument eine Struktur gibt. In der Praxis, 

Die letzte Option, und viele Anwendungen verwenden jetzt die Windows-Registrierung. Ich persönlich empfehle es nicht (Registry Bloat, Korruption, andere mögliche Probleme), aber es ist eine Option.

0
blacksol

Nach meiner Erfahrung reicht in den meisten Fällen JSON in einer Datei aus (meistens müssen Sie ein Array oder ein Objekt oder nur eine einzelne Zahl oder einen String speichern). Ich brauche selten SQLite (was mehr Zeit benötigt, um es einzurichten und zu benutzen, meistens ist es übertrieben).

0
JedatKinports

Ohne zu wissen, wie Ihre Daten aussehen, d. H. Die Komplexität, Größe usw. usw. XML ist einfach zu pflegen und leicht zugänglich. Ich würde KEINE Access-Datenbank verwenden, und flache Dateien sind auf lange Sicht schwieriger zu warten, insbesondere wenn Sie mehr als ein Datenfeld/Element in Ihrer Datei verwenden.

Ich beschäftige mich täglich mit großen Flat-File-Datenfeeds in guten Mengen, und selbst in einem extremen Beispiel sind Flat-File-Daten viel schwieriger zu verwalten 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 LINQ to XML auch als Option zum Abfragen der XML-Daten auschecken ...

HTH ...

0
Tom Miller