Melhor maneira de armazenar dados localmente no .net (c #)

Estou escrevendo um aplicativo que pega dados do usuário e os armazena localmente para uso posterior. O aplicativo será iniciado e interrompido com bastante frequência e eu gostaria de salvá-lo / carregar os dados no início / fim do aplicativo.

Seria bastante simples se eu usasse arquivos simples, pois os dados realmente não precisam ser protegidos (só serão armazenados neste PC). As opções que acredito são assim:

  • Arquivos simples
  • XML
  • SQL DB

Arquivos planos requerem um pouco mais de esforço para manter (sem classs embutidas como com XML), no entanto eu não usei XML antes, e SQL parece um exagero para essa tarefa relativamente fácil.

Existem outros caminhos que valham a pena explorar? Se não, qual destas é a melhor solução?


Edit: Para adicionar um pouco mais de dados para o problema, basicamente, a única coisa que eu gostaria de armazenar é um dictionary que se parece com isso

Dictionary<string, List> 

onde conta é outro tipo personalizado.

Eu seria serializar o dict como o xmlroot e, em seguida, o tipo de conta como atributos?


Atualização 2:

Então é possível serializar um dictionary. O que torna complicado é que o valor para esse comando seja um genérico em si, que é uma lista de estruturas de dados complexas do tipo Conta. Cada conta é bastante simples, é apenas um monte de propriedades.

É meu entendimento que o objective aqui é tentar acabar com isso:

   data1 data2     data1 data2   data1 data2   

Como você pode ver a heirachia é

  • Nome de usuário (string do dict)>
  • Conta (cada conta na lista)>
  • Dados da conta (ou seja, propriedades de class).

Obter este layout a partir de um Dictionary<Username, List> é a parte complicada e a essência desta questão.

Há muitas respostas ‘como’ aqui na serialização, o que é minha culpa, já que não deixei isso claro desde o início, mas agora estou procurando uma solução definitiva.

Eu armazenaria o arquivo como JSON . Como você está armazenando um dictionary que é apenas uma lista de par nome / valor, então é para isso que o json foi projetado.
Existem algumas bibliotecas .NET json decentes e gratuitas – aqui está uma, mas você pode encontrar uma lista completa no primeiro link.

Isso realmente depende do que você está armazenando. Se você estiver falando sobre dados estruturados, o XML ou um SQL RDBMS muito leve, como o SQLite ou o SQL Server Compact Edition, funcionará bem para você. A solução SQL torna-se especialmente atraente se os dados se moverem além de um tamanho trivial.

Se você está armazenando grandes pedaços de dados relativamente não estruturados (objects binários como imagens, por exemplo), então obviamente nem um database nem uma solução XML são apropriados, mas, dada sua pergunta, acredito que seja mais do primeiro que do último.

XML é fácil de usar, via serialização. Use armazenamento isolado .

Veja também Como decidir onde armazenar o estado por usuário? Registro? Dados do aplicativo? Armazenamento isolado?

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

Todas as respostas acima são boas e geralmente resolvem o problema.

Se você precisar de uma maneira fácil e gratuita de escalonar para milhões de dados, experimente o projeto da interface gerenciada do ESENT no CodePlex .

ESENT é um mecanismo de armazenamento de database (ISAM) que faz parte do Windows. Ele fornece armazenamento de dados confiável, transacionado, simultâneo e de alto desempenho com bloqueio em nível de linha, registro write-ahead e isolamento de instantâneo. Este é um wrapper gerenciado para a API do ESENT Win32.

Tem um object PersistentDictionary que é bastante fácil de usar. Pense nisso como um object Dictionary (), mas ele é automaticamente carregado e salvo em disco sem código extra.

Por exemplo:

 ///  /// Ask the user for their first name and see if we remember /// their last name. ///  public static void Main() { PersistentDictionary dictionary = new PersistentDictionary("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(); } 

Para responder a pergunta de George:

Tipos de chaves suportados

Somente esses tipos são suportados como chaves de dictionary:

Byte booleano Int16 UInt16 Int32 UInt32 Int64 UInt64 Flutuante duplo Guid DateTime TimeSpan String

Tipos de valor suportados

Os valores do dictionary podem ser qualquer um dos tipos de chave, versões com Nullable dos tipos de chave, Uri, endereço IP ou uma estrutura serializável. Uma estrutura só é considerada serializável se atender a todos esses critérios:

• A estrutura é marcada como serializável • Cada membro da estrutura é: 1. Um tipo de dados primitivo (por exemplo, Int32) 2. Um String, Uri ou IPAddress 3. Uma estrutura serializável.

Ou, em outras palavras, uma estrutura serializável não pode conter nenhuma referência a um object de class. Isso é feito para preservar a consistência da API. Adicionar um object a um PersistentDictionary cria uma cópia do object através da serialização. Modificar o object original não modificará a cópia, o que levaria a um comportamento confuso. Para evitar esses problemas, o PersistentDictionary aceitará apenas tipos de valor como valores.

Pode Ser Serializado [Serializable] struct Good {public DateTime? Recebido; public string Nome; Preço Decimal Público; Uri Url pública; }

Não pode ser serializado [Serializable] struct Bad {public byte [] Data; // arrays não são suportados public Exception Error; // object de referência}

Eu recomendo class de leitor / gravador de XML para arquivos porque é facilmente serializado.

Serialização em c #

A serialização (conhecida como pickling em python) é uma maneira fácil de converter um object em uma representação binária que pode ser, por exemplo, gravada em disco ou enviada por um fio.

É útil, por exemplo, para salvar facilmente as configurações em um arquivo.

Você pode serializar suas próprias classs se marcá-las com o atributo [Serializable] . Isso serializa todos os membros de uma class, exceto aqueles marcados como [NonSerialized] .

A seguir está o código para mostrar como fazer isso:

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

Você então, em talvez um ConfigForm, chame sua class ConfigManager e serialize-a!

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

Depois de serializado, você pode chamar os parâmetros do seu arquivo de configuração usando cm.WindowTitle, etc.

Uma quarta opção para aqueles que você menciona são arquivos binários . Embora isso pareça misterioso e difícil, é realmente fácil com a API de serialização no .NET.

Se você escolher arquivos binários ou XML, você pode usar a mesma API de serialização, embora você usaria serializadores diferentes.

Para serializar binariamente uma class, ela deve ser marcada com o atributo [Serializable] ou implementar ISerializable.

Você pode fazer algo semelhante com XML , embora a interface seja chamada IXmlSerializable e os atributos sejam [XmlRoot] e outros atributos no namespace System.Xml.Serialization.

Se você quiser usar um database relacional, o SQL Server Compact Edition é gratuito e muito leve e baseado em um único arquivo.

Acabei de codificar o armazenamento de dados para o meu projeto atual. Aqui estão meus 5 centavos.

Eu comecei com serialização binária. Foi lento (cerca de 30 segundos para carregar 100.000 objects) e estava criando um arquivo bem grande no disco também. No entanto, precisei de algumas linhas de código para implementar e consegui cobrir todas as minhas necessidades de armazenamento. Para obter melhor desempenho, mudei a serialização personalizada. Foi encontrado o framework FastSerialization por Tim Haynes no Code Project. Na verdade, é algumas vezes mais rápido (tem 12 segundos para carregar, 8 segundos para salvar, 100 mil registros) e ocupa menos espaço em disco. O framework é construído sobre a técnica delineada por GalacticJello em um post anterior.

Então mudei para o SQLite e consegui um desempenho duas vezes 3 vezes mais rápido – 6 segundos para carregar e 4 segundos para salvar, 100 mil registros. Inclui a análise de tabelas do ADO.NET para tipos de aplicativos. Também me deu um arquivo muito menor no disco. Este artigo explica como obter o melhor desempenho do ADO.NET: http://sqlite.phxsoftware.com/forums/t/134.aspx . Gerar instruções INSERT é uma péssima ideia. Você pode imaginar como eu vim a saber disso. 🙂 De fato, a implementação do SQLite me levou um pouco de tempo, além de uma medição cuidadosa do tempo em praticamente todas as linhas do código.

Se sua coleção ficar muito grande, descobri que a serialização Xml fica muito lenta. Outra opção para serializar seu dictionary seria “rolar seu próprio” usando um BinaryReader e BinaryWriter.

Aqui está um código de exemplo apenas para você começar. Você pode fazer esses methods genéricos de extensão para lidar com qualquer tipo de dictionary, e funciona muito bem, mas é muito detalhado para postar aqui.

 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> 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 accountList = new List(); for (int i = 0; i < accountCount; i++) { Account account = new Account(); account.Deserialize(br); accountList.Add(account); } accounts.Add(key, accountList); } } } static Dictionary> accounts = new Dictionary>(); static void Main(string[] args) { string accountName = "Bob"; List newAccounts = new List(); newAccounts.Add(AddAccount("A", 1)); newAccounts.Add(AddAccount("B", 2)); newAccounts.Add(AddAccount("C", 3)); accounts.Add(accountName, newAccounts); accountName = "Tom"; newAccounts = new List(); 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; } } 

