Files
Store.Management/Source/Store.Management.Framework/Services/SettingsServiceBase.cs
T
2026-10-27 14:35:03 +08:00

100 lines
2.9 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using System.IO;
using Newtonsoft.Json;
using Prism.Mvvm;
using Store.Management.Framework.Configs;
namespace Store.Management.Framework.Services;
public interface ISettingsServiceBase<T> where T : class
{
T Model { get; }
string FilePath { get; }
T GetDefaultModel();
void LoadSettings();
Task LoadSettingsAsync();
void SaveSettings();
Task SaveSettingsAsync();
}
/// <summary>
/// JSON 配置服务。缺失文件保留当前配置,加载失败保留旧模型并抛出异常。
/// 保存通过同目录临时文件替换目标;模型变更应在所属 UI 线程调用。
/// </summary>
public abstract class JsonSettingsServiceBase<T> : BindableBase, ISettingsServiceBase<T> where T : class
{
private readonly SemaphoreSlim _gate = new(1, 1);
private T? _model;
/// <summary>默认按模型类型名保存到用户配置目录,派生类可覆盖文件名或完整路径。</summary>
public virtual string FilePath => FolderConfig.GetSettingsFilePath(typeof(T).Name + ".json");
public abstract T GetDefaultModel();
public T Model
{
get => _model ??= GetDefaultModel() ?? throw new InvalidOperationException("默认配置不能为空。");
protected set
{
ArgumentNullException.ThrowIfNull(value);
SetProperty(ref _model, value);
}
}
public void LoadSettings()
{
T? loaded = LoadModelAsync().GetAwaiter().GetResult();
if (loaded is not null) Model = loaded;
}
public void SaveSettings() => SaveSettingsAsync().GetAwaiter().GetResult();
public async Task LoadSettingsAsync()
{
T? loaded = await LoadModelAsync();
if (loaded is not null) Model = loaded;
}
private async Task<T?> LoadModelAsync()
{
T? loaded = null;
await _gate.WaitAsync().ConfigureAwait(false);
try
{
string path = GetFullPath();
try
{
string json = await File.ReadAllTextAsync(path).ConfigureAwait(false);
loaded = JsonConvert.DeserializeObject<T>(json)
?? throw new JsonSerializationException($"配置文件不能为 null:{path}");
}
catch (FileNotFoundException) { }
catch (DirectoryNotFoundException) { }
}
finally { _gate.Release(); }
return loaded;
}
public async Task SaveSettingsAsync()
{
// 在首次等待前读取模型,避免在后台线程初始化默认模型。
string json = JsonConvert.SerializeObject(Model, Formatting.Indented);
await _gate.WaitAsync().ConfigureAwait(false);
string? temporaryPath = null;
try
{
string path = GetFullPath();
Directory.CreateDirectory(Path.GetDirectoryName(path)!);
temporaryPath = path + "." + Guid.NewGuid().ToString("N") + ".tmp";
await File.WriteAllTextAsync(temporaryPath, json).ConfigureAwait(false);
File.Move(temporaryPath, path, overwrite: true);
}
finally
{
try { if (temporaryPath is not null && File.Exists(temporaryPath)) File.Delete(temporaryPath); }
finally { _gate.Release(); }
}
}
private string GetFullPath()
{
ArgumentException.ThrowIfNullOrWhiteSpace(FilePath);
return Path.GetFullPath(FilePath);
}
}