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

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

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

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

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

Действительно ли там какие-либо другие проспекты стоит исследовать? В противном случае, какой из них является лучшим решением?


Править: Для добавления немного большего количества данных к проблеме в основном единственной вещью, которую я хотел бы сохранить, является Словарь, который похож на это

Dictionary<string, List<Account>> 

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

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


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

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

Это - мое понимание, что цель здесь состоит в том, чтобы попытаться закончиться с этим:

<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)>
  • Учетная запись (каждая учетная запись в Списке)>
  • Данные учетной записи (т.е. свойства класса).

Получение этого расположения от a Dictionary<Username, List<Account>> хитрый бит и сущность этого вопроса.

Существует много из, 'как к' ответам здесь на сериализации, которая является моим отказом, так как я не сделал это более ясным вначале, но теперь я ищу определенное решение.

68
задан George 21 December 2009 в 20:32
поделиться

18 ответов

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

23
ответ дан 24 November 2019 в 14:17
поделиться

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

0
ответ дан 24 November 2019 в 14:17
поделиться

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

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

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

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

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

7
ответ дан 24 November 2019 в 14:17
поделиться

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

Если вы '

23
ответ дан 24 November 2019 в 14:17
поделиться

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();
    }
}
14
ответ дан 24 November 2019 в 14:17
поделиться

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

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

0
ответ дан 24 November 2019 в 14:17
поделиться

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

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

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, внутри подпапки для вашей программы. Это место, где текущие пользователи всегда будут иметь доступ для записи, и оно защищено от других пользователей самой ОС.

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

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

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

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

0
ответ дан 24 November 2019 в 14:17
поделиться

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

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

0
ответ дан 24 November 2019 в 14:17
поделиться

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

4
ответ дан 24 November 2019 в 14:17
поделиться

Если ваша коллекция становится слишком большой, я обнаружил, что сериализация 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;
    }
}
7
ответ дан 24 November 2019 в 14:17
поделиться

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

4
ответ дан 24 November 2019 в 14:17
поделиться

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

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

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

У нее есть объект 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();
    }

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

Поддерживаемые типы ключей

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

логический байт Int16 UInt16 Int32 UInt32 Int64 UInt64 с плавающей запятой Double Guid DateTime TimeSpan String

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

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

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

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

Can Be Serialized [Serializable] struct Good { общедоступное DateTime? Получили; публичная строка Имя; публичная десятичная цена; общедоступный Uri Url; }

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

11
ответ дан 24 November 2019 в 14:17
поделиться

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

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

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

3
ответ дан 24 November 2019 в 14:17
поделиться

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

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

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

DataSet reportData = new DataSet();

reportData.ReadXml(fi.FullName);

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

HTH...

.
0
ответ дан 24 November 2019 в 14:17
поделиться

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

Сериализация на C#

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

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

Вы можете сериализовать свои собственные классы, если вы отмечаете их с помощью [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, и т.д.

.
8
ответ дан 24 November 2019 в 14:17
поделиться

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

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

.
2
ответ дан 24 November 2019 в 14:17
поделиться

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

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

.
0
ответ дан 24 November 2019 в 14:17
поделиться

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

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

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

.
6
ответ дан 24 November 2019 в 14:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: