Domanda

Sto scrivendo un'applicazione che prende i dati dell'utente e lo memorizza in locale per un uso successivo. verrà avviata l'applicazione e si fermò abbastanza spesso, e mi piacerebbe farlo salvare / caricare i dati sullo start application / fine.

Sarebbe abbastanza semplice se ho usato i file flat, non come il dato fa davvero bisogno di essere protetto (sarà memorizzata solo sul PC). Le opzioni di credo sono quindi:

  • File flat
  • XML
  • SQL DB

Lime piatte richiedono un po 'più di sforzo per mantenere (non costruito in classi come con XML), ma non ho usato XML prima, e SQL sembra eccessivo per questo compito relativamente facile.

Ci sono altre strade da esplorare? In caso contrario, quale di queste è la soluzione migliore?


Modifica: Per aggiungere un po 'di più dati per il problema, in fondo l'unica cosa che mi piacerebbe negozio è un dizionario che assomiglia a questo

Dictionary<string, List<Account>> 

dove account è un altro tipo personalizzato.

Sarei serializzare il dict come xmlroot, e quindi il tipo di account come attributi?


Aggiornamento 2:

Quindi è possibile serializzare un dizionario. Ciò che lo rende complicato è che il valore di questo dict è un sé generica, che è una lista di complesse strutture di dati di tipo di account. Ogni account è abbastanza semplice, è solo un mucchio di proprietà.

E 'la mia comprensione che l'obiettivo è quello di cercare di finire con questo:

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

Come si può vedere il heirachy è

  • Nome utente (stringa di dict)>
  • Account (ogni account nella lista)>
  • account di dati (ad esempio le proprietà di classe).

Come ottenere questo layout da un Dictionary<Username, List<Account>> è il po 'complicato, e l'essenza di questa domanda.

Ci sono un sacco di 'come' le risposte qui su serializzazione, che è colpa mia in quanto non ho fatto più chiaro nella fase iniziale, ma ora sto cercando una soluzione definitiva.

È stato utile?

Soluzione

Mi piacerebbe memorizzare il file come JSON . Dal momento che si sta memorizzando un dizionario che è solo un elenco coppia nome / valore, allora questo è più o meno quello che è stato progettato per JSON.
C'è un bel po 'decente, librerie JSON libero NET - ecco uno ma si Potete trovare un elenco completo sul primo link.

Altri suggerimenti

Dipende da quello che stai memorizzazione. Se stai parlando di dati strutturati, quindi XML o un molto leggero RDBMS SQL come SQLite o SQL Server Compact Edition funzionerà bene per voi. La soluzione SQL diventa particolarmente interessante se i dati si muove al di là di una dimensione di poco conto.

Se sei memorizzare grandi pezzi di dati non strutturati (relativamente oggetti binari come le immagini, per esempio), allora, ovviamente, né una soluzione di database né XML sono appropriati, ma data la tua domanda Sto indovinando che è più del primo rispetto al secondo .

XML è facile da usare, tramite la serializzazione. Utilizzare archiviazione isolata .

Come per decidere dove memorizzare lo stato per utente? Registry? AppData? 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();
    }
}

Tutto quanto sopra sono buone risposte, e generalmente risolve il problema.

Se avete bisogno di un modo gratuito e facile per scalare a milioni di pezzi di dati, provare l'ESENT Managed progetto di interfaccia su CodePlex .

  

ESENT è un motore di archiviazione di database integrato (ISAM), che fa parte di Windows. Fornisce affidabile, transazione, concorrente, memorizzazione dati ad alte prestazioni con blocco a livello di riga, write-ahead e isolamento istantanee. Questo è un wrapper gestito per l'API ESENT Win32.

Ha un oggetto PersistentDictionary che è abbastanza facile da usare. Pensate a come un oggetto Dictionary (), ma viene caricato automaticamente da e salvato su disco, senza codice aggiuntivo.

Ad esempio:

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

Per rispondere alla domanda di George:

  

Tipi chiave supportati

     

Solo questi tipi sono supportati come   chiavi del dizionario:

     

booleano Byte Int16 Int32 UInt16 UInt32   Int64 UInt64 Float   Doppia Guid DateTime TimeSpan Stringa

     

Tipi di valore supportato

     

I valori dizionario può essere uno qualsiasi dei   tipi di chiave, le versioni del Nullable   tipi di chiave, Uri, IPAddress o un   Struttura serializzabile. Una struttura è   solo considerato serializzabile se   soddisfa tutti questi criteri:

     

• La struttura è contrassegnato come   serializzabile • Ogni membro del   struct è uno:   1. Un'operazione primitiva (ad esempio Int32)   2. A String, Uri o IPAddress   3. Una struttura serializzabile.

     

