mirror of
https://github.com/GeWuYou/GFramework.git
synced 2026-05-06 16:16:44 +08:00
fix(game): 修复数据仓库与场景路由分析器警告
- 修复数据仓库异步存储调用的 ConfigureAwait(false) 使用,消除目标 MA0004 警告 - 更新 UnifiedSettingsDataRepository 的字符串键字典 comparer 为 StringComparer.Ordinal,消除目标 MA0002 警告 - 保留场景切换流程在当前上下文继续执行,并显式使用 ConfigureAwait(true) 说明上下文约束
This commit is contained in:
parent
7e13752bb1
commit
e3eec5452c
@ -52,7 +52,9 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options =
|
||||
var key = location.ToStorageKey();
|
||||
|
||||
// 检查存储中是否存在指定键的数据
|
||||
T result = await Storage.ExistsAsync(key) ? await Storage.ReadAsync<T>(key) : new T();
|
||||
T result = await Storage.ExistsAsync(key).ConfigureAwait(false)
|
||||
? await Storage.ReadAsync<T>(key).ConfigureAwait(false)
|
||||
: new T();
|
||||
|
||||
// 如果启用事件功能,则发送数据加载完成事件
|
||||
if (_options.EnableEvents)
|
||||
@ -70,7 +72,7 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options =
|
||||
public async Task SaveAsync<T>(IDataLocation location, T data)
|
||||
where T : class, IData
|
||||
{
|
||||
await SaveCoreAsync(location, data, emitSavedEvent: true);
|
||||
await SaveCoreAsync(location, data, emitSavedEvent: true).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -91,12 +93,12 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options =
|
||||
{
|
||||
var key = location.ToStorageKey();
|
||||
|
||||
if (!await Storage.ExistsAsync(key))
|
||||
if (!await Storage.ExistsAsync(key).ConfigureAwait(false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Storage.DeleteAsync(key);
|
||||
await Storage.DeleteAsync(key).ConfigureAwait(false);
|
||||
if (_options.EnableEvents)
|
||||
this.SendEvent(new DataDeletedEvent(location));
|
||||
}
|
||||
@ -113,7 +115,7 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options =
|
||||
// 但抑制逐项 DataSavedEvent,避免监听器对同一批次收到重复语义的事件。
|
||||
foreach (var (location, data) in valueTuples)
|
||||
{
|
||||
await SaveCoreUntypedAsync(location, data, emitSavedEvent: false);
|
||||
await SaveCoreUntypedAsync(location, data, emitSavedEvent: false).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
if (_options.EnableEvents)
|
||||
@ -140,8 +142,8 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options =
|
||||
{
|
||||
var key = location.ToStorageKey();
|
||||
|
||||
await BackupIfNeededAsync<T>(key);
|
||||
await Storage.WriteAsync(key, data);
|
||||
await BackupIfNeededAsync<T>(key).ConfigureAwait(false);
|
||||
await Storage.WriteAsync(key, data).ConfigureAwait(false);
|
||||
|
||||
if (emitSavedEvent && _options.EnableEvents)
|
||||
{
|
||||
@ -156,14 +158,14 @@ public class DataRepository(IStorage? storage, DataRepositoryOptions? options =
|
||||
private async Task BackupIfNeededAsync<T>(string key)
|
||||
where T : class, IData
|
||||
{
|
||||
if (!_options.AutoBackup || !await Storage.ExistsAsync(key))
|
||||
if (!_options.AutoBackup || !await Storage.ExistsAsync(key).ConfigureAwait(false))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var backupKey = $"{key}.backup";
|
||||
var existing = await Storage.ReadAsync<T>(key);
|
||||
await Storage.WriteAsync(backupKey, existing);
|
||||
var existing = await Storage.ReadAsync<T>(key).ConfigureAwait(false);
|
||||
await Storage.WriteAsync(backupKey, existing).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -99,7 +99,7 @@ public class SaveRepository<TSaveData> : AbstractContextUtility, ISaveRepository
|
||||
public async Task<bool> ExistsAsync(int slot)
|
||||
{
|
||||
var storage = GetSlotStorage(slot);
|
||||
return await storage.ExistsAsync(_config.SaveFileName);
|
||||
return await storage.ExistsAsync(_config.SaveFileName).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -111,10 +111,10 @@ public class SaveRepository<TSaveData> : AbstractContextUtility, ISaveRepository
|
||||
{
|
||||
var storage = GetSlotStorage(slot);
|
||||
|
||||
if (await storage.ExistsAsync(_config.SaveFileName))
|
||||
if (await storage.ExistsAsync(_config.SaveFileName).ConfigureAwait(false))
|
||||
{
|
||||
var loaded = await storage.ReadAsync<TSaveData>(_config.SaveFileName);
|
||||
return await MigrateIfNeededAsync(slot, storage, loaded);
|
||||
var loaded = await storage.ReadAsync<TSaveData>(_config.SaveFileName).ConfigureAwait(false);
|
||||
return await MigrateIfNeededAsync(slot, storage, loaded).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
return new TSaveData();
|
||||
@ -130,11 +130,11 @@ public class SaveRepository<TSaveData> : AbstractContextUtility, ISaveRepository
|
||||
var slotPath = $"{_config.SaveSlotPrefix}{slot}";
|
||||
|
||||
// 确保槽位目录存在
|
||||
if (!await _rootStorage.DirectoryExistsAsync(slotPath))
|
||||
await _rootStorage.CreateDirectoryAsync(slotPath);
|
||||
if (!await _rootStorage.DirectoryExistsAsync(slotPath).ConfigureAwait(false))
|
||||
await _rootStorage.CreateDirectoryAsync(slotPath).ConfigureAwait(false);
|
||||
|
||||
var storage = GetSlotStorage(slot);
|
||||
await storage.WriteAsync(_config.SaveFileName, data);
|
||||
await storage.WriteAsync(_config.SaveFileName, data).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -144,7 +144,7 @@ public class SaveRepository<TSaveData> : AbstractContextUtility, ISaveRepository
|
||||
public async Task DeleteAsync(int slot)
|
||||
{
|
||||
var storage = GetSlotStorage(slot);
|
||||
await storage.DeleteAsync(_config.SaveFileName);
|
||||
await storage.DeleteAsync(_config.SaveFileName).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -154,7 +154,7 @@ public class SaveRepository<TSaveData> : AbstractContextUtility, ISaveRepository
|
||||
public async Task<IReadOnlyList<int>> ListSlotsAsync()
|
||||
{
|
||||
// 列举所有槽位目录
|
||||
var directories = await _rootStorage.ListDirectoriesAsync();
|
||||
var directories = await _rootStorage.ListDirectoriesAsync().ConfigureAwait(false);
|
||||
|
||||
var slots = new List<int>();
|
||||
|
||||
@ -171,7 +171,7 @@ public class SaveRepository<TSaveData> : AbstractContextUtility, ISaveRepository
|
||||
|
||||
// 直接检查存档文件是否存在,避免重复创建 ScopedStorage
|
||||
var saveFilePath = $"{dirName}/{_config.SaveFileName}";
|
||||
if (await _rootStorage.ExistsAsync(saveFilePath))
|
||||
if (await _rootStorage.ExistsAsync(saveFilePath).ConfigureAwait(false))
|
||||
slots.Add(slot);
|
||||
}
|
||||
|
||||
@ -246,7 +246,7 @@ public class SaveRepository<TSaveData> : AbstractContextUtility, ISaveRepository
|
||||
$"{typeof(TSaveData).Name} in slot {slot}",
|
||||
"save migration");
|
||||
|
||||
await storage.WriteAsync(_config.SaveFileName, migrated);
|
||||
await storage.WriteAsync(_config.SaveFileName, migrated).ConfigureAwait(false);
|
||||
return migrated;
|
||||
}
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ public class UnifiedSettingsDataRepository(
|
||||
{
|
||||
private readonly SemaphoreSlim _lock = new(1, 1);
|
||||
private readonly DataRepositoryOptions _options = options ?? new DataRepositoryOptions();
|
||||
private readonly Dictionary<string, Type> _typeRegistry = new();
|
||||
private readonly Dictionary<string, Type> _typeRegistry = new(StringComparer.Ordinal);
|
||||
private UnifiedSettingsFile? _file;
|
||||
private bool _loaded;
|
||||
private IRuntimeTypeSerializer? _serializer = serializer;
|
||||
@ -67,7 +67,7 @@ public class UnifiedSettingsDataRepository(
|
||||
public async Task<T> LoadAsync<T>(IDataLocation location)
|
||||
where T : class, IData, new()
|
||||
{
|
||||
await EnsureLoadedAsync();
|
||||
await EnsureLoadedAsync().ConfigureAwait(false);
|
||||
var key = location.Key;
|
||||
var result = _file!.Sections.TryGetValue(key, out var raw) ? Serializer.Deserialize<T>(raw) : new T();
|
||||
if (_options.EnableEvents)
|
||||
@ -85,8 +85,9 @@ public class UnifiedSettingsDataRepository(
|
||||
public async Task SaveAsync<T>(IDataLocation location, T data)
|
||||
where T : class, IData
|
||||
{
|
||||
await EnsureLoadedAsync();
|
||||
await MutateAndPersistAsync(file => file.Sections[location.Key] = Serializer.Serialize(data));
|
||||
await EnsureLoadedAsync().ConfigureAwait(false);
|
||||
await MutateAndPersistAsync(file => file.Sections[location.Key] = Serializer.Serialize(data))
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (_options.EnableEvents)
|
||||
{
|
||||
@ -101,7 +102,7 @@ public class UnifiedSettingsDataRepository(
|
||||
/// <returns>如果数据存在则返回true,否则返回false</returns>
|
||||
public async Task<bool> ExistsAsync(IDataLocation location)
|
||||
{
|
||||
await EnsureLoadedAsync();
|
||||
await EnsureLoadedAsync().ConfigureAwait(false);
|
||||
return File.Sections.ContainsKey(location.Key);
|
||||
}
|
||||
|
||||
@ -112,10 +113,10 @@ public class UnifiedSettingsDataRepository(
|
||||
/// <returns>异步操作任务</returns>
|
||||
public async Task DeleteAsync(IDataLocation location)
|
||||
{
|
||||
await EnsureLoadedAsync();
|
||||
await EnsureLoadedAsync().ConfigureAwait(false);
|
||||
var removed = false;
|
||||
|
||||
await _lock.WaitAsync();
|
||||
await _lock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var currentFile = File;
|
||||
@ -126,7 +127,7 @@ public class UnifiedSettingsDataRepository(
|
||||
return;
|
||||
}
|
||||
|
||||
await WriteUnifiedFileCoreAsync(currentFile, nextFile);
|
||||
await WriteUnifiedFileCoreAsync(currentFile, nextFile).ConfigureAwait(false);
|
||||
_file = nextFile;
|
||||
}
|
||||
finally
|
||||
@ -148,17 +149,18 @@ public class UnifiedSettingsDataRepository(
|
||||
public async Task SaveAllAsync(
|
||||
IEnumerable<(IDataLocation location, IData data)> dataList)
|
||||
{
|
||||
await EnsureLoadedAsync();
|
||||
await EnsureLoadedAsync().ConfigureAwait(false);
|
||||
|
||||
var valueTuples = dataList.ToList();
|
||||
|
||||
await MutateAndPersistAsync(file =>
|
||||
{
|
||||
foreach (var (location, data) in valueTuples)
|
||||
{
|
||||
file.Sections[location.Key] = Serializer.Serialize(data);
|
||||
}
|
||||
});
|
||||
foreach (var (location, data) in valueTuples)
|
||||
{
|
||||
file.Sections[location.Key] = Serializer.Serialize(data);
|
||||
}
|
||||
})
|
||||
.ConfigureAwait(false);
|
||||
|
||||
if (_options.EnableEvents)
|
||||
this.SendEvent(new DataBatchSavedEvent(valueTuples));
|
||||
@ -170,9 +172,9 @@ public class UnifiedSettingsDataRepository(
|
||||
/// <returns>包含所有数据项的字典,键为数据位置键,值为数据对象</returns>
|
||||
public async Task<IDictionary<string, IData>> LoadAllAsync()
|
||||
{
|
||||
await EnsureLoadedAsync();
|
||||
await EnsureLoadedAsync().ConfigureAwait(false);
|
||||
|
||||
var result = new Dictionary<string, IData>();
|
||||
var result = new Dictionary<string, IData>(StringComparer.Ordinal);
|
||||
|
||||
foreach (var (key, raw) in File.Sections)
|
||||
{
|
||||
@ -216,15 +218,15 @@ public class UnifiedSettingsDataRepository(
|
||||
{
|
||||
if (_loaded) return;
|
||||
|
||||
await _lock.WaitAsync();
|
||||
await _lock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
if (_loaded) return;
|
||||
|
||||
var key = UnifiedKey;
|
||||
|
||||
_file = await Storage.ExistsAsync(key)
|
||||
? await Storage.ReadAsync<UnifiedSettingsFile>(key)
|
||||
_file = await Storage.ExistsAsync(key).ConfigureAwait(false)
|
||||
? await Storage.ReadAsync<UnifiedSettingsFile>(key).ConfigureAwait(false)
|
||||
: new UnifiedSettingsFile { Version = 1 };
|
||||
|
||||
_loaded = true;
|
||||
@ -241,7 +243,7 @@ public class UnifiedSettingsDataRepository(
|
||||
/// </summary>
|
||||
private async Task MutateAndPersistAsync(Action<UnifiedSettingsFile> mutation)
|
||||
{
|
||||
await _lock.WaitAsync();
|
||||
await _lock.WaitAsync().ConfigureAwait(false);
|
||||
try
|
||||
{
|
||||
var currentFile = File;
|
||||
@ -250,7 +252,7 @@ public class UnifiedSettingsDataRepository(
|
||||
// 先在副本上计算“下一份已提交状态”,只有底层持久化成功后才交换缓存,
|
||||
// 这样即使备份或写入失败,也不会把未提交修改留在内存快照里。
|
||||
mutation(nextFile);
|
||||
await WriteUnifiedFileCoreAsync(currentFile, nextFile);
|
||||
await WriteUnifiedFileCoreAsync(currentFile, nextFile).ConfigureAwait(false);
|
||||
_file = nextFile;
|
||||
}
|
||||
finally
|
||||
@ -270,13 +272,13 @@ public class UnifiedSettingsDataRepository(
|
||||
/// <param name="nextFile">即将提交的新统一文件快照。</param>
|
||||
private async Task WriteUnifiedFileCoreAsync(UnifiedSettingsFile currentFile, UnifiedSettingsFile nextFile)
|
||||
{
|
||||
if (_options.AutoBackup && await Storage.ExistsAsync(UnifiedKey))
|
||||
if (_options.AutoBackup && await Storage.ExistsAsync(UnifiedKey).ConfigureAwait(false))
|
||||
{
|
||||
var backupKey = $"{UnifiedKey}.backup";
|
||||
await Storage.WriteAsync(backupKey, currentFile);
|
||||
await Storage.WriteAsync(backupKey, currentFile).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
await Storage.WriteAsync(UnifiedKey, nextFile);
|
||||
await Storage.WriteAsync(UnifiedKey, nextFile).ConfigureAwait(false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -82,7 +82,7 @@ public abstract class SceneRouterBase
|
||||
string sceneKey,
|
||||
ISceneEnterParam? param = null)
|
||||
{
|
||||
await _transitionLock.WaitAsync();
|
||||
await _transitionLock.WaitAsync().ConfigureAwait(true);
|
||||
try
|
||||
{
|
||||
IsTransitioning = true;
|
||||
@ -184,7 +184,7 @@ public abstract class SceneRouterBase
|
||||
string sceneKey,
|
||||
ISceneEnterParam? param = null)
|
||||
{
|
||||
await _transitionLock.WaitAsync();
|
||||
await _transitionLock.WaitAsync().ConfigureAwait(true);
|
||||
try
|
||||
{
|
||||
IsTransitioning = true;
|
||||
@ -220,7 +220,7 @@ public abstract class SceneRouterBase
|
||||
}
|
||||
|
||||
// 守卫检查
|
||||
if (!await ExecuteEnterGuardsAsync(sceneKey, param))
|
||||
if (!await ExecuteEnterGuardsAsync(sceneKey, param).ConfigureAwait(true))
|
||||
{
|
||||
Log.Warn("Push blocked by guard: {0}", sceneKey);
|
||||
return;
|
||||
@ -233,20 +233,20 @@ public abstract class SceneRouterBase
|
||||
Root!.AddScene(scene);
|
||||
|
||||
// 加载资源
|
||||
await scene.OnLoadAsync(param);
|
||||
await scene.OnLoadAsync(param).ConfigureAwait(true);
|
||||
|
||||
// 暂停当前场景
|
||||
if (Stack.Count > 0)
|
||||
{
|
||||
var current = Stack.Peek();
|
||||
await current.OnPauseAsync();
|
||||
await current.OnPauseAsync().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
// 压入栈
|
||||
Stack.Push(scene);
|
||||
|
||||
// 进入场景
|
||||
await scene.OnEnterAsync();
|
||||
await scene.OnEnterAsync().ConfigureAwait(true);
|
||||
|
||||
Log.Debug("Push Scene: {0}, stackCount={1}",
|
||||
sceneKey, Stack.Count);
|
||||
@ -262,7 +262,7 @@ public abstract class SceneRouterBase
|
||||
/// <returns>异步任务。</returns>
|
||||
public async ValueTask PopAsync()
|
||||
{
|
||||
await _transitionLock.WaitAsync();
|
||||
await _transitionLock.WaitAsync().ConfigureAwait(true);
|
||||
try
|
||||
{
|
||||
IsTransitioning = true;
|
||||
@ -293,7 +293,7 @@ public abstract class SceneRouterBase
|
||||
var top = Stack.Peek();
|
||||
|
||||
// 守卫检查
|
||||
if (!await ExecuteLeaveGuardsAsync(top.Key))
|
||||
if (!await ExecuteLeaveGuardsAsync(top.Key).ConfigureAwait(true))
|
||||
{
|
||||
Log.Warn("Pop blocked by guard: {0}", top.Key);
|
||||
return;
|
||||
@ -302,10 +302,10 @@ public abstract class SceneRouterBase
|
||||
Stack.Pop();
|
||||
|
||||
// 退出场景
|
||||
await top.OnExitAsync();
|
||||
await top.OnExitAsync().ConfigureAwait(true);
|
||||
|
||||
// 卸载资源
|
||||
await top.OnUnloadAsync();
|
||||
await top.OnUnloadAsync().ConfigureAwait(true);
|
||||
|
||||
// 从场景树移除
|
||||
Root!.RemoveScene(top);
|
||||
@ -314,7 +314,7 @@ public abstract class SceneRouterBase
|
||||
if (Stack.Count > 0)
|
||||
{
|
||||
var next = Stack.Peek();
|
||||
await next.OnResumeAsync();
|
||||
await next.OnResumeAsync().ConfigureAwait(true);
|
||||
}
|
||||
|
||||
Log.Debug("Pop Scene, stackCount={0}", Stack.Count);
|
||||
@ -330,7 +330,7 @@ public abstract class SceneRouterBase
|
||||
/// <returns>异步任务。</returns>
|
||||
public async ValueTask ClearAsync()
|
||||
{
|
||||
await _transitionLock.WaitAsync();
|
||||
await _transitionLock.WaitAsync().ConfigureAwait(true);
|
||||
try
|
||||
{
|
||||
IsTransitioning = true;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user