using System; using System.Collections; using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; using System.Text.RegularExpressions; using LiteDB; using Microsoft.Extensions.Logging; namespace Net.BITKit.Database {public static class LiteDbHelper { public static string GetReadableCollectionName() { var type = typeof(T); return SanitizeTypeName(type); } private static string SanitizeTypeName(Type type) { // 基础类型名 string name = type.Name; // 若为泛型,如 Dictionary 变为 Dictionary_String_Int if (type.IsGenericType) { name = type.Name.Substring(0, type.Name.IndexOf('`')) + "_" + string.Join("_", type.GetGenericArguments().Select(SanitizeTypeName)); } // 替换非法字符,保留 a-zA-Z0-9_$ name = Regex.Replace(name, @"[^a-zA-Z0-9_$]", "_"); // 开头不能是数字 if (!Regex.IsMatch(name, @"^[a-zA-Z$_]")) { name = "_" + name; } return name; } } public class LiteDbDictionary : IDictionary,IReadOnlyDictionary, IDisposable where TKey : notnull { // 内部的 LiteDB 数据库实例 private readonly LiteDatabase _db; // LiteDB 集合名称可以自定义,默认为 "kv" private readonly ILiteCollection _collection; // 内部使用的键值项(POCO类) private record KeyValueItem { // LiteDB 将此字段作为文档的 _id(主键) [BsonId] public TKey Key { get; set; } public TValue Value { get; set; } } public LiteDbDictionary(ILogger> logger=null) { /* _db = new LiteDatabase(":memory:"); _collection = _db.GetCollection(typeof(TValue).Name); _collection.EnsureIndex(x => x.Key); */ // 获取程序集名作为默认数据库名 var asmName = Assembly.GetEntryAssembly()?.GetName().Name ?? Assembly.GetExecutingAssembly().GetName().Name ?? "LiteDb"; // 优先使用环境变量控制根目录 var rootDir = Environment.GetEnvironmentVariable("DATA_DIR"); // 默认持久化目录(跨平台) if (string.IsNullOrWhiteSpace(rootDir)) { #if UNITY_5_3_OR_NEWER rootDir = Path.Combine(UnityEngine.Application.persistentDataPath, asmName); #else if (OperatingSystem.IsWindows()) rootDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), asmName); else rootDir = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), asmName); #endif } Directory.CreateDirectory(rootDir); // 确保目录存在 var dbFilePath = Path.Combine(rootDir, $"{asmName}.db"); var connectionString = $"Filename={dbFilePath};Connection=shared"; logger?.LogInformation($"LiteDbDictionary<{typeof(TKey).Name},{typeof(TValue).Name}> 数据库文件: {dbFilePath}"); _db = new LiteDatabase(connectionString); _collection = _db.GetCollection($"{LiteDbHelper.GetReadableCollectionName()}_{LiteDbHelper.GetReadableCollectionName()}"); _collection.EnsureIndex(x => x.Key); } /// /// 构造函数 /// connectionString 可以是磁盘文件(如 "Filename=MyData.db;Mode=Shared") /// 或内存数据库(如 ":memory:") /// /// 数据库连接字符串 /// 集合名称,默认 "kv" public LiteDbDictionary(string connectionString, string collectionName = "kv") { _db = new LiteDatabase(connectionString); _collection = _db.GetCollection(collectionName); // 确保对键生成索引 _collection.EnsureIndex(x => x.Key); } #region IDictionary 实现 public TValue this[TKey key] { get { var bsonId = _db.Mapper.Serialize(typeof(TKey), key); var item = _collection.FindById(bsonId); if (item == null) throw new KeyNotFoundException($"Key '{key}' not found."); return item.Value; } set { var item = new KeyValueItem { Key = key, Value = value }; _collection.Upsert(item); } } IEnumerable IReadOnlyDictionary.Keys => Keys; IEnumerable IReadOnlyDictionary.Values => Values; public ICollection Keys => _collection.FindAll().Select(x => x.Key).ToList(); public ICollection Values => _collection.FindAll().Select(x => x.Value).ToList(); public int Count => _collection.Count(); public bool IsReadOnly => false; public void Add(TKey key, TValue value) { if (ContainsKey(key)) throw new ArgumentException($"An element with the key '{key}' already exists."); _collection.Insert(new KeyValueItem { Key = key, Value = value }); } public bool ContainsKey(TKey key) { var bsonKey = _db.Mapper.Serialize(typeof(TKey), key); return _collection.FindById(bsonKey) != null; // ✅ 更安全 } public bool Remove(TKey key) { var bsonKey = _db.Mapper.Serialize(typeof(TKey), key); return _collection.Delete(bsonKey); } public bool TryGetValue(TKey key, out TValue value) { var bsonKey = _db.Mapper.Serialize(typeof(TKey), key); var doc = _collection.FindById(bsonKey); if (doc == null) { value = default; return false; } value = doc.Value; return true; } public void Add(KeyValuePair item) { Add(item.Key, item.Value); } public void Clear() { _collection.DeleteAll(); } public bool Contains(KeyValuePair item) { if (TryGetValue(item.Key, out var val)) return EqualityComparer.Default.Equals(val, item.Value); return false; } public void CopyTo(KeyValuePair[] array, int arrayIndex) { foreach (var kv in this) { array[arrayIndex++] = kv; } } public bool Remove(KeyValuePair item) { if (Contains(item)) return Remove(item.Key); return false; } public IEnumerator> GetEnumerator() { foreach (var item in _collection.FindAll()) { yield return new KeyValuePair(item.Key, item.Value); } } IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); #endregion #region IDisposable 实现 public void Dispose() { _db?.Dispose(); } #endregion } }