O, per dirla in altro modo, un   Struttura serializzabile non può contenere   qualsiasi riferimento a un oggetto di classe. Questo   è fatto per preservare la coerenza API.   Aggiunta di un oggetto in un   PersistentDictionary crea una copia   l'oggetto però serializzazione.   Modifica l'oggetto originale non lo farà   modificare la copia, che porterebbe a   comportamento confusione. Per evitare quelli   problemi PersistentDictionary sarà   solo accettare tipi di valore come valori.

     

può essere serializzato [Serializable] struct Buono {       DateTime pubblico? ricevuto;       public string Name;       pubblica Decimale prezzo;       pubblica Uri URL; }

     

non può essere serializzato [Serializable] struct Bad {       public byte [] Dati; // array non sono supportati       Errore di eccezione pubblica; // oggetto di riferimento}

Vi consiglio di classe XML lettore / scrittore per i file perché è facilmente serializzato.

serializzazione in C #

  

La serializzazione (noto come decapaggio in   python) è un modo semplice per convertire un   oggetto di una rappresentazione binaria che   può quindi essere ad esempio scritti su disco o   inviato su un filo.

     

E 'per esempio utile per un facile salvataggio delle impostazioni in un file.

     

È possibile serializzare le vostre classi, se   li contrassegna con [Serializable]   attributo. Questo serializza tutti i membri   di una classe, ad eccezione di quelli contrassegnati come   [NonSerialized].

Di seguito viene riportato il codice per mostrare come fare questo:

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

A questo punto, in forse un ConfigForm, chiami la classe configmanager e serializzare esso!

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

Dopo che è stato serializzato, è possibile chiamare i parametri del vostro file di configurazione utilizzando cm.WindowTitle, ecc.

Una quarta opzione a quelle accennate sono file binari . Anche se questo suona arcana e difficile, è davvero facile con l'API serializzazione in .NET.

Se si sceglie file binari o XML, è possibile utilizzare la stessa API serializzazione, anche se si può usare diversi serializzatori.

Per serializzare binario una classe, va contrassegnata con l'attributo [Serializable] o implementare ISerializable.

Si può fare qualcosa di simile con XML , anche se ci si chiama l'interfaccia IXmlSerializable, e gli attributi sono [XmlRoot] e altri attributi nello spazio dei nomi System.Xml.Serialization.

Se si desidera utilizzare un database relazionale, SQL Server Compact Edition è gratuito e molto leggero e sulla base di un singolo file.

Se la vostra collezione diventa troppo grande, ho trovato che serializzazione XML diventa piuttosto lento. Un'altra opzione per serializzare il dizionario sarebbe "roll your own" utilizzando un BinaryReader e BinaryWriter.

Ecco alcuni esempi di codice solo per iniziare. È possibile effettuare queste metodi di estensione generici per gestire qualsiasi tipo di dizionario, e funziona abbastanza bene, ma è troppo prolisso per pubblicare qui.

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

Appena finito di codifica di archiviazione dati per il mio progetto in corso. Ecco il mio 5 centesimi.

Ho iniziato con la serializzazione binaria. E 'stato lento (circa 30 sec per il carico di 100.000 oggetti) e si stava creando una abbastanza grande file sul disco pure. Tuttavia, mi ci sono voluti un paio di righe di codice per implementare e ho avuto le mie tutte le esigenze di storage coperti. Per ottenere prestazioni migliori sono passato serializzazione personalizzata. quadro FastSerialization Trovato da Tim Haynes su Codice progetto. In effetti si tratta di un paio di volte più veloce (ha ottenuto 12 secondi per il carico, 8 secondi per salvare, 100K record) e richiede meno spazio su disco. Il quadro è costruito sulla tecnica descritta da GalacticJello in un post precedente.

Poi mi sono trasferito a SQLite ed è stato in grado di ottenere 2 volte 3 volte più veloce delle prestazioni - 6 secondi per il carico e 4 secondi per salvare, 100K record. Esso comprende tabelle ADO.NET analisi per tipi di applicazioni. Mi ha dato anche di file molto più piccolo sul disco. Questo articolo spiega come ottenere le migliori prestazioni di ADO.NET: http: //sqlite.phxsoftware .com / forum / t / 134.aspx . Generazione di istruzioni INSERT è una pessima idea. Si può immaginare come sono venuto a sapere di questo. :) In effetti, l'implementazione SQLite mi ha portato un po 'di tempo, più attenta misurazione del tempo prendendo da praticamente ogni riga del codice.

Se i dati sono complessi, ad alto contenuto di quantità o è necessario interrogare localmente quindi opporsi database potrebbe essere una valida opzione. Mi piacerebbe suggerire a guardare db4o o Karvonite .

La prima cosa che vorrei guardare è un database. Tuttavia, la serializzazione è un'opzione. Se si va per la serializzazione binaria, quindi vorrei evitare BinaryFormatter - ha la tendenza ad arrabbiarsi tra le versioni se si modificano i campi ecc Xml via XmlSerialzier andrebbe bene, e può essere side-by-side compatibile (cioè con le stesse definizioni di classe) con protobuf-net, se si vuole provare contratto a base di serializzazione binaria (dando un serializzatore di file flat, senza alcuno sforzo).

Un sacco di risposte in questo tentativo thread per overengineer la soluzione. Se non sbaglio, si desidera solo per memorizzare le impostazioni utente.

Utilizzare un file INI o di un file app.config per questo.

Se mi sbaglio, e si archiviano i dati che è più di un semplice impostazioni, utilizzare un file di testo in formato csv piatta. Questi sono facile e veloce senza il sovraccarico di XML. Gente come a poo poo questi dal momento che non sono così eleganti, non scala bene e non guardare come bene su un curriculum, ma potrebbe essere la soluzione migliore per voi a seconda di che cosa avete bisogno.

Ho fatto diversi "stand alone" le applicazioni che hanno un archivio dati locale. Credo che la cosa migliore da utilizzare sarebbe di SQL Server Compact Edition (precedentemente noto come SQLAnywhere).

E 'leggero e gratuito. Inoltre, si può attaccare a scrivere uno strato di accesso ai dati che è riutilizzabile in altri progetti più se l'applicazione dovesse aver bisogno di scalare a qualcosa di più grande come server completo SQL soffiato, avete solo bisogno di modificare la stringa di connessione.

Il mio primo istinto è un database di Access. I file con estensione mdb vengono memorizzati localmente, e possono essere crittografati qualora ciò sia ritenuto necessario. Anche se XML o JSON sarebbe anche lavorare per molti scenari. file flat avrei usato solo per sola lettura, non di ricerca (in avanti solo lettura) informazioni. Io tendo a preferire formato csv per impostare la larghezza.

Dipende dalla quantità di dati che si sta cercando di memorizzare. In realtà non c'è alcuna differenza tra file flat e XML. XML sarebbe probabilmente preferibile in quanto fornisce una struttura per il documento. In pratica,

L'ultima opzione, e un sacco di applicazioni utilizzano ora è il Registro di Windows. Non consiglio personalmente (Bloat Registro di sistema, la corruzione, altri potenziali problemi), ma è un'opzione.

Senza sapere che cosa i vostri dati sembra, vale a dire la complessità, dimensioni, ecc ... XML è facile da mantenere e facilmente accessibile. Non vorrei usare un database di Access e file piatti sono più difficili da mantenere nel lungo periodo, in particolare se avete a che fare con più di un campo di dati / elemento nel file.

Mi occupo di dati di grandi dimensioni file flat alimenta in buone quantità ogni giorno, e anche se un esempio estremo, dati flat-file è molto più difficile da gestire rispetto ai dati XML feed io processo.

Un esempio semplice di caricamento dei dati XML in un set di dati utilizzando C #:

DataSet reportData = new DataSet();

reportData.ReadXml(fi.FullName);

È anche possibile controllare LINQ to XML come opzione per interrogare i dati XML ...

HTH ...

Se si va il percorso serializzazione binaria, si consideri la velocità con cui deve essere accessibile un particolare membro del dato. Se è solo una piccola collezione, caricare l'intero file avrà un senso, ma se sarà grande, si potrebbe anche prendere in considerazione un file di indice.

Monitoraggio del conto Proprietà / campi che si trovano ad un indirizzo specifico all'interno del file può aiutare a velocizzare il tempo di accesso, soprattutto se si ottimizzare il file indice basato su utilizzo della chiave. (Forse anche quando si scrive su disco.)

A seconda della compelexity del vostro oggetto Account, lo consiglierei XML o file flat.

Se ci sono solo un paio di valori di memorizzare per ogni account, è possibile memorizzare su un file di proprietà, in questo modo:

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

... e così via. La lettura da un file di proprietà dovrebbe essere facile, in quanto le mappe direttamente a un dizionario stringa.

Per quanto riguarda dove memorizzare il file, la scelta migliore sarebbe quella di memorizzare nella cartella AppData, all'interno di una sottocartella per il programma. Questo è un luogo in cui gli utenti attuali avranno sempre accesso a scrivere, ed è tenuto al sicuro da altri utenti da parte del sistema operativo stesso.

Keep it simple - come hai detto, un file flat è sufficiente. Utilizzare un file flat.

Questo è supponendo che avete analizzato le vostre esigenze in modo corretto. Vorrei saltare la serializzazione come passo XML, eccessivo per un semplice dizionario. Stessa cosa per un database.

Nella mia esperienza nella maggior parte dei casi JSON in un file è sufficiente (per lo più è necessario memorizzare un array o un oggetto o solo un singolo numero o una stringa). Raramente ho bisogno di SQLite (che ha bisogno di più tempo per la sua creazione e l'utilizzo di esso, la maggior parte delle volte è eccessivo).

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top