/
evsyukov.s
/
DiceGame
Обзор
Документация
Войти
/
evsyukov.s
/
DiceGame
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
main
src/DicePoker.Core/Data/Repositories/JsonFileGameRepository.cs
182 строки
8 KB
evsyukov.s
Исправлена обработка ошибок в репозитории
26 дек 2025, 18:38
26 дек 2025, 18:38
169efb5
Код
Авторство
О чём код?
using System.Text.Json; using System.Text.Json.Serialization; using DicePoker.Core.Data.Abstractions; using DicePoker.Core.Game; namespace DicePoker.Core.Data.Repositories { /// <summary> /// Реализация репозитория игры на основе JSON файлов. /// Сохраняет и загружает снимки состояния игры в JSON формате на файловую систему. /// Обеспечивает создание директорий, безопасные имена файлов и форматированный вывод JSON. /// </summary> public sealed class JsonFileGameRepository : IGameRepository { /// <summary>Базовая директория для хранения всех файлов сохранений</summary> private readonly string _basePath; /// <summary>Параметры сериализации JSON (форматированный вывод, игнорирование null значений)</summary> private readonly JsonSerializerOptions _jsonOptions; /// <summary> /// Конструктор, инициализирует репозиторий с указанной базовой директорией. /// Настраивает параметры JSON сериализации для читаемого и компактного формата. /// </summary> public JsonFileGameRepository(string basePath) { _basePath = basePath ?? throw new ArgumentNullException(nameof(basePath)); _jsonOptions = new JsonSerializerOptions { WriteIndented = true, // Форматируем JSON для удобного чтения DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull // Не сохраняем null свойства }; } /// <summary> /// Функция, сохраняет снимок состояния игры в JSON файл. /// Автоматически создаёт необходимые директории. /// </summary> public async Task SaveAsync(GameSnapshot snapshot, string key) { // Валидируем входные параметры if (snapshot == null) { throw new ArgumentNullException(nameof(snapshot)); } if (string.IsNullOrWhiteSpace(key)) { throw new ArgumentException("Key cannot be empty", nameof(key)); } // Получаем безопасный путь файла string filePath = GetFilePath(key); // Убеждаемся, что директория существует EnsureDirectoryExists(filePath); // Сериализуем объект в JSON и сохраняем в файл string json = JsonSerializer.Serialize(snapshot, _jsonOptions); await File.WriteAllTextAsync(filePath, json); } /// <summary> /// Функция, загружает снимок состояния игры из JSON файла. /// Возвращает null, если файл не найден или поврежден. /// </summary> public async Task<GameSnapshot?> LoadAsync(string key) { // Валидируем входной параметр if (string.IsNullOrWhiteSpace(key)) { throw new ArgumentException("Key cannot be empty", nameof(key)); } // Получаем безопасный путь файла string filePath = GetFilePath(key); // Если файл не существует, возвращаем null if (!File.Exists(filePath)) { return null; } try { // Читаем JSON файл и десериализуем в объект string json = await File.ReadAllTextAsync(filePath); return JsonSerializer.Deserialize<GameSnapshot>(json, _jsonOptions); } catch (JsonException) { // Если файл поврежден (невалидный JSON), возвращаем null // Это позволяет приложению продолжить работу при ошибках в файле return null; } catch (IOException) { // Если произошла ошибка чтения файла (занят другим процессом, нет доступа и т.д.) // возвращаем null - игра не может быть загружена return null; } catch (UnauthorizedAccessException) { // Если нет прав доступа к файлу, возвращаем null return null; } } /// <summary> /// Функция, проверяет, существует ли файл сохранения с указанным ключом. /// </summary> public Task<bool> ExistsAsync(string key) { // Валидируем входной параметр if (string.IsNullOrWhiteSpace(key)) { throw new ArgumentException("Key cannot be empty", nameof(key)); } // Получаем безопасный путь и проверяем существование файла string filePath = GetFilePath(key); return Task.FromResult(File.Exists(filePath)); } /// <summary> /// Функция, удаляет файл сохранения с указанным ключом. /// Если файл не существует, операция завершается без ошибок. /// </summary> public Task DeleteAsync(string key) { // Валидируем входной параметр if (string.IsNullOrWhiteSpace(key)) { throw new ArgumentException("Key cannot be empty", nameof(key)); } // Получаем безопасный путь и удаляем файл если он существует string filePath = GetFilePath(key); if (File.Exists(filePath)) { File.Delete(filePath); } return Task.CompletedTask; } /// <summary> /// Функция, преобразует произвольный ключ в безопасное имя файла. /// Удаляет недопустимые символы и добавляет расширение .json. /// </summary> private string GetFilePath(string key) { // Удаляем недопустимые для имен файлов символы string safeFileName = string.Concat(key.Split(Path.GetInvalidFileNameChars())); // Добавляем расширение .json если его еще нет if (!safeFileName.EndsWith(".json", StringComparison.OrdinalIgnoreCase)) { safeFileName += ".json"; } // Возвращаем полный путь в базовой директории return Path.Combine(_basePath, safeFileName); } /// <summary> /// Гарантирует, что директория для файла существует. /// Создает директорию если её еще нет. /// </summary> private void EnsureDirectoryExists(string filePath) { // Извлекаем директорию из полного пути string? directory = Path.GetDirectoryName(filePath); // Если директория не существует, создаем её (включая родительские директории) if (!string.IsNullOrEmpty(directory) && !Directory.Exists(directory)) { Directory.CreateDirectory(directory); } } } }