Files
LanMountainDesktop/LanMountainDesktop/Services/AppSettingsService.cs

155 lines
4.8 KiB
C#
Raw Normal View History

2026-03-04 15:22:52 +08:00
using System;
2026-02-28 03:00:25 +08:00
using System.IO;
using System.Text.Json;
2026-03-04 15:22:52 +08:00
using LanMountainDesktop.Models;
2026-02-28 03:00:25 +08:00
2026-03-04 15:22:52 +08:00
namespace LanMountainDesktop.Services;
2026-02-28 03:00:25 +08:00
public sealed class AppSettingsService
{
2026-03-08 14:00:13 +08:00
public static event Action<string>? SettingsSaved;
2026-02-28 03:00:25 +08:00
private static readonly JsonSerializerOptions SerializerOptions = new()
{
WriteIndented = true
};
2026-03-05 14:34:33 +08:00
private static readonly object CacheGate = new();
private static readonly TimeSpan CacheProbeInterval = TimeSpan.FromMilliseconds(400);
private static string? _cachedPath;
private static AppSettingsSnapshot? _cachedSnapshot;
private static DateTime _cachedWriteTimeUtc = DateTime.MinValue;
private static DateTime _lastProbeUtc = DateTime.MinValue;
2026-02-28 03:00:25 +08:00
private readonly string _settingsPath;
2026-03-08 14:00:13 +08:00
public string InstanceId { get; } = Guid.NewGuid().ToString("N");
2026-02-28 03:00:25 +08:00
public AppSettingsService()
{
var appData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
2026-03-04 15:22:52 +08:00
var settingsDirectory = Path.Combine(appData, "LanMountainDesktop");
2026-02-28 03:00:25 +08:00
_settingsPath = Path.Combine(settingsDirectory, "settings.json");
}
public AppSettingsSnapshot Load()
{
try
{
2026-03-05 14:34:33 +08:00
lock (CacheGate)
2026-02-28 03:00:25 +08:00
{
2026-03-05 14:34:33 +08:00
var nowUtc = DateTime.UtcNow;
if (TryGetCachedWithoutProbe(nowUtc, out var cached))
{
return cached;
}
2026-02-28 03:00:25 +08:00
2026-03-05 14:34:33 +08:00
var hasFile = File.Exists(_settingsPath);
var writeTimeUtc = hasFile
? File.GetLastWriteTimeUtc(_settingsPath)
: DateTime.MinValue;
_lastProbeUtc = nowUtc;
if (TryGetCachedAfterProbe(writeTimeUtc, out cached))
{
return cached;
}
var loadedSnapshot = hasFile
? LoadSnapshotFromDisk()
: new AppSettingsSnapshot();
UpdateCache(loadedSnapshot, writeTimeUtc, nowUtc);
return loadedSnapshot.Clone();
}
2026-02-28 03:00:25 +08:00
}
2026-03-10 21:25:47 +08:00
catch (Exception ex)
2026-02-28 03:00:25 +08:00
{
2026-03-10 21:25:47 +08:00
AppLogger.Warn("AppSettings", $"Failed to load settings from '{_settingsPath}'.", ex);
2026-02-28 03:00:25 +08:00
return new AppSettingsSnapshot();
}
}
public void Save(AppSettingsSnapshot snapshot)
{
2026-03-05 14:34:33 +08:00
var snapshotToPersist = snapshot?.Clone() ?? new AppSettingsSnapshot();
2026-02-28 03:00:25 +08:00
try
{
var directory = Path.GetDirectoryName(_settingsPath);
if (!string.IsNullOrWhiteSpace(directory))
{
Directory.CreateDirectory(directory);
}
2026-03-05 14:34:33 +08:00
var json = JsonSerializer.Serialize(snapshotToPersist, SerializerOptions);
2026-02-28 03:00:25 +08:00
File.WriteAllText(_settingsPath, json);
2026-03-05 14:34:33 +08:00
var writeTimeUtc = File.Exists(_settingsPath)
? File.GetLastWriteTimeUtc(_settingsPath)
: DateTime.UtcNow;
lock (CacheGate)
{
UpdateCache(snapshotToPersist, writeTimeUtc, DateTime.UtcNow);
}
2026-03-08 14:00:13 +08:00
SettingsSaved?.Invoke(InstanceId);
2026-02-28 03:00:25 +08:00
}
2026-03-10 21:25:47 +08:00
catch (Exception ex)
2026-02-28 03:00:25 +08:00
{
2026-03-10 21:25:47 +08:00
AppLogger.Warn("AppSettings", $"Failed to save settings to '{_settingsPath}'.", ex);
2026-02-28 03:00:25 +08:00
}
}
2026-03-05 14:34:33 +08:00
private bool TryGetCachedWithoutProbe(DateTime nowUtc, out AppSettingsSnapshot snapshot)
{
if (string.Equals(_cachedPath, _settingsPath, StringComparison.Ordinal) &&
_cachedSnapshot is not null &&
nowUtc - _lastProbeUtc < CacheProbeInterval)
{
snapshot = _cachedSnapshot.Clone();
return true;
}
snapshot = null!;
return false;
}
private bool TryGetCachedAfterProbe(DateTime writeTimeUtc, out AppSettingsSnapshot snapshot)
{
if (string.Equals(_cachedPath, _settingsPath, StringComparison.Ordinal) &&
_cachedSnapshot is not null &&
writeTimeUtc == _cachedWriteTimeUtc)
{
snapshot = _cachedSnapshot.Clone();
return true;
}
snapshot = null!;
return false;
}
private AppSettingsSnapshot LoadSnapshotFromDisk()
{
try
{
var json = File.ReadAllText(_settingsPath);
return JsonSerializer.Deserialize<AppSettingsSnapshot>(json, SerializerOptions) ?? new AppSettingsSnapshot();
}
2026-03-10 21:25:47 +08:00
catch (Exception ex)
2026-03-05 14:34:33 +08:00
{
2026-03-10 21:25:47 +08:00
AppLogger.Warn("AppSettings", $"Failed to deserialize settings from '{_settingsPath}'.", ex);
2026-03-05 14:34:33 +08:00
return new AppSettingsSnapshot();
}
}
private void UpdateCache(AppSettingsSnapshot snapshot, DateTime writeTimeUtc, DateTime probeTimeUtc)
{
_cachedPath = _settingsPath;
_cachedSnapshot = snapshot.Clone();
_cachedWriteTimeUtc = writeTimeUtc;
_lastProbeUtc = probeTimeUtc;
}
}