diff --git a/GFramework.Game.Abstractions/data/IDataRepository.cs b/GFramework.Game.Abstractions/data/IDataRepository.cs index a1b3ef0..0077a5b 100644 --- a/GFramework.Game.Abstractions/data/IDataRepository.cs +++ b/GFramework.Game.Abstractions/data/IDataRepository.cs @@ -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; /// /// 定义数据仓库接口,提供异步的数据加载、保存、检查存在性和删除操作 /// -public interface IDataRepository +public interface IDataRepository : IUtility { /// /// 异步加载指定类型的数据对象 @@ -25,6 +27,13 @@ public interface IDataRepository /// 返回加载的数据对象的Task Task LoadAsync() where T : class, IData, new(); + /// + /// 根据类型异步加载数据 + /// + /// 要加载的数据类型 + /// 异步操作任务,返回实现IData接口的数据对象 + Task LoadAsync(Type type); + /// /// 异步保存指定的数据对象 /// @@ -50,5 +59,7 @@ public interface IDataRepository /// /// 批量保存多个数据 /// + /// 要保存的数据列表,实现IData接口的对象集合 + /// 异步操作任务 Task SaveAllAsync(IEnumerable dataList); } \ No newline at end of file diff --git a/GFramework.Game.Abstractions/setting/ISettingsPersistence.cs b/GFramework.Game.Abstractions/setting/ISettingsPersistence.cs deleted file mode 100644 index 00280ae..0000000 --- a/GFramework.Game.Abstractions/setting/ISettingsPersistence.cs +++ /dev/null @@ -1,35 +0,0 @@ -using GFramework.Core.Abstractions.utility; - -namespace GFramework.Game.Abstractions.setting; - -/// -/// 设置持久化接口 -/// 定义了设置数据的异步加载、保存、检查存在性和删除操作 -/// -public interface ISettingsPersistence : IContextUtility -{ - /// - /// 异步加载指定类型的设置数据 - /// - Task LoadAsync() where T : class, IResettable, new(); - - /// - /// 异步保存指定的设置数据 - /// - Task SaveAsync(T section) where T : class, IResettable; - - /// - /// 异步检查指定类型的设置数据是否存在 - /// - Task ExistsAsync() where T : class, IResettable; - - /// - /// 异步删除指定类型的设置数据 - /// - Task DeleteAsync() where T : class, IResettable; - - /// - /// 保存所有设置数据 - /// - Task SaveAllAsync(IEnumerable allData); -} \ No newline at end of file diff --git a/GFramework.Game.Abstractions/setting/ISettingsSection.cs b/GFramework.Game.Abstractions/setting/ISettingsSection.cs index be480fe..7beb968 100644 --- a/GFramework.Game.Abstractions/setting/ISettingsSection.cs +++ b/GFramework.Game.Abstractions/setting/ISettingsSection.cs @@ -1,7 +1,9 @@ -namespace GFramework.Game.Abstractions.setting; +using GFramework.Game.Abstractions.data; + +namespace GFramework.Game.Abstractions.setting; /// /// 表示游戏设置的一个配置节接口 /// 该接口定义了设置配置节的基本契约,用于管理游戏中的各种配置选项 /// -public interface ISettingsSection; \ No newline at end of file +public interface ISettingsSection : IData; \ No newline at end of file diff --git a/GFramework.Game/data/DataRepository.cs b/GFramework.Game/data/DataRepository.cs index 599ad2b..1de64dc 100644 --- a/GFramework.Game/data/DataRepository.cs +++ b/GFramework.Game/data/DataRepository.cs @@ -44,6 +44,7 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options = var key = GetKey(); T result; + // 检查存储中是否存在指定键的数据 if (await Storage.ExistsAsync(key)) { result = await Storage.ReadAsync(key); @@ -53,12 +54,47 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options = result = new T(); } + // 如果启用事件功能,则发送数据加载完成事件 if (_options.EnableEvents) this.SendEvent(new DataLoadedEvent(result)); return result; } + /// + /// 异步加载指定类型的数据(通过Type参数) + /// + /// 要加载的数据类型 + /// 加载的数据对象 + public async Task 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(key); + } + else + { + result = (IData)Activator.CreateInstance(type)!; + } + + // 如果启用事件功能,则发送数据加载完成事件 + if (_options.EnableEvents) + this.SendEvent(new DataLoadedEvent(result)); + + return result; + } + + /// /// 异步保存指定类型的数据 /// diff --git a/GFramework.Game/setting/SettingsModel.cs b/GFramework.Game/setting/SettingsModel.cs index 9c18cb7..86bd2e4 100644 --- a/GFramework.Game/setting/SettingsModel.cs +++ b/GFramework.Game/setting/SettingsModel.cs @@ -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; /// /// 设置模型类,用于管理不同类型的应用程序设置部分 /// -public class SettingsModel : AbstractModel, ISettingsModel +public class SettingsModel(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)); + private readonly ConcurrentDictionary _applicators = new(); private readonly ConcurrentDictionary _dataSettings = new(); - private readonly ConcurrentDictionary _loadAsyncMethodCache = new(); private readonly ConcurrentDictionary> _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); } } } + /// /// 初始化方法,用于获取设置持久化服务 /// protected override void OnInit() { - _persistence = this.GetUtility(); + _repository ??= this.GetUtility()!; } } \ No newline at end of file diff --git a/GFramework.Game/setting/SettingsPersistence.cs b/GFramework.Game/setting/SettingsPersistence.cs deleted file mode 100644 index 27987b3..0000000 --- a/GFramework.Game/setting/SettingsPersistence.cs +++ /dev/null @@ -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; - -/// -/// 设置持久化服务类,负责处理设置数据的加载、保存、删除等操作 -/// -public class SettingsPersistence : AbstractContextUtility, ISettingsPersistence -{ - private IStorage _storage = null!; - - /// - /// 异步加载指定类型的设置数据 - /// - /// 设置数据类型,必须实现ISettingsData接口 - /// 如果存在则返回存储的设置数据,否则返回新创建的实例 - public async Task LoadAsync() where T : class, IResettable, new() - { - var key = GetKey(); - - if (await _storage.ExistsAsync(key)) - { - var result = await _storage.ReadAsync(key); - this.SendEvent(new SettingsLoadedEvent(result)); - return result; - } - - var newSettings = new T(); - this.SendEvent(new SettingsLoadedEvent(newSettings)); - return newSettings; - } - - /// - /// 异步保存设置数据到存储中 - /// - /// 设置数据类型,必须实现ISettingsData接口 - /// 要保存的设置数据实例 - public async Task SaveAsync(T section) where T : class, IResettable - { - var key = GetKey(); - await _storage.WriteAsync(key, section); - this.SendEvent(new SettingsSavedEvent(section)); - } - - /// - /// 检查指定类型的设置数据是否存在 - /// - /// 设置数据类型,必须实现ISettingsData接口 - /// 如果存在返回true,否则返回false - public async Task ExistsAsync() where T : class, IResettable - { - var key = GetKey(); - return await _storage.ExistsAsync(key); - } - - /// - /// 异步删除指定类型的设置数据 - /// - /// 设置数据类型,必须实现ISettingsData接口 - public async Task DeleteAsync() where T : class, IResettable - { - var key = GetKey(); - await _storage.DeleteAsync(key); - this.SendEvent(new SettingsDeletedEvent(typeof(T))); - await Task.CompletedTask; - } - - /// - /// 异步保存所有设置数据到存储中 - /// - /// 包含所有设置数据的可枚举集合 - public async Task SaveAllAsync(IEnumerable 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()!; - } - - /// - /// 获取指定类型的存储键名 - /// - /// 设置数据类型 - /// 格式为"Settings_类型名称"的键名 - private static string GetKey() where T : IResettable - { - return GetKey(typeof(T)); - } - - /// - /// 获取指定类型的存储键名 - /// - /// 设置数据类型 - /// 格式为"Settings_类型名称"的键名 - private static string GetKey(Type type) - { - return $"Settings_{type.Name}"; - } -} \ No newline at end of file