namespace GFramework.Game.Config; /// /// 提供面向宿主的 YAML 文本校验入口,使保存前校验可以复用运行时同一套 schema 规则。 /// public static class YamlConfigTextValidator { // Cache parsed schemas by table/path plus last write time so save-path validation can // avoid repeated disk IO and JSON parsing while still observing schema edits. private static readonly ConcurrentDictionary SchemaCache = new(); /// /// 使用指定 schema 文件同步校验 YAML 文本。 /// /// 所属配置表名称。 /// Schema 文件绝对路径。 /// YAML 文件路径,仅用于诊断信息。 /// 待校验的 YAML 文本。 /// 为空白时抛出。 /// 时抛出。 /// 当 schema 文件不可用,或 YAML 内容与 schema 不匹配时抛出。 /// /// 同步加载 schema 并立即校验,适合非异步上下文;内部委托 执行校验逻辑。 /// public static void Validate( string tableName, string schemaPath, string yamlPath, string yamlText) { var schema = GetOrLoadSchema(tableName, schemaPath); YamlConfigSchemaValidator.Validate(tableName, schema, yamlPath, yamlText); } /// /// 使用指定 schema 文件异步校验 YAML 文本。 /// /// 所属配置表名称。 /// Schema 文件绝对路径。 /// YAML 文件路径,仅用于诊断信息。 /// 待校验的 YAML 文本。 /// 取消令牌。 /// 表示异步校验操作的任务。 /// 为空白时抛出。 /// 时抛出。 /// 当 schema 文件不可用,或 YAML 内容与 schema 不匹配时抛出。 /// 已被触发时抛出。 /// /// 异步加载 schema(调用 )后同步执行校验,适合 I/O 密集场景; /// 校验本身不涉及异步操作。 /// public static async Task ValidateAsync( string tableName, string schemaPath, string yamlPath, string yamlText, CancellationToken cancellationToken = default) { var schema = await GetOrLoadSchemaAsync(tableName, schemaPath, cancellationToken) .ConfigureAwait(false); YamlConfigSchemaValidator.Validate(tableName, schema, yamlPath, yamlText); } /// /// 获取可复用的 schema 模型,必要时从磁盘重新加载。 /// /// 所属配置表名称。 /// Schema 文件绝对路径。 /// 与当前 schema 文件内容匹配的已解析模型。 /// 为空白时抛出。 /// 当 schema 文件不可用或内容非法时抛出。 private static YamlConfigSchema GetOrLoadSchema( string tableName, string schemaPath) { var cacheKey = CreateCacheKey(tableName, schemaPath); if (TryGetCachedSchema(cacheKey, out var cachedSchema)) { return cachedSchema; } var lastWriteTimeUtc = File.GetLastWriteTimeUtc(schemaPath); var schema = YamlConfigSchemaValidator.Load(tableName, schemaPath); CacheSchema(cacheKey, lastWriteTimeUtc, schema); return schema; } /// /// 异步获取可复用的 schema 模型,必要时从磁盘重新加载。 /// /// 所属配置表名称。 /// Schema 文件绝对路径。 /// 取消令牌。 /// 与当前 schema 文件内容匹配的已解析模型。 /// 为空白时抛出。 /// 当 schema 文件不可用或内容非法时抛出。 private static async Task GetOrLoadSchemaAsync( string tableName, string schemaPath, CancellationToken cancellationToken) { var cacheKey = CreateCacheKey(tableName, schemaPath); if (TryGetCachedSchema(cacheKey, out var cachedSchema)) { return cachedSchema; } var lastWriteTimeUtc = File.GetLastWriteTimeUtc(schemaPath); var schema = await YamlConfigSchemaValidator.LoadAsync(tableName, schemaPath, cancellationToken) .ConfigureAwait(false); CacheSchema(cacheKey, lastWriteTimeUtc, schema); return schema; } /// /// 创建 schema 缓存键,并提前执行与公开入口一致的参数契约检查。 /// /// 所属配置表名称。 /// Schema 文件绝对路径。 /// 用于缓存查找的稳定键。 /// 为空白时抛出。 private static SchemaCacheKey CreateCacheKey( string tableName, string schemaPath) { if (string.IsNullOrWhiteSpace(tableName)) { throw new ArgumentException("Table name cannot be null or whitespace.", nameof(tableName)); } if (string.IsNullOrWhiteSpace(schemaPath)) { throw new ArgumentException("Schema path cannot be null or whitespace.", nameof(schemaPath)); } return new SchemaCacheKey(tableName, schemaPath); } /// /// 尝试命中当前 schema 文件版本对应的缓存项。 /// /// 缓存键。 /// 命中的 schema;未命中时为 。 /// 当缓存项仍与当前文件时间戳一致时返回 private static bool TryGetCachedSchema( SchemaCacheKey cacheKey, out YamlConfigSchema schema) { var lastWriteTimeUtc = File.GetLastWriteTimeUtc(cacheKey.SchemaPath); if (SchemaCache.TryGetValue(cacheKey, out var cacheEntry) && cacheEntry.LastWriteTimeUtc == lastWriteTimeUtc) { schema = cacheEntry.Schema; return true; } schema = null!; return false; } /// /// 使用读取前捕获的文件时间戳刷新 schema 缓存。 /// 这样即使 schema 在读取过程中发生变化,后续访问也会因时间戳变新而重新加载, /// 避免把“旧内容 + 新时间戳”写入缓存。 /// /// 缓存键。 /// 本次读取开始前捕获的 schema 文件修改时间。 /// 最新加载的 schema。 private static void CacheSchema( SchemaCacheKey cacheKey, DateTime lastWriteTimeUtc, YamlConfigSchema schema) { SchemaCache[cacheKey] = new SchemaCacheEntry(lastWriteTimeUtc, schema); } /// /// 表示一个 schema 缓存键。 /// /// 所属配置表名称。 /// Schema 文件绝对路径。 private readonly record struct SchemaCacheKey( string TableName, string SchemaPath); /// /// 表示一个带文件时间戳的 schema 缓存条目。 /// /// 加载时观察到的 schema 文件修改时间。 /// 已解析的 schema 模型。 private readonly record struct SchemaCacheEntry( DateTime LastWriteTimeUtc, YamlConfigSchema Schema); }