refactor(setting): 将设置持久化功能整合到数据仓库中

- 移除独立的SettingsPersistence类,将其功能合并到DataRepository
- 在DataRepository中新增基于Type参数的异步加载方法
- 修改SettingsModel以使用IDataRepository替代ISettingsPersistence
- 更新SettingsModel为泛型类并注入数据仓库依赖
- 将ISettingsSection接口继承IData接口
- 删除ISettingsPersistence接口定义
This commit is contained in:
GeWuYou 2026-01-28 20:36:24 +08:00
parent 0b7c64fd99
commit 76a8b140f1
6 changed files with 65 additions and 168 deletions

View File

@ -11,12 +11,14 @@
// See the License for the specific language governing permissions and
// limitations under the License.
using GFramework.Core.Abstractions.utility;
namespace GFramework.Game.Abstractions.data;
/// <summary>
/// 定义数据仓库接口,提供异步的数据加载、保存、检查存在性和删除操作
/// </summary>
public interface IDataRepository
public interface IDataRepository : IUtility
{
/// <summary>
/// 异步加载指定类型的数据对象
@ -25,6 +27,13 @@ public interface IDataRepository
/// <returns>返回加载的数据对象的Task</returns>
Task<T> LoadAsync<T>() where T : class, IData, new();
/// <summary>
/// 根据类型异步加载数据
/// </summary>
/// <param name="type">要加载的数据类型</param>
/// <returns>异步操作任务返回实现IData接口的数据对象</returns>
Task<IData> LoadAsync(Type type);
/// <summary>
/// 异步保存指定的数据对象
/// </summary>
@ -50,5 +59,7 @@ public interface IDataRepository
/// <summary>
/// 批量保存多个数据
/// </summary>
/// <param name="dataList">要保存的数据列表实现IData接口的对象集合</param>
/// <returns>异步操作任务</returns>
Task SaveAllAsync(IEnumerable<IData> dataList);
}

View File

@ -1,35 +0,0 @@
using GFramework.Core.Abstractions.utility;
namespace GFramework.Game.Abstractions.setting;
/// <summary>
/// 设置持久化接口
/// 定义了设置数据的异步加载、保存、检查存在性和删除操作
/// </summary>
public interface ISettingsPersistence : IContextUtility
{
/// <summary>
/// 异步加载指定类型的设置数据
/// </summary>
Task<T> LoadAsync<T>() where T : class, IResettable, new();
/// <summary>
/// 异步保存指定的设置数据
/// </summary>
Task SaveAsync<T>(T section) where T : class, IResettable;
/// <summary>
/// 异步检查指定类型的设置数据是否存在
/// </summary>
Task<bool> ExistsAsync<T>() where T : class, IResettable;
/// <summary>
/// 异步删除指定类型的设置数据
/// </summary>
Task DeleteAsync<T>() where T : class, IResettable;
/// <summary>
/// 保存所有设置数据
/// </summary>
Task SaveAllAsync(IEnumerable<IResettable> allData);
}

View File

@ -1,7 +1,9 @@
namespace GFramework.Game.Abstractions.setting;
using GFramework.Game.Abstractions.data;
namespace GFramework.Game.Abstractions.setting;
/// <summary>
/// 表示游戏设置的一个配置节接口
/// 该接口定义了设置配置节的基本契约,用于管理游戏中的各种配置选项
/// </summary>
public interface ISettingsSection;
public interface ISettingsSection : IData;

View File

