mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-03-22 10:34:30 +08:00
feat(storage): 添加存储系统接口和文件存储实现
- 定义了IStorage接口提供同步和异步的数据存储操作功能 - 实现了基于文件系统的FileStorage类支持读写删除操作 - 添加了ScopedStorage包装器为存储键提供作用域前缀功能 - 创建了ISerializer接口并实现JsonSerializer使用Newtonsoft.Json - 在项目中引入Newtonsoft.Json包依赖
This commit is contained in:
parent
7843e2a14f
commit
c3376bf4d5
72
GFramework.Core.Abstractions/storage/IStorage.cs
Normal file
72
GFramework.Core.Abstractions/storage/IStorage.cs
Normal file
@ -0,0 +1,72 @@
|
||||
using System.Threading.Tasks;
|
||||
using GFramework.Core.Abstractions.utility;
|
||||
|
||||
namespace GFramework.Core.Abstractions.storage;
|
||||
|
||||
/// <summary>
|
||||
/// 存储接口,提供同步和异步的数据存储操作功能
|
||||
/// </summary>
|
||||
public interface IStorage : IUtility
|
||||
{
|
||||
/// <summary>
|
||||
/// 检查指定键的存储项是否存在
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的键</param>
|
||||
/// <returns>如果键存在则返回true,否则返回false</returns>
|
||||
bool Exists(string key);
|
||||
|
||||
/// <summary>
|
||||
/// 异步检查指定键的存储项是否存在
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的键</param>
|
||||
/// <returns>如果键存在则返回true,否则返回false</returns>
|
||||
Task<bool> ExistsAsync(string key);
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定键的值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要读取的值的类型</typeparam>
|
||||
/// <param name="key">要读取的键</param>
|
||||
/// <returns>指定键对应的值</returns>
|
||||
T Read<T>(string key);
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定键的值,如果键不存在则返回默认值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要读取的值的类型</typeparam>
|
||||
/// <param name="key">要读取的键</param>
|
||||
/// <param name="defaultValue">当键不存在时返回的默认值</param>
|
||||
/// <returns>指定键对应的值,如果键不存在则返回默认值</returns>
|
||||
T Read<T>(string key, T defaultValue);
|
||||
|
||||
/// <summary>
|
||||
/// 异步读取指定键的值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要读取的值的类型</typeparam>
|
||||
/// <param name="key">要读取的键</param>
|
||||
/// <returns>指定键对应的值</returns>
|
||||
Task<T> ReadAsync<T>(string key);
|
||||
|
||||
/// <summary>
|
||||
/// 将值写入指定键
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要写入的值的类型</typeparam>
|
||||
/// <param name="key">要写入的键</param>
|
||||
/// <param name="value">要写入的值</param>
|
||||
void Write<T>(string key, T value);
|
||||
|
||||
/// <summary>
|
||||
/// 异步将值写入指定键
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要写入的值的类型</typeparam>
|
||||
/// <param name="key">要写入的键</param>
|
||||
/// <param name="value">要写入的值</param>
|
||||
/// <returns>异步操作任务</returns>
|
||||
Task WriteAsync<T>(string key, T value);
|
||||
|
||||
/// <summary>
|
||||
/// 删除指定键的存储项
|
||||
/// </summary>
|
||||
/// <param name="key">要删除的键</param>
|
||||
void Delete(string key);
|
||||
}
|
||||
23
GFramework.Game.Abstractions/serializer/ISerializer.cs
Normal file
23
GFramework.Game.Abstractions/serializer/ISerializer.cs
Normal file
@ -0,0 +1,23 @@
|
||||
namespace GFramework.Game.Abstractions.serializer;
|
||||
|
||||
/// <summary>
|
||||
/// 定义序列化器接口,提供对象序列化和反序列化的通用方法
|
||||
/// </summary>
|
||||
public interface ISerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// 将指定的对象序列化为字符串
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要序列化的对象类型</typeparam>
|
||||
/// <param name="value">要序列化的对象实例</param>
|
||||
/// <returns>序列化后的字符串表示</returns>
|
||||
string Serialize<T>(T value);
|
||||
|
||||
/// <summary>
|
||||
/// 将字符串数据反序列化为指定类型的对象
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要反序列化的目标对象类型</typeparam>
|
||||
/// <param name="data">包含序列化数据的字符串</param>
|
||||
/// <returns>反序列化后的对象实例</returns>
|
||||
T Deserialize<T>(string data);
|
||||
}
|
||||
@ -10,4 +10,7 @@
|
||||
<ProjectReference Include="..\GFramework.Core\GFramework.Core.csproj"/>
|
||||
<ProjectReference Include="..\$(AssemblyName).Abstractions\$(AssemblyName).Abstractions.csproj"/>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3"/>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
|
||||
29
GFramework.Game/serializer/JsonSerializer.cs
Normal file
29
GFramework.Game/serializer/JsonSerializer.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using GFramework.Game.Abstractions.serializer;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace GFramework.Game.serializer;
|
||||
|
||||
/// <summary>
|
||||
/// JSON序列化器实现类,用于将对象序列化为JSON字符串或将JSON字符串反序列化为对象
|
||||
/// </summary>
|
||||
public sealed class JsonSerializer : ISerializer
|
||||
{
|
||||
/// <summary>
|
||||
/// 将指定的对象序列化为JSON字符串
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要序列化的对象类型</typeparam>
|
||||
/// <param name="value">要序列化的对象实例</param>
|
||||
/// <returns>序列化后的JSON字符串</returns>
|
||||
public string Serialize<T>(T value)
|
||||
=> JsonConvert.SerializeObject(value);
|
||||
|
||||
/// <summary>
|
||||
/// 将JSON字符串反序列化为指定类型的对象
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要反序列化的对象类型</typeparam>
|
||||
/// <param name="data">包含JSON数据的字符串</param>
|
||||
/// <returns>反序列化后的对象实例</returns>
|
||||
/// <exception cref="ArgumentException">当无法反序列化数据时抛出</exception>
|
||||
public T Deserialize<T>(string data)
|
||||
=> JsonConvert.DeserializeObject<T>(data) ?? throw new ArgumentException("Cannot deserialize data");
|
||||
}
|
||||
170
GFramework.Game/storage/FileStorage.cs
Normal file
170
GFramework.Game/storage/FileStorage.cs
Normal file
@ -0,0 +1,170 @@
|
||||
using System.Text;
|
||||
using GFramework.Core.Abstractions.storage;
|
||||
using GFramework.Game.Abstractions.serializer;
|
||||
|
||||
namespace GFramework.Game.storage;
|
||||
|
||||
/// <summary>
|
||||
/// 基于文件系统的存储实现,实现了IStorage接口
|
||||
/// </summary>
|
||||
public sealed class FileStorage : IStorage
|
||||
{
|
||||
private readonly string _extension;
|
||||
private readonly string _rootPath;
|
||||
private readonly ISerializer _serializer;
|
||||
|
||||
/// <summary>
|
||||
/// 初始化FileStorage实例
|
||||
/// </summary>
|
||||
/// <param name="rootPath">存储根目录路径</param>
|
||||
/// <param name="serializer">序列化器实例</param>
|
||||
/// <param name="extension">存储文件的扩展名</param>
|
||||
public FileStorage(string rootPath, ISerializer serializer, string extension = ".dat")
|
||||
{
|
||||
_rootPath = rootPath;
|
||||
_serializer = serializer;
|
||||
_extension = extension;
|
||||
|
||||
Directory.CreateDirectory(_rootPath);
|
||||
}
|
||||
|
||||
#region Delete
|
||||
|
||||
/// <summary>
|
||||
/// 删除指定键的存储项
|
||||
/// </summary>
|
||||
/// <param name="key">存储键</param>
|
||||
public void Delete(string key)
|
||||
{
|
||||
var path = ToPath(key);
|
||||
if (File.Exists(path))
|
||||
File.Delete(path);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Helpers
|
||||
|
||||
/// <summary>
|
||||
/// 将存储键转换为文件路径
|
||||
/// </summary>
|
||||
/// <param name="key">存储键</param>
|
||||
/// <returns>对应的文件路径</returns>
|
||||
private string ToPath(string key)
|
||||
{
|
||||
// 防止非法路径
|
||||
key = Path.GetInvalidFileNameChars().Aggregate(key, (current, c) => current.Replace(c, '_'));
|
||||
return Path.Combine(_rootPath, $"{key}{_extension}");
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Exists
|
||||
|
||||
/// <summary>
|
||||
/// 检查指定键的存储项是否存在
|
||||
/// </summary>
|
||||
/// <param name="key">存储键</param>
|
||||
/// <returns>存在返回true,否则返回false</returns>
|
||||
public bool Exists(string key)
|
||||
=> File.Exists(ToPath(key));
|
||||
|
||||
/// <summary>
|
||||
/// 异步检查指定键的存储项是否存在
|
||||
/// </summary>
|
||||
/// <param name="key">存储键</param>
|
||||
/// <returns>存在返回true,否则返回false</returns>
|
||||
public Task<bool> ExistsAsync(string key)
|
||||
=> Task.FromResult(Exists(key));
|
||||
|
||||
#endregion
|
||||
|
||||
#region Read
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定键的存储项
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要反序列化的类型</typeparam>
|
||||
/// <param name="key">存储键</param>
|
||||
/// <returns>反序列化后的对象</returns>
|
||||
/// <exception cref="FileNotFoundException">当指定键不存在时抛出</exception>
|
||||
public T Read<T>(string key)
|
||||
{
|
||||
var path = ToPath(key);
|
||||
|
||||
if (!File.Exists(path))
|
||||
throw new FileNotFoundException($"Storage key not found: {key}", path);
|
||||
|
||||
var content = File.ReadAllText(path, Encoding.UTF8);
|
||||
return _serializer.Deserialize<T>(content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定键的存储项,如果不存在则返回默认值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要反序列化的类型</typeparam>
|
||||
/// <param name="key">存储键</param>
|
||||
/// <param name="defaultValue">默认值</param>
|
||||
/// <returns>存在则返回反序列化后的对象,否则返回默认值</returns>
|
||||
public T Read<T>(string key, T defaultValue)
|
||||
{
|
||||
var path = ToPath(key);
|
||||
if (!File.Exists(path))
|
||||
return defaultValue;
|
||||
|
||||
var content = File.ReadAllText(path, Encoding.UTF8);
|
||||
return _serializer.Deserialize<T>(content);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步读取指定键的存储项
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要反序列化的类型</typeparam>
|
||||
/// <param name="key">存储键</param>
|
||||
/// <returns>反序列化后的对象</returns>
|
||||
/// <exception cref="FileNotFoundException">当指定键不存在时抛出</exception>
|
||||
public async Task<T> ReadAsync<T>(string key)
|
||||
{
|
||||
var path = ToPath(key);
|
||||
|
||||
if (!File.Exists(path))
|
||||
throw new FileNotFoundException($"Storage key not found: {key}", path);
|
||||
|
||||
var content = await File.ReadAllTextAsync(path, Encoding.UTF8);
|
||||
return _serializer.Deserialize<T>(content);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Write
|
||||
|
||||
/// <summary>
|
||||
/// 写入指定键的存储项
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要序列化的类型</typeparam>
|
||||
/// <param name="key">存储键</param>
|
||||
/// <param name="value">要存储的值</param>
|
||||
public void Write<T>(string key, T value)
|
||||
{
|
||||
var path = ToPath(key);
|
||||
var content = _serializer.Serialize(value);
|
||||
|
||||
File.WriteAllText(path, content, Encoding.UTF8);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 异步写入指定键的存储项
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要序列化的类型</typeparam>
|
||||
/// <param name="key">存储键</param>
|
||||
/// <param name="value">要存储的值</param>
|
||||
public async Task WriteAsync<T>(string key, T value)
|
||||
{
|
||||
var path = ToPath(key);
|
||||
var content = _serializer.Serialize(value);
|
||||
|
||||
await File.WriteAllTextAsync(path, content, Encoding.UTF8);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
98
GFramework.Game/storage/ScopedStorage.cs
Normal file
98
GFramework.Game/storage/ScopedStorage.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using GFramework.Core.Abstractions.storage;
|
||||
|
||||
namespace GFramework.Game.storage;
|
||||
|
||||
/// <summary>
|
||||
/// 提供带有作用域前缀的存储包装器,将所有键都加上指定的前缀
|
||||
/// </summary>
|
||||
/// <param name="inner">内部的实际存储实现</param>
|
||||
/// <param name="prefix">用于所有键的前缀字符串</param>
|
||||
public sealed class ScopedStorage(IStorage inner, string prefix) : IStorage
|
||||
{
|
||||
/// <summary>
|
||||
/// 检查指定键是否存在
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的键</param>
|
||||
/// <returns>如果键存在则返回true,否则返回false</returns>
|
||||
public bool Exists(string key)
|
||||
=> inner.Exists(Key(key));
|
||||
|
||||
/// <summary>
|
||||
/// 异步检查指定键是否存在
|
||||
/// </summary>
|
||||
/// <param name="key">要检查的键</param>
|
||||
/// <returns>如果键存在则返回true,否则返回false</returns>
|
||||
public Task<bool> ExistsAsync(string key)
|
||||
=> inner.ExistsAsync(Key(key));
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定键的值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要读取的值的类型</typeparam>
|
||||
/// <param name="key">要读取的键</param>
|
||||
/// <returns>键对应的值</returns>
|
||||
public T Read<T>(string key)
|
||||
=> inner.Read<T>(Key(key));
|
||||
|
||||
/// <summary>
|
||||
/// 读取指定键的值,如果键不存在则返回默认值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要读取的值的类型</typeparam>
|
||||
/// <param name="key">要读取的键</param>
|
||||
/// <param name="defaultValue">当键不存在时返回的默认值</param>
|
||||
/// <returns>键对应的值或默认值</returns>
|
||||
public T Read<T>(string key, T defaultValue)
|
||||
=> inner.Read(Key(key), defaultValue);
|
||||
|
||||
/// <summary>
|
||||
/// 异步读取指定键的值
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要读取的值的类型</typeparam>
|
||||
/// <param name="key">要读取的键</param>
|
||||
/// <returns>键对应的值的任务</returns>
|
||||
public Task<T> ReadAsync<T>(string key)
|
||||
=> inner.ReadAsync<T>(Key(key));
|
||||
|
||||
/// <summary>
|
||||
/// 写入指定键值对
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要写入的值的类型</typeparam>
|
||||
/// <param name="key">要写入的键</param>
|
||||
/// <param name="value">要写入的值</param>
|
||||
public void Write<T>(string key, T value)
|
||||
=> inner.Write(Key(key), value);
|
||||
|
||||
/// <summary>
|
||||
/// 异步写入指定键值对
|
||||
/// </summary>
|
||||
/// <typeparam name="T">要写入的值的类型</typeparam>
|
||||
/// <param name="key">要写入的键</param>
|
||||
/// <param name="value">要写入的值</param>
|
||||
public Task WriteAsync<T>(string key, T value)
|
||||
=> inner.WriteAsync(Key(key), value);
|
||||
|
||||
/// <summary>
|
||||
/// 删除指定键
|
||||
/// </summary>
|
||||
/// <param name="key">要删除的键</param>
|
||||
public void Delete(string key)
|
||||
=> inner.Delete(Key(key));
|
||||
|
||||
/// <summary>
|
||||
/// 为给定的键添加前缀
|
||||
/// </summary>
|
||||
/// <param name="key">原始键</param>
|
||||
/// <returns>添加前缀后的键</returns>
|
||||
private string Key(string key)
|
||||
=> string.IsNullOrEmpty(prefix)
|
||||
? key
|
||||
: $"{prefix}/{key}";
|
||||
|
||||
/// <summary>
|
||||
/// 创建一个新的作用域存储实例
|
||||
/// </summary>
|
||||
/// <param name="scope">新的作用域名称</param>
|
||||
/// <returns>新的作用域存储实例</returns>
|
||||
public IStorage Scope(string scope)
|
||||
=> new ScopedStorage(inner, Key(scope));
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user