mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-23 11:14:30 +08:00
refactor(setting): 将设置持久化功能整合到数据仓库中
- 移除独立的SettingsPersistence类,将其功能合并到DataRepository - 在DataRepository中新增基于Type参数的异步加载方法 - 修改SettingsModel以使用IDataRepository替代ISettingsPersistence - 更新SettingsModel为泛型类并注入数据仓库依赖 - 将ISettingsSection接口继承IData接口 - 删除ISettingsPersistence接口定义
This commit is contained in:
parent
0b7c64fd99
commit
76a8b140f1
@ -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);
|
||||
}
|
||||
@ -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);
|
||||
}
|
||||
@ -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;
|
||||
@ -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>
|
||||
|
||||
@ -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>()!;
|
||||
}
|
||||
}
|
||||
@ -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}";
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user