Se os seus dados forem complexos, em grande quantidade ou se você precisar consultá-los localmente, os bancos de dados de objects poderão ser uma opção válida. Eu sugiro olhar para Db4o ou Karvonite .

A primeira coisa que eu olhei é um database. No entanto, a serialização é uma opção. Se você for para a serialização binária, então eu evitaria BinaryFormatter – ele tem uma tendência a ficar irritado entre as versões se você alterar os campos, etc. Xml via XmlSerialzier seria bom, e pode ser compatível lado-a-lado (ou seja, com a mesma class definições) com protobuf-net se você quiser tentar a serialização binária baseada em contrato (fornecendo um serializador de arquivo simples sem nenhum esforço).

Muitas das respostas neste tópico tentam overengineer a solução. Se eu estiver correto, você só quer armazenar as configurações do usuário.

Use um arquivo .ini ou arquivo App.Config para isso.

Se eu estiver errado e você estiver armazenando dados que são mais do que apenas configurações, use um arquivo de texto simples no formato csv. Estes são rápidos e fáceis sem a sobrecarga do XML. As pessoas gostam de poo poo estes, uma vez que eles não são tão elegantes, não escala bem e não parece tão bom em um currículo, mas pode ser a melhor solução para você, dependendo do que você precisa.

Eu fiz vários aplicativos “autônomos” que possuem um armazenamento de dados local. Acho que a melhor coisa a usar seria o SQL Server Compact Edition (anteriormente conhecido como SQLAnywhere).

