it-swarm.dev

Nejlepší způsob, jak ukládat data lokálně v síti .NET (C #)

Píšu aplikaci, která vezme uživatelská data a uloží ji lokálně pro pozdější použití. Aplikace bude spuštěna a zastavena poměrně často a rád bych ji uložil/načetl data na začátku/konci aplikace.

Bylo by to docela jednoduché, kdybych použil ploché soubory, protože data ve skutečnosti nemusí být zabezpečena (bude uložena pouze v tomto počítači). Myslím si, že možnosti jsou tedy:

  • Ploché soubory
  • XML
  • SQL DB

Ploché soubory vyžadují trochu více úsilí na udržení (žádné vestavěné ve třídách jako u XML), ale předtím jsem nepoužíval XML a SQL se zdá být pro tuto relativně snadnou úlohu jako přehnané.

Existují další cesty, které stojí za to prozkoumat? Pokud ne, které z nich je nejlepším řešením?


Upravit: Chcete-li k problému přidat trochu více dat, je to v podstatě jediná věc, kterou bych rád uložil, je slovník, který vypadá takto

Dictionary<string, List<Account>> 

kde je účet jiný vlastní typ.

Chtěl bych serializovat dikt jako xmlroot a pak typ účtu jako atributy?


Aktualizace 2:

Je tedy možné serializovat slovník. Komplikované je to, že hodnota tohoto diktátu je sama o sobě obecná, což je seznam složitých datových struktur typu Účet. Každý účet je poměrně jednoduchý, je to jen spousta vlastností.

Mám za to, že cílem je pokusit se a skončit s tímto:

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

Jak vidíte, heirachy je 

  • Uživatelské jméno (řetězec dict)> 
  • Účet (každý účet v seznamu)> 
  • Údaje o účtu (tj. Vlastnosti třídy). 

Získání tohoto rozložení z Dictionary<Username, List<Account>> je složitý kousek a podstata této otázky.

Je zde spousta odpovědí na serializaci, což je moje chyba, protože jsem to nezjasnila, ale teď hledám jednoznačné řešení.

64
George

Soubor bych uložil jako JSON . Vzhledem k tomu, že ukládáte slovník, který je pouze seznamem jmen/hodnot, pak je to do značné míry to, pro co byl JON určen.
Existuje dost slušných, bezplatných knihoven .NET json - zde je jeden , ale na prvním odkazu najdete úplný seznam.

22
zebrabox

Záleží na tom, co ukládáte. Pokud mluvíte o strukturovaných datech, pak buď XML, nebo velmi lehký SQL RDBMS, jako je SQLite nebo SQL Server Compact Edition, vám bude dobře fungovat. Řešení SQL je obzvláště přesvědčivé, pokud se data přesouvají mimo triviální velikost.

Pokud ukládáte velké kusy relativně nestrukturovaných dat (například binární objekty, například obrázky), pak samozřejmě není vhodná ani databáze ani řešení XML, ale vzhledem k vaší otázce odhaduji, že je to více než ta předchozí.

23
Adam Robinson

XML je snadno použitelný, přes serializaci. Použití Izolované úložiště

Viz také Jak se rozhodnout, kam uložit stav pro uživatele? Registr? Data aplikací? Samostatné skladování?

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

Všechny výše uvedené jsou dobré odpovědi a problém obecně řeší.

Pokud potřebujete snadný a volný způsob, jak škálovat na miliony dat, vyzkoušejte projekt ESENT Managed Interface na CodePlex .

ESENT je modul pro ukládání dat (ISAM), který je součástí systému Windows. Poskytuje spolehlivé, souběžně prováděné, vysoce výkonné datové úložiště s uzamykáním na úrovni řádků, zapisováním před zapisováním a izolací snímků. Toto je spravovaný wrapper pro rozhraní API ESENT Win32.

Má objekt PersistentDictionary, který je poměrně snadno použitelný. Považujte to za objekt Dictionary (), ale je automaticky načten z disku a uložen na disk bez dodatečného kódu.

Například:

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

Odpovědět na Georgeovu otázku:

Podporované typy klíčů

Pouze tyto typy jsou podporovány jako klíčová slova :

Boolean bajt Int16 UInt16 Int32 UInt32 Int64 UInt64 Float DateTime Double Guid DateSim TimeSpan String 

Podporované typy hodnot

Hodnoty slovníku mohou být libovolné z typů klíčů , Nulovatelných verzí typů klíčů , Uri, IPAddress nebo serializovatelné struktury . Struktura je Považována za serializovatelnou pouze tehdy, když Splňuje všechna tato kritéria:

• Struktura je označena jako Serializovatelná • Každý člen struktury Je buď: 1. Primitivní datový typ (např. Int32) 2. A String , Uri nebo IP adresa 3. Serializovatelná struktura.

Jinak řečeno, serializovatelná struktura Nemůže obsahovat Žádné odkazy na objekt třídy. Přidání objektu do PersistentDictionary vytvoří kopii Objektu, i když se jedná o serializaci. původní objekt nebude upravovat kopii, což by vedlo k matoucímu chování. Aby se předešlo těmto problémům , PersistentDictionary bude přijímat pouze hodnoty jako hodnoty.

Může být serializováno [Serializable] struct Good { Public DateTime? Public string Jméno, Public Desítková cena, Public Uri Url; } 

Nelze serializovat [Serializable] struct Bad { Public byte [] Data; // arrays nejsou podporovány veřejnou chybou výjimek; // referenční objekt}

11
GalacticJello

Doporučuji třídu XML pro čtení/zápis pro soubory, protože je snadno serializován. 

Serializace v C #

Serializace (známá jako moření v Python) je snadný způsob, jak převést Objekt na binární reprezentaci, která může být. zapisován na disk nebo odeslaný přes drát.

Je to užitečné, např. pro snadné uložení nastavení do souboru.

Své vlastní třídy můžete serializovat, pokud je označíte znakem [Serializable] . Toto serializuje všechny členy Třídy kromě těch, které jsou označeny jako [NonSerialized].

Následující kód vám ukáže, jak to provést:

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

Pak můžete v ConfigForm zavolat třídu ConfigManager a serializovat!

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

Poté, co byl serializován, pak můžete zavolat parametry konfiguračního souboru pomocí cm.WindowTitle, atd.

8
user195488

Čtvrtou možností pro ty, které zmiňujete, jsou binární soubory . Ačkoli to zní tajemně a obtížně, je to opravdu snadné s serializační API v .NET.

Ať už zvolíte binární nebo XML soubory, můžete použít stejné serializační API, i když byste použili různé serializátory.

Chcete-li binární serializovat třídy, musí být označena atributem [Serializable] nebo implementovat ISerializable.

Můžete udělat něco podobného sXML, ačkoli rozhraní se nazývá IXmlSerializable a atributy jsou [XmlRoot] a další atributy v oboru názvů System.Xml.Serialization.

Pokud chcete použít relační databázi, SQL Server Compact Edition je zdarma a velmi lehký a založený na jediném souboru.

7
Mark Seemann

Pokud se vaše sbírka dostane příliš velká, zjistila jsem, že Xml serializace je poměrně pomalá. Další možností serializovat váš slovník by být "roll vlastní" pomocí BinaryReader a BinaryWriter. 

Zde je několik ukázkových kódů, jen aby vás mohli začít. Můžete použít tyto obecné metody rozšíření pro zpracování jakéhokoli typu slovníku, a funguje to docela dobře, ale je příliš podrobný, aby sem mohl psát.

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

Právě dokončené ukládání dat kódování pro můj současný projekt. Zde je 5 centů.

Začal jsem binární serializací. Bylo to pomalé (cca 30 sekund na zatížení 100 000 objektů) a na disku také vznikl docela velký soubor. Nicméně, trvalo mi několik řádků kódu, abych provedl, a já jsem dostal všechny své potřeby skladování. Pro dosažení lepšího výkonu jsem se přesunul na vlastní serializaci. Nalezen FastSerialization rámec Tim Haynes na Code Project. Opravdu je to několikrát rychlejší (12 vteřin pro zatížení, 8 s pro uložení, 100K záznamů) a trvá méně místa na disku. Rámec je postaven na technice nastíněné GalacticJello v předchozím příspěvku. 

Pak jsem se přestěhoval do SQLite a byl jsem schopen dostat 2 někdy 3 krát rychlejší výkon - 6 s pro zatížení a 4 sec pro uložení, 100K záznamů. Zahrnuje zpracování tabulek ADO.NET na typy aplikací. Také mi dal mnohem menší soubor na disku. Tento článek vysvětluje, jak získat nejlepší výkon z ADO.NET: http://sqlite.phxsoftware.com/forums/t/134.aspx . Generování příkazů INSERT je velmi špatný nápad. Můžete hádat, jak jsem se o tom dozvěděl. :) Vskutku, SQLite implementace mi trvalo dost času a opatrné měření času s téměř každý řádek kódu.

5
ACH

Pokud jsou vaše data složitá, jejich množství je vysoké nebo je nutné dotazovat lokálně, databáze objektů mohou být platnou volbou. Navrhoval bych se podívat na Db4o nebo Karvonite .

4
Goran

První věc, na kterou bych se podíval, je databáze. Volba je však serializace. Pokud jdete na binární serializaci, pak bych vyhnout BinaryFormatter - má tendenci se rozzlobit mezi verzemi, pokud změníte pole atd. Xml přes XmlSerialzier by bylo v pořádku, a může být kompatibilní vedle sebe (tj. se stejnými definicemi tříd) s protobuf-netem, pokud chcete vyzkoušet binární serializaci založenou na kontraktech (což vám bez námahy dává serializátor plochých souborů).

4
Marc Gravell

Mnoho odpovědí v tomto vlákně se pokouší překonat řešení. Pokud jsem správný, chcete pouze uložit uživatelská nastavení.

Použijte k tomu soubor INI nebo App.Config.

Pokud se mýlím a ukládáte data, která jsou více než jen nastavení, použijte plochý textový soubor ve formátu csv. Ty jsou rychlé a snadné bez režie XML. Lidé rádi poo poo tyto, protože nejsou tak elegantní, ne měřítko pěkně a nevypadají tak dobře na životopis, ale to by mohlo být nejlepší řešení pro vás v závislosti na tom, co potřebujete.

3
James

Udělal jsem několik "samostatných" aplikací, které mají místní úložiště dat. Myslím, že nejlepší by bylo použití SQL Server Compact Edition (dříve známé jako SQLAnywhere).

Je to lehký a volný. Navíc se můžete držet psaní vrstvy pro přístup k datům, která je opakovaně použitelná v jiných projektech, a pokud aplikace někdy potřebuje škálovat na něco většího, jako je například plně nafouknutý server SQL, stačí změnit řetězec připojení.

2
HitLikeAHammer

Pokud jdete binární serializační trasou, vezměte v úvahu rychlost, se kterou je třeba přistupovat ke konkrétnímu členovi počátku. Pokud se jedná pouze o malou kolekci, má smysl načíst celý soubor, ale pokud bude velký, můžete zvážit také soubor indexu. 

Vlastnosti/pole sledování účtu, které jsou umístěny na konkrétní adrese v rámci souboru, vám mohou pomoci urychlit čas přístupu, zejména pokud optimalizujete tento soubor indexu na základě použití klíče. (možná i při zápisu na disk.)

0
Todd Richardson

Udržujte to jednoduché - jak jste řekl, plochý soubor je dostačující. Použijte plochý soubor.

Předpokládá se, že jste správně analyzovali své požadavky. Já bych přeskočil serializovat jako XML krok, overkill pro jednoduchý slovník. Totéž platí pro databázi.

0
Larry Watanabe

V závislosti na compelexity vašeho účtu bych doporučil XML nebo Flat soubor.

Pokud pro každý účet existuje pouze několik hodnot, můžete je uložit do souboru vlastností, jako je tento:

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

... a tak dále. Čtení ze souboru vlastností by mělo být snadné, protože mapuje přímo do řetězcového řetězce.

Pokud jde o umístění tohoto souboru, nejlepší volbou by bylo uložení do složky AppData uvnitř podsložky pro váš program. Toto je místo, kde budou mít aktuální uživatelé vždy přístup k zápisu, a to je udržováno v bezpečí před ostatními uživateli samotným OS.

0
Pablo Venturino

Moje první sklon je přístupová databáze. Soubory .mdb jsou uloženy lokálně a mohou být šifrovány, pokud je to považováno za nezbytné. Ačkoli XML nebo JSON by také fungovaly pro mnoho scénářů. Ploché soubory Používám pouze pro čtení, pouze pro vyhledávání (pouze pro čtení). Mám sklon dávat přednost formátu csv pro nastavení šířky.

0
Matthew Vines

Záleží na množství dat, která chcete uložit. Ve skutečnosti neexistuje žádný rozdíl mezi plochými soubory a XML. XML by pravděpodobně byl vhodnější, protože poskytuje strukturu dokumentu. V praxi, 

Poslední možností, a mnoho aplikací nyní používají je Windows registru. Já osobně nedoporučuji (Registry Bloat, Korupce, další potenciální problémy), ale je to možnost.

0
blacksol

Podle mých zkušeností ve většině případů je JSON v souboru dostačující (většinou potřebujete uložit pole nebo objekt nebo jen jedno číslo nebo řetězec). Zřídka potřebuji SQLite (který potřebuje více času na jeho nastavení a používání, většinou je to přehnané).

0
JedatKinports

Bez znalosti toho, jak vypadá vaše data, tj. Složitost, velikost atd. XML je snadno udržovatelný a snadno dostupný. Já bych NEPOUŽÍVAT databázi aplikace Access a ploché soubory je obtížnější udržet po dlouhou trať, zejména pokud máte ve svém souboru více než jedno datové pole/prvek.

Zabývám se velkými plochými datovými zdroji dat v dobrých množstvích denně, ai když je extrémní příklad, data z plochých souborů jsou mnohem obtížnější než data XML, která zpracovávám.

Jednoduchý příklad načítání dat XML do datové sady pomocí C #:

DataSet reportData = new DataSet();

reportData.ReadXml(fi.FullName);

Můžete také zkontrolovat LINQ na XML jako možnost dotazování dat XML ...

HTH ...

0
Tom Miller