@ -44,6 +44,7 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options =
var key = GetKey<T>();
T result;
// 检查存储中是否存在指定键的数据
if (await Storage.ExistsAsync(key))
{
result = await Storage.ReadAsync<T>(key);
@ -53,12 +54,47 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options =
result = new T();
}
// 如果启用事件功能,则发送数据加载完成事件
if (_options.EnableEvents)
this.SendEvent(new DataLoadedEvent<T>(result));
return result;
}
/// <summary>
/// 异步加载指定类型的数据通过Type参数
/// </summary>
/// <param name="type">要加载的数据类型</param>
/// <returns>加载的数据对象</returns>
public async Task<IData> LoadAsync(Type type)
{
if (!typeof(IData).IsAssignableFrom(type))
throw new ArgumentException($"{type.Name} does not implement IData");
if (!type.IsClass || type.GetConstructor(Type.EmptyTypes) == null)
throw new ArgumentException($"{type.Name} must be a class with parameterless constructor");
var key = GetKey(type);
IData result;
// 检查存储中是否存在指定键的数据
if (await Storage.ExistsAsync(key))
{
result = await Storage.ReadAsync<IData>(key);
}
else
{
result = (IData)Activator.CreateInstance(type)!;
}
// 如果启用事件功能,则发送数据加载完成事件
if (_options.EnableEvents)
this.SendEvent(new DataLoadedEvent<IData>(result));
return result;
}
/// <summary>
/// 异步保存指定类型的数据
/// </summary>

View File

@ -1,10 +1,10 @@
using System.Collections.Concurrent;
using System.Reflection;
using GFramework.Core.Abstractions.logging;
using GFramework.Core.Abstractions.versioning;
using GFramework.Core.extensions;
using GFramework.Core.logging;
using GFramework.Core.model;
using GFramework.Game.Abstractions.data;
using GFramework.Game.Abstractions.setting;
namespace GFramework.Game.setting;
@ -12,15 +12,18 @@ namespace GFramework.Game.setting;
/// <summary>
/// 设置模型类,用于管理不同类型的应用程序设置部分
/// </summary>
public class SettingsModel : AbstractModel, ISettingsModel
public class SettingsModel<TRepository>(IDataRepository? repository)
: AbstractModel, ISettingsModel where TRepository : class, IDataRepository
{
private static readonly ILogger Log = LoggerFactoryResolver.Provider.CreateLogger(nameof(SettingsModel));
private static readonly ILogger Log =
LoggerFactoryResolver.Provider.CreateLogger(nameof(SettingsModel<TRepository>));
private readonly ConcurrentDictionary<Type, IApplyAbleSettings> _applicators = new();
private readonly ConcurrentDictionary<Type, IResettable> _dataSettings = new();
private readonly ConcurrentDictionary<Type, MethodInfo> _loadAsyncMethodCache = new();
private readonly ConcurrentDictionary<Type, Dictionary<int, ISettingsMigration>> _migrationCache = new();
private readonly ConcurrentDictionary<(Type type, int from), ISettingsMigration> _migrations = new();
private ISettingsPersistence? _persistence;
private IDataRepository? _repository = repository;
private IDataRepository Repository => _repository ?? throw new InvalidOperationException("Repository is not set");
// -----------------------------
// Data
@ -167,36 +170,29 @@ public class SettingsModel : AbstractModel, ISettingsModel
foreach (var type in settingTypes)
{
if (!typeof(IResettable).IsAssignableFrom(type) ||
!type.IsClass ||
type.GetConstructor(Type.EmptyTypes) == null)
!typeof(IData).IsAssignableFrom(type))
continue;
try
{
var method = _loadAsyncMethodCache.GetOrAdd(type, t => typeof(ISettingsPersistence)
.GetMethod(nameof(ISettingsPersistence.LoadAsync))!
.MakeGenericMethod(t));
var task = (Task)method.Invoke(_persistence, null)!;
await task;
var loaded = (ISettingsSection)((dynamic)task).Result;
var loaded = (ISettingsSection)await Repository.LoadAsync(type);
var migrated = MigrateIfNeeded(loaded);
_dataSettings[type] = (IResettable)migrated;
_migrationCache.TryRemove(type, out _);
}
catch (Exception ex)
{
Log.Error($"Failed to load settings for {type.Name}: {ex}", ex);
Log.Error($"Failed to load settings for {type.Name}", ex);
}
}
}
/// <summary>
/// 初始化方法,用于获取设置持久化服务
/// </summary>
protected override void OnInit()
{
_persistence = this.GetUtility<ISettingsPersistence>();
_repository ??= this.GetUtility<TRepository>()!;
}
}