É leve e grátis. Além disso, você pode escrever uma camada de access a dados que é reutilizável em outros projetos, e se o aplicativo precisar escalonar para algo maior, como servidor SQL completo, você só precisa alterar a string de conexão.

Minha primeira inclinação é um database de access. Os arquivos .mdb são armazenados localmente e podem ser criptografados se isso for considerado necessário. Embora XML ou JSON também funcionem para muitos cenários. Arquivos simples que eu usaria somente para informações somente leitura, sem pesquisa (somente leitura direta). Eu tendem a preferir o formato csv para definir a largura.

Depende da quantidade de dados que você deseja armazenar. Na realidade, não há diferença entre arquivos simples e XML. XML provavelmente seria preferível, pois fornece uma estrutura para o documento. Na prática,

A última opção, e muitos aplicativos usam agora, é o Registro do Windows. Eu pessoalmente não o recomendo (Registry Bloat, Corruption, outros possíveis problemas), mas é uma opção.

Sem saber como são seus dados, ou seja, a complexidade, tamanho, etc … XML é fácil de manter e facilmente acessível. Eu NÃO usaria um database do Access, e os arquivos simples são mais difíceis de manter a longo prazo, especialmente se você estiver lidando com mais de um campo / elemento de dados em seu arquivo.

Eu lido diariamente com grandes feeds de dados de arquivo simples em boas quantidades e, mesmo sendo um exemplo extremo, os dados de arquivo simples são muito mais difíceis de manter do que os feeds de dados XML que processo.

Um exemplo simples de carregar dados XML em um dataset usando C #:

 DataSet reportData = new DataSet(); reportData.ReadXml(fi.FullName); 

Você também pode verificar o LINQ para XML como uma opção para consultar os dados XML …

HTH …

Se você for a rota de serialização binária, considere a velocidade na qual um determinado membro do datum precisa ser acessado. Se for apenas uma coleção pequena, o carregamento de todo o arquivo fará sentido, mas, se for grande, você também poderá considerar um arquivo de índice.

As propriedades / campos da conta de rastreamento localizados em um endereço específico dentro do arquivo podem ajudá-lo a acelerar o tempo de access, especialmente se você otimizar esse arquivo de índice com base no uso das chaves. (possivelmente até mesmo quando você escreve no disco.)

Dependendo da compelexidade do seu object Account, recomendo XML ou Flat file.

Se houver apenas alguns valores para armazenar para cada conta, você poderá armazená-los em um arquivo de propriedades, como este:

 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 assim por diante. A leitura de um arquivo de propriedades deve ser fácil, pois é mapeada diretamente para um dictionary de strings.

Quanto a onde armazenar este arquivo, a melhor escolha seria armazenar na pasta AppData, dentro de uma subpasta para o seu programa. Este é um local onde os usuários atuais sempre terão access para escrever, e é mantido seguro de outros usuários pelo próprio SO.

Mantê-lo simples – como você disse, um arquivo simples é suficiente. Use um arquivo simples.

Isto supõe que você tenha analisado seus requisitos corretamente. Eu pularia a serialização como etapa XML, um exagero para um dictionary simples. Mesma coisa para um database.

Na minha experiência, na maioria dos casos, o JSON em um arquivo é suficiente (na maioria das vezes você precisa armazenar um array ou um object ou apenas um único número ou string). Eu raramente preciso de SQLite (que precisa de mais tempo para configurá-lo e usá-lo, na maioria das vezes é exagero).