Структура данных на основе файловой системы?

Представьте себе такую ​​структуру данных:

public class Cat
{
    public string Name;
    public string FavoriteFood;
    public List<Memory> Memories;
}

public class Memory
{
    public string Name;
    public DateTime Date;
    public List<string> Thoughts;
}

Иногда у Cat будет много Memories, у каждого много мыслей. Это может занять очень много места, поэтому хранить его в памяти - не лучшая идея. Как лучше всего сохранить эти данные в файлах и папках?

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

\---Cats
    +---Charles
    |   |   cat.json
    |   |
    |   \---Memories
    |       |   eating_food.json
    |       |   sleeping.json
    |       |   biting_some_dude.json
    |
    \---Brumpbo
        |   cat.json
        |
        \---Memories
            |   sleeping.json
            |   sleeping_again.json

Файлы cat.json могут выглядеть примерно так:

{
    "name": "Charles",
    "favorite_food": "pant",
    "memories": [
        "eating_food",
        "sleeping",
        "biting_some_dude"
    ]
}

Файлы памяти могут выглядеть примерно так (обратите внимание, что thoughts может быть очень длинным):

{
    "name": "eating_food",
    "date": "2009-01-20T12:00:00.000Z",
    "thoughts": [
        "God, I love pant.",
        "This is some great pant.",
        // ...
        "I am never going to eat ever again.",
        "This was a mistake."
    ]
}

Моей первой попыткой реализовать это было использование IDisposable для сериализации.

public class Cat : IDisposable
{
    public string Name;
    public string FavoriteFood;
    public List<string> Memories;

    // Load a cat if it already exists, or create a new one.
    public Cat(string name)
    {
        if (Storage.DirectoryExists(name))
        {
            var info = Storage.ReadFile<CatInfo>($"{name}/cat.json");
            this.Name = info.Name;
            this.FavoriteFood = info.FavoriteFood;
            this.Memories = info.Memories;
        }
        else
        {
            this.Memories = new List<string>();
        }
    }

    public Memory GetMemory(string name)
    {
        if (this.Memories.Contains(name))
        {
            return new Memory(this, name);
        }
        return null;
    }

    // Serialize and store the cat.
    public void Dispose()
    {
        var info = new CatInfo
        {
            Name = this.Name,
            FavoriteFood = this.FavoriteFood,
            Memories = this.Memories
        };
        Storage.WriteFile("${this.Name}/cat.json", info);
    }
}

public Memory : IDisposable
{
    private readonly Cat cat;

    public string Name;
    public DateTime Date;
    public List<string> Thoughts;

    public Memory(Cat cat, string name)
    {
        if (Storage.FileExists($"{cat.Name}/Memories/{name}.json"))
        {
            var info = Storage.ReadFile<MemoryInfo>($"{cat.Name}/Memories/{name}.json");
            this.Name = info.Name;
            this.Date = info.Date;
            this.Thoughts = info.Thoughts;
        }
        else
        {
            this.Thoughts = new List<string>();
        }
    }

    public void Dispose()
    {
        var info = new MemoryInfo
        {
            Name = this.Name,
            Date = this.Date,
            Thoughts = this.Thoughts
        };
        Storage.WriteFile($"{this.cat.Name}/Memories/{this.Name}.json", info);
    }
}

Каким бы ужасным это ни было, он работает достаточно хорошо, пока не возникнет одна проблема: безопасность потоков. Представьте себе: Чарльз Cat обнаруживает, что он любит есть «хлеб» больше, чем «трусики». Теперь это требует двух изменений; один в поле Cat.FavoriteFood и дополнение к Cat.Memories. Однако эти два изменения, скорее всего, обрабатываются двумя отдельными процессами в приложении. Это может привести к потере данных.

Thread 1: Charles is loaded to update FavoriteFood.
Thread 2: Charles is loaded to update Memories.
Thread 1: Charles's FavoriteFood is updated to "bread."
Thread 2: Charles's Memories is updated to include "eating_bread."
Thread 1: Charles's data is serialized and written. 
Thread 2: Charles's data is serialized and written.

Поскольку поток 2 был загружен до того, как поток 1 сериализовал любимую еду Чарльза и был написан после этого, обновление для FavoriteFood полностью потеряно.

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

Чтобы быть ясным, цель здесь - потокобезопасный метод хранения данных на диске в доступной для человека форме; это не обязательно означает использование JSON или даже текстовых файлов. Какое здесь лучшее решение?

Вы должны увидеть, как разместить этот вопрос на Программная инженерия.

Mark Benningfield 12.03.2018 17:24

Почему бы не использовать БД?

juharr 12.03.2018 17:24

Новое изобретение колеса кажется неэффективным - есть встраиваемые БД, такие как SQLite, и более крупные БД, такие как SQL-сервер, которые решили все эти проблемы.

Charleh 12.03.2018 17:28

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

Matt 12.03.2018 17:34
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
85
1

Ответы 1

Один из шаблонов, который, я думаю, подошел бы тому, что вы делаете хорошо, - это использовать Шаблон репозитория с UnitOfWork, с этим вы могли бы облегчить вашу проблему синхронных данных. Дополнение его Entity Framework и резервное копирование с помощью базы данных позволило бы создать идеально масштабируемое решение для ваших нужд, а также избавить само приложение от многих задач I/O.

Другие вопросы по теме