namespace XFEExtension.NetCore.AutoConfig;
/// <summary>
/// 为同一种配置类型创建相互独立的实例和文件存储。适用于多租户、多账号或多工作区场景。
/// </summary>
/// <typeparam name="TProfile">配置类型</typeparam>
public sealed class ProfileStore<TProfile> where TProfile : XFEProfile, new()
{
private readonly object syncRoot = new();
private readonly Func<TProfile> profileFactory;
private readonly string profilePath;
private TProfile current;
/// <summary>
/// 创建配置存储。
/// </summary>
/// <param name="profilePath">包含扩展名的完整配置文件路径</param>
/// <param name="autoLoad">是否在创建时读取文件</param>
/// <param name="profileFactory">可选实例工厂;省略时调用公共无参构造函数</param>
public ProfileStore(string profilePath, bool autoLoad = true, Func<TProfile>? profileFactory = null)
{
ArgumentException.ThrowIfNullOrWhiteSpace(profilePath);
this.profilePath = Path.GetFullPath(profilePath);
this.profileFactory = profileFactory ?? (static () => new TProfile());
current = CreateProfile();
if (autoLoad)
current = (TProfile)current.InstanceLoadProfile(CreateProfile);
}
/// <summary>实际使用的完整配置文件路径。</summary>
public string ProfilePath => profilePath;
/// <summary>当前实例。加载或导入只有在迁移和验证成功后才会替换它。</summary>
public TProfile Current
{
get
{
lock (syncRoot)
return current;
}
}
/// <summary>重新从文件加载配置。</summary>
public void Load()
{
lock (syncRoot)
current = (TProfile)current.InstanceLoadProfile(CreateProfile);
}
/// <summary>立即保存当前配置。</summary>
public void Save()
{
lock (syncRoot)
current.InstanceSaveProfile();
}
/// <summary>异步保存当前配置。</summary>
public Task SaveAsync(CancellationToken cancellationToken = default)
{
TProfile snapshot;
lock (syncRoot)
snapshot = current;
return snapshot.InstanceSaveProfileAsync(cancellationToken);
}
/// <summary>等待调用前已经请求的自动保存完成。</summary>
public Task FlushAsync(CancellationToken cancellationToken = default)
{
TProfile snapshot;
lock (syncRoot)
snapshot = current;
return snapshot.InstanceFlushProfileAsync(cancellationToken);
}
/// <summary>删除此存储对应的配置文件。</summary>
public void Delete()
{
lock (syncRoot)
current.InstanceDeleteProfile();
}
/// <summary>导出包含版本元数据的配置文本。</summary>
public string Export()
{
lock (syncRoot)
return current.InstanceExportProfile();
}
/// <summary>以原始字节导出包含版本元数据的配置;MessagePack 模式不会产生 Base64 中间文本。</summary>
public byte[] ExportBytes()
{
lock (syncRoot)
return current.InstanceExportProfileBytes();
}
/// <summary>导入配置文本;迁移或验证失败时保持当前实例不变。</summary>
public void Import(string profileContent)
{
ArgumentNullException.ThrowIfNull(profileContent);
lock (syncRoot)
current = (TProfile)current.InstanceImportProfile(profileContent, CreateProfile);
}
/// <summary>从原始字节导入配置;迁移或验证失败时保持当前实例不变。</summary>
public void ImportBytes(ReadOnlyMemory<byte> profileContent)
{
lock (syncRoot)
current = (TProfile)current.InstanceImportProfileBytes(profileContent, CreateProfile);
}
/// <summary>
/// 在实例锁内修改配置,并在修改成功后请求一次合并自动保存。
/// </summary>
public void Update(Action<TProfile> update)
{
ArgumentNullException.ThrowIfNull(update);
lock (syncRoot)
lock (current.ProfileSyncRoot)
{
update(current);
current.InstanceRequestSaveProfile();
}
}
/// <summary>在实例锁内读取配置。</summary>
public TResult Read<TResult>(Func<TProfile, TResult> read)
{
ArgumentNullException.ThrowIfNull(read);
lock (syncRoot)
lock (current.ProfileSyncRoot)
return read(current);
}
private TProfile CreateProfile()
{
var profile = profileFactory() ?? throw new InvalidOperationException("配置实例工厂返回了 null");
profile.Initialize();
profile.CurrentProfilePath = profilePath;
return profile;
}
}
namespace XFEExtension.NetCore.AutoConfig;
/// <summary>
/// 为同一种配置类型创建相互独立的实例和文件存储。适用于多租户、多账号或多工作区场景。
/// </summary>
/// <typeparam name="TProfile">配置类型</typeparam>
public sealed class ProfileStore<TProfile> where TProfile : XFEProfile, new()
{
private readonly object syncRoot = new();
private readonly Func<TProfile> profileFactory;
private readonly string profilePath;
private TProfile current;
/// <summary>
/// 创建配置存储。
/// </summary>
/// <param name="profilePath">包含扩展名的完整配置文件路径</param>
/// <param name="autoLoad">是否在创建时读取文件</param>
/// <param name="profileFactory">可选实例工厂;省略时调用公共无参构造函数</param>
public ProfileStore(string profilePath, bool autoLoad = true, Func<TProfile>? profileFactory = null)
{
ArgumentException.ThrowIfNullOrWhiteSpace(profilePath);
this.profilePath = Path.GetFullPath(profilePath);
this.profileFactory = profileFactory ?? (static () => new TProfile());
current = CreateProfile();
if (autoLoad)
current = (TProfile)current.InstanceLoadProfile(CreateProfile);
}
/// <summary>实际使用的完整配置文件路径。</summary>
public string ProfilePath => profilePath;
/// <summary>当前实例。加载或导入只有在迁移和验证成功后才会替换它。</summary>
public TProfile Current
{
get
{
lock (syncRoot)
return current;
}
}
/// <summary>重新从文件加载配置。</summary>
public void Load()
{
lock (syncRoot)
current = (TProfile)current.InstanceLoadProfile(CreateProfile);
}
/// <summary>立即保存当前配置。</summary>
public void Save()
{
lock (syncRoot)
current.InstanceSaveProfile();
}
/// <summary>异步保存当前配置。</summary>
public Task SaveAsync(CancellationToken cancellationToken = default)
{
TProfile snapshot;
lock (syncRoot)
snapshot = current;
return snapshot.InstanceSaveProfileAsync(cancellationToken);
}
/// <summary>等待调用前已经请求的自动保存完成。</summary>
public Task FlushAsync(CancellationToken cancellationToken = default)
{
TProfile snapshot;
lock (syncRoot)
snapshot = current;
return snapshot.InstanceFlushProfileAsync(cancellationToken);
}
/// <summary>删除此存储对应的配置文件。</summary>
public void Delete()
{
lock (syncRoot)
current.InstanceDeleteProfile();
}
/// <summary>导出包含版本元数据的配置文本。</summary>
public string Export()
{
lock (syncRoot)
return current.InstanceExportProfile();
}
/// <summary>以原始字节导出包含版本元数据的配置;MessagePack 模式不会产生 Base64 中间文本。</summary>
public byte[] ExportBytes()
{
lock (syncRoot)
return current.InstanceExportProfileBytes();
}
/// <summary>导入配置文本;迁移或验证失败时保持当前实例不变。</summary>
public void Import(string profileContent)
{
ArgumentNullException.ThrowIfNull(profileContent);
lock (syncRoot)
current = (TProfile)current.InstanceImportProfile(profileContent, CreateProfile);
}
/// <summary>从原始字节导入配置;迁移或验证失败时保持当前实例不变。</summary>
public void ImportBytes(ReadOnlyMemory<byte> profileContent)
{
lock (syncRoot)
current = (TProfile)current.InstanceImportProfileBytes(profileContent, CreateProfile);
}
/// <summary>
/// 在实例锁内修改配置,并在修改成功后请求一次合并自动保存。
/// </summary>
public void Update(Action<TProfile> update)
{
ArgumentNullException.ThrowIfNull(update);
lock (syncRoot)
lock (current.ProfileSyncRoot)
{
update(current);
current.InstanceRequestSaveProfile();
}
}
/// <summary>在实例锁内读取配置。</summary>
public TResult Read<TResult>(Func<TProfile, TResult> read)
{
ArgumentNullException.ThrowIfNull(read);
lock (syncRoot)
lock (current.ProfileSyncRoot)
return read(current);
}
private TProfile CreateProfile()
{
var profile = profileFactory() ?? throw new InvalidOperationException("配置实例工厂返回了 null");
profile.Initialize();
profile.CurrentProfilePath = profilePath;
return profile;
}
}