Представьте себе такую структуру данных:
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 или даже текстовых файлов. Какое здесь лучшее решение?
Почему бы не использовать БД?
Новое изобретение колеса кажется неэффективным - есть встраиваемые БД, такие как SQLite, и более крупные БД, такие как SQL-сервер, которые решили все эти проблемы.
Проголосовали за вас обратно до 0, так как это хорошо написанный и исследованный вопрос, поэтому он не должен был быть отвергнут, однако, как говорили другие, используйте базу данных, поскольку это именно то, что вы описываете, и есть много вариантов. Если кто-то ковыряется в вашем JSON и редактирует что-то, что не будет проанализировано, вы столкнетесь с проблемами.





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