Подтвердить что ты не робот

Лучший способ хранения данных локально в .NET(С#)

Я пишу приложение, которое берет данные пользователя и сохраняет его локально для использования позже. Приложение будет запущено и остановлено довольно часто, и я бы хотел, чтобы он сохранял/загружал данные в начало и конец приложения.

Было бы довольно просто, если бы я использовал плоские файлы, так как данные действительно не нужно защищать (он будет храниться только на этом ПК). Возможные варианты:

  • Плоские файлы
  • XML
  • SQL DB

Плоские файлы требуют немного больше усилий для поддержки (нет встроенных классов, таких как XML), однако я раньше не использовал XML, и SQL кажется излишним для этой относительно простой задачи.

Есть ли другие возможности, которые стоит изучить? Если нет, то какое из них является лучшим решением?


Изменить: Чтобы добавить немного больше данных в проблему, в основном единственное, что я хотел бы сохранить, - это словарь, который выглядит так:

Dictionary<string, List<Account>> 

где Учетная запись - другой пользовательский тип.

Я бы сериализовал dict как xmlroot, а затем тип учетной записи как атрибуты?


Обновление 2:

Таким образом, можно сериализовать словарь. Сложность заключается в том, что значение для этого dict является самим обобщением, который представляет собой список сложных структур данных типа Account. Каждая учетная запись довольно проста, это всего лишь куча свойств.

Я понимаю, что цель здесь состоит в том, чтобы попытаться в итоге:

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

Как вы видите, heirachy

  • Имя пользователя (строка dict) >
  • Аккаунт (каждая учетная запись в списке) >
  • Данные учетной записи (т.е. свойства класса).

Получение этого макета из Dictionary<Username, List<Account>> - это сложный бит и суть этого вопроса.

В сериализации есть много ответов "как", это моя ошибка, так как я не делал это более ясными на ранней стадии, но теперь я ищу определённое решение.

4b9b3361

Ответ 1

Я бы сохранил файл как JSON. Поскольку вы храните словарь, который является только списком пар имя/значение, тогда это в значительной степени предназначено для json. Там немало достойных бесплатных библиотек .NET json - здесь один, но вы можете найти полный список по первой ссылке.

Ответ 2

Это действительно зависит от того, что вы храните. Если вы говорите о структурированных данных, то XML или очень легкие SQL RDBMS, такие как SQLite или SQL Server Compact Edition, будут хорошо работать для вас. Решение SQL становится особенно убедительным, если данные перемещаются за тривиальный размер.

Если вы храните большие куски относительно неструктурированных данных (например, двоичные объекты, такие как изображения), то, очевидно, ни базы данных, ни XML-решения не подходят, но, учитывая ваш вопрос, я предполагаю, что он больше из первых, чем последний.

Ответ 3

XML прост в использовании, посредством сериализации. Используйте изолированное хранилище.

См. также Как решить, где хранить состояние для каждого пользователя? Реестр? Данные приложения? Изолированное хранилище?

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

Ответ 4

Все вышесказанное является хорошим ответом и обычно решает проблему.

Если вам нужен простой и бесплатный способ масштабирования для миллионов данных, попробуйте проект ESENT Managed Interface на CodePlex.

ESENT - это встроенный механизм хранения базы данных (ISAM), который является частью Windows. Он обеспечивает надежное, транзакционное, одновременное высокопроизводительное хранилище данных с блокировкой на уровне строк, протоколированием на основе записи и изоляцией моментальных снимков. Это управляемая оболочка API ESENT Win32.

У него есть объект PersistentDictionary, который довольно прост в использовании. Подумайте об этом как о объекте Dictionary(), но он автоматически загружается и сохраняется на диск без дополнительного кода.

Например:

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

Чтобы ответить на вопрос Джорджа:

Поддерживаемые типы клавиш

Только эти типы поддерживаются как словарные ключи:

Boolean Byte Int16 UInt16 Int32 UInt32 Int64 UInt64 Float Строка TimeSyn DoubleTime TimeTime

Поддерживаемые типы значений

Значения словаря могут быть любыми типы ключей, Nullable версии ключевые типы, Uri, IPAddress или сериализуемая структура. Структура считается только сериализуемым, если он отвечает всем этим критериям:

• Структура отмечена как сериализуемый • Каждый член struct: 1. Первоначальный тип данных (например, Int32) 2. Строка, Uri или IPAddress 3. Сериализуемая структура.

Или, иначе говоря, сериализуемая структура не может содержать любые ссылки на объект класса. Эта выполняется для сохранения согласованности API. Добавление объекта к PersistentDictionary создает копию объект, хотя сериализация. Изменение исходного объекта не будет изменить копию, которая привела бы к запутанное поведение. Чтобы избежать этих проблемы PersistentDictionary только принимать значения в качестве значений.

Может быть сериализовано [Serializable] struct Хорошее {     public DateTime? Получено;     public string Name;     общественная десятичная цена;     публичный Uri Url; }

Невозможно сериализоваться [Serializable] struct Плохо {     public byte [] Данные;//поддерживается массив arent     публичная ошибка исключения;//ссылочный объект}

Ответ 5

Я рекомендую класс чтения/записи XML для файлов, потому что он легко сериализуется.

Сериализация в С#

Сериализация (известная как травление в python) - это простой способ преобразования объект к двоичному представлению, что может быть, например, записанных на диск или отправляется по проводам.

Это полезно, например. для удобства сохранения настроек в файл.

Вы можете сериализовать свои собственные классы, если вы отмечаете их с помощью [Serializable]атрибут. Это сериализует всех участников класса, кроме отмеченных как [NonSerialized].

Ниже приведен код, показывающий, как это сделать:

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

Затем вы, возможно, в ConfigForm, вызываете свой класс ConfigManager и сериализуете его!

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

После того, как он был сериализован, вы можете вызвать параметры своего конфигурационного файла с помощью cm.WindowTitle и т.д.

Ответ 6

Четвертый вариант для тех, кого вы упоминаете, - это двоичные файлы. Хотя это звучит тайно и сложно, это очень легко с помощью API сериализации в .NET.

Если вы выберете двоичные или XML файлы, вы можете использовать один и тот же API сериализации, хотя вы бы использовали разные сериализаторы.

Чтобы двоичный сериализовать класс, он должен быть помечен атрибутом [Serializable] или реализовать ISerializable.

Вы можете сделать что-то подобное с XML, хотя интерфейс называется IXmlSerializable, а атрибуты - [XmlRoot] и другие атрибуты в пространстве имен System.Xml.Serialization.

Если вы хотите использовать реляционную базу данных, SQL Server Compact Edition является бесплатным и очень легким и основан на одном файле.

Ответ 7

Если ваша коллекция становится слишком большой, я обнаружил, что сериализация Xml происходит довольно медленно. Другой вариант сериализации вашего словаря будет "сворачивать свой собственный" с помощью BinaryReader и BinaryWriter.

Вот пример кода, чтобы вы начали. Вы можете сделать эти общие методы расширений для работы с любым типом словаря, и он работает достаточно хорошо, но слишком подробен для публикации здесь.

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

Ответ 8

Просто закончил хранение данных кодирования для моего текущего проекта. Вот мои 5 центов.

Я начал с двоичной сериализации. Он был медленным (около 30 секунд для загрузки 100 000 объектов), и он также создавал довольно большой файл на диске. Однако мне потребовалось несколько строк кода для реализации, и я получил все мои потребности в хранилищах. Чтобы повысить производительность, я перешел к пользовательской сериализации. Найденная система FastSerialization Тимом Хейнсом в проекте Code. Действительно, он в несколько раз быстрее (получил 12 секунд для загрузки, 8 секунд для сохранения, 100K записей), и он занимает меньше места на диске. Структура основана на технике, описанной GalacticJello в предыдущем посте.

Затем я перешел на SQLite и смог получить 2 раза в 3 раза быстрее - 6 секунд для загрузки и 4 с для сохранения, 100 тыс. записей. Он включает в себя анализ таблиц ADO.NET для типов приложений. Это также дало мне гораздо меньше файлов на диске. В этой статье объясняется, как получить лучшую производительность из ADO.NET: http://sqlite.phxsoftware.com/forums/t/134.aspx. Создание инструкций INSERT - очень плохая идея. Вы можете догадаться, как я узнал об этом.:) Действительно, реализация SQLite заняла у меня довольно много времени плюс тщательное измерение времени, которое занимает почти каждая строка кода.

Ответ 9

Если ваши данные сложны, имеют большое количество или вам нужно запрашивать их локально, то объектные базы данных могут быть допустимым вариантом. Я предлагаю посмотреть Db4o или Karvonite.

Ответ 10

Первое, на что я смотрю, - это база данных. Однако сериализация является опцией. Если вы идете на двоичную сериализацию, тогда я бы избегал BinaryFormatter - у него есть тенденция сердиться на версии, если вы меняете поля и т.д. Xml через XmlSerialzier будет в порядке и может быть боковым (например, с теми же определениями классов) с protobuf-net, если вы хотите попробовать двоичную сериализацию на основе контрактов (без каких-либо усилий).

Ответ 11

Многие ответы в этом потоке пытаются переоценить решение. Если я прав, вы просто хотите сохранить пользовательские настройки.

Для этого используйте файл .ini или файл App.Config.

Если я ошибаюсь, и вы сохраняете данные, которые больше, чем просто настройки, используйте плоский текстовый файл в формате csv. Это быстро и легко без накладных расходов на XML. Люди любят poo poo, потому что они не такие элегантные, не красиво красивы и не выглядят так хорошо на резюме, но это может быть лучшим решением для вас в зависимости от того, что вам нужно.

Ответ 12

Я сделал несколько "самостоятельных" приложений с локальным хранилищем данных. Я считаю, что лучше всего использовать SQL Server Compact Edition (ранее известный как SQLAnywhere).

Это легкий и бесплатный. Кроме того, вы можете придерживаться написания слоя доступа к данным, который можно использовать повторно в других проектах, плюс, если приложение когда-либо понадобится масштабировать до большего, чем полноразмерный SQL-сервер, вам нужно только изменить строку подключения.

Ответ 13

Мой первый наклон - это база данных доступа. Файлы .mdb хранятся локально и могут быть зашифрованы, если это считается необходимым. Хотя XML или JSON также будут работать во многих сценариях. Плоские файлы я бы использовал только для чтения, а не для поиска (только для чтения). Я предпочитаю формат csv для установки ширины.

Ответ 14

Это зависит от объема данных, которые вы хотите хранить. На самом деле нет никакой разницы между плоскими файлами и XML. XML, вероятно, будет предпочтительнее, поскольку он обеспечивает структуру документа. На практике

Последняя опция, и многие приложения теперь используют реестр Windows. Я лично не рекомендую (Registry Bloat, Corruption, другие потенциальные проблемы), но это вариант.

Ответ 15

Не зная, как выглядят ваши данные, т.е. сложность, размер и т.д. XML легко поддерживать и легко доступен. Я бы не использовал базу данных Access, а плоские файлы сложнее поддерживать в течение длительного времени, особенно если вы имеете дело с более чем одним полем данных/элементом в вашем файле.

Я ежедневно обрабатываю большие файлы данных с плоскими файлами в хороших количествах, и даже в крайнем примере данные с плоскими файлами намного сложнее в обслуживании, чем обрабатываемые XML файлы данных.

Простой пример загрузки XML-данных в набор данных с использованием С#:

DataSet reportData = new DataSet();

reportData.ReadXml(fi.FullName);

Вы также можете проверить LINQ to XML в качестве опции для запроса данных XML...

НТН...

Ответ 16

Если вы идете по бинарной сериализации, рассмотрите скорость, с которой должен быть доступен конкретный член базы данных. Если это только небольшая коллекция, загрузка всего файла будет иметь смысл, но если она будет большой, вы можете также рассмотреть файл индекса.

Свойства/поля отслеживания учетной записи, которые расположены по определенному адресу в файле, могут помочь вам ускорить время доступа, особенно если вы оптимизируете этот индексный файл на основе использования ключа. (возможно, даже когда вы пишете на диск.)

Ответ 17

В зависимости от сложности вашего объекта Account я бы рекомендовал либо XML, либо Flat файл.

Если для каждой учетной записи есть всего несколько значений, вы можете сохранить их в файле свойств, например:

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

... и так далее. Чтение из файла свойств должно быть простым, так как оно непосредственно сопоставляется со строковым словарем.

Что касается того, где хранить этот файл, лучшим выбором будет сохранение в папке AppData внутри подпапки для вашей программы. Это местоположение, в котором текущие пользователи всегда будут иметь доступ к записи, и он будет защищен от других пользователей самой ОС.

Ответ 18

Держите его простым - как вы сказали, плоского файла достаточно. Используйте плоский файл.

Предполагается, что вы правильно проанализировали свои требования. Я бы пропустил сериализацию как шаг XML, переполнил простой словарь. То же самое для базы данных.

Ответ 19

По моему опыту в большинстве случаев достаточно JSON в файле (в основном вам нужно хранить массив или объект или только один номер или строку). Мне редко нужен SQLite (для этого требуется больше времени для его настройки и использования, большую часть времени он переполняет).