View File

@ -1,113 +0,0 @@
using GFramework.Core.Abstractions.storage;
using GFramework.Core.extensions;
using GFramework.Core.utility;
using GFramework.Game.Abstractions.setting;
using GFramework.Game.setting.events;
namespace GFramework.Game.setting;
/// <summary>
/// 设置持久化服务类,负责处理设置数据的加载、保存、删除等操作
/// </summary>
public class SettingsPersistence : AbstractContextUtility, ISettingsPersistence
{
private IStorage _storage = null!;
/// <summary>
/// 异步加载指定类型的设置数据
/// </summary>
/// <typeparam name="T">设置数据类型必须实现ISettingsData接口</typeparam>
/// <returns>如果存在则返回存储的设置数据,否则返回新创建的实例</returns>
public async Task<T> LoadAsync<T>() where T : class, IResettable, new()
{
var key = GetKey<T>();
if (await _storage.ExistsAsync(key))
{
var result = await _storage.ReadAsync<T>(key);
this.SendEvent(new SettingsLoadedEvent<T>(result));
return result;
}
var newSettings = new T();
this.SendEvent(new SettingsLoadedEvent<T>(newSettings));
return newSettings;
}
/// <summary>
/// 异步保存设置数据到存储中
/// </summary>
/// <typeparam name="T">设置数据类型必须实现ISettingsData接口</typeparam>
/// <param name="section">要保存的设置数据实例</param>
public async Task SaveAsync<T>(T section) where T : class, IResettable
{
var key = GetKey<T>();
await _storage.WriteAsync(key, section);
this.SendEvent(new SettingsSavedEvent<T>(section));
}
/// <summary>
/// 检查指定类型的设置数据是否存在
/// </summary>
/// <typeparam name="T">设置数据类型必须实现ISettingsData接口</typeparam>
/// <returns>如果存在返回true否则返回false</returns>
public async Task<bool> ExistsAsync<T>() where T : class, IResettable
{
var key = GetKey<T>();
return await _storage.ExistsAsync(key);
}
/// <summary>
/// 异步删除指定类型的设置数据
/// </summary>
/// <typeparam name="T">设置数据类型必须实现ISettingsData接口</typeparam>
public async Task DeleteAsync<T>() where T : class, IResettable
{
var key = GetKey<T>();
await _storage.DeleteAsync(key);
this.SendEvent(new SettingsDeletedEvent(typeof(T)));
await Task.CompletedTask;
}
/// <summary>
/// 异步保存所有设置数据到存储中
/// </summary>
/// <param name="allData">包含所有设置数据的可枚举集合</param>
public async Task SaveAllAsync(IEnumerable<IResettable> allData)
{
var dataList = allData.ToList();
foreach (var data in dataList)
{
var type = data.GetType();
var key = GetKey(type);
await _storage.WriteAsync(key, data);
}
this.SendEvent(new SettingsBatchSavedEvent(dataList));
}
protected override void OnInit()
{
_storage = this.GetUtility<IStorage>()!;
}
/// <summary>
/// 获取指定类型的存储键名
/// </summary>
/// <typeparam name="T">设置数据类型</typeparam>
/// <returns>格式为"Settings_类型名称"的键名</returns>
private static string GetKey<T>() where T : IResettable
{
return GetKey(typeof(T));
}
/// <summary>
/// 获取指定类型的存储键名
/// </summary>
/// <param name="type">设置数据类型</param>
/// <returns>格式为"Settings_类型名称"的键名</returns>
private static string GetKey(Type type)
{
return $"Settings_{type.Name}";
}
}