C#项目数据存储方案纠结?试试用SqlSugar+JSON设计一个超灵活的通用存储接口(附SQLite/MySQL配置)

张开发
2026/4/6 6:59:56 15 分钟阅读

分享文章

C#项目数据存储方案纠结?试试用SqlSugar+JSON设计一个超灵活的通用存储接口(附SQLite/MySQL配置)
C#通用数据存储方案SqlSugarJSON的灵活架构实践在项目初期选择数据存储方案时开发者常常陷入关系型与文档型数据库的纠结。传统关系型数据库需要严格定义表结构而文档型数据库虽然灵活但可能缺乏事务支持。本文将介绍一种结合两者优势的混合方案——基于SqlSugar ORM和JSON序列化的通用存储接口它能以单一表结构存储任意C#对象特别适合快速原型开发、小型工具或数据结构频繁变化的场景。1. 为什么选择SqlSugarJSON方案SqlSugar作为一款轻量高效的国产ORM框架相比Entity Framework Core和Dapper在易用性和性能之间取得了良好平衡。其特色包括零配置快速上手无需复杂映射配置即可开始操作数据库多数据库支持同一套代码可适配SQLite、MySQL、SQL Server等高性能查询内置缓存机制和优化的SQL生成器简洁API设计链式调用让代码更易读易维护JSON序列化的优势在于结构自由无需预定义字段即可存储复杂对象语言中立数据可被各种平台和工具解析开发高效省去频繁修改表结构的麻烦典型适用场景包括配置信息存储用户偏好设置原型开发阶段数据结构频繁迭代的项目小型单机应用2. 核心架构设计2.1 通用表结构设计我们使用单一表结构存储所有类型的数据public class UniversalEntity { [SugarColumn(IsPrimaryKey true, IsIdentity true)] public long Id { get; set; } [SugarColumn(Length 50)] public string EntityType { get; set; } [SugarColumn(Length 100)] public string EntityId { get; set; } [SugarColumn(ColumnDataType text)] public string JsonData { get; set; } public DateTime ModifiedTime { get; set; } }字段说明字段名类型用途Idlong自增主键EntityTypestring实体类型标识EntityIdstring业务主键JsonDatastring序列化后的JSON数据ModifiedTimeDateTime最后修改时间2.2 基础仓储接口定义通用数据访问接口public interface IUniversalRepository { TaskT GetAsyncT(string entityId) where T : class; TaskListT GetAllAsyncT() where T : class; Task SaveAsyncT(T entity, string entityId) where T : class; Task DeleteAsyncT(string entityId) where T : class; Taskbool ExistsAsyncT(string entityId) where T : class; }3. 具体实现3.1 SqlSugar配置首先配置SqlSugarClientpublic class DatabaseConfig { public static SqlSugarScope GetInstance() { return new SqlSugarScope(new ConnectionConfig() { DbType DbType.Sqlite, ConnectionString DataSource./data.db, IsAutoCloseConnection true }, db { // 全局配置 db.Aop.OnLogExecuting (sql, pars) { Console.WriteLine(sql); }; }); } }3.2 仓储实现完整实现通用仓储public class UniversalRepository : IUniversalRepository { private readonly SqlSugarScope _db; private readonly JsonSerializerSettings _jsonSettings; public UniversalRepository() { _db DatabaseConfig.GetInstance(); _jsonSettings new JsonSerializerSettings { TypeNameHandling TypeNameHandling.Auto, NullValueHandling NullValueHandling.Ignore, ReferenceLoopHandling ReferenceLoopHandling.Ignore }; InitializeDatabase(); } private void InitializeDatabase() { _db.CodeFirst.InitTablesUniversalEntity(); } public async TaskT GetAsyncT(string entityId) where T : class { var entityType typeof(T).Name; var record await _db.QueryableUniversalEntity() .Where(x x.EntityType entityType x.EntityId entityId) .FirstAsync(); return record null ? null : JsonConvert.DeserializeObjectT(record.JsonData, _jsonSettings); } public async TaskListT GetAllAsyncT() where T : class { var entityType typeof(T).Name; var records await _db.QueryableUniversalEntity() .Where(x x.EntityType entityType) .ToListAsync(); return records.Select(x JsonConvert.DeserializeObjectT(x.JsonData, _jsonSettings)) .ToList(); } public async Task SaveAsyncT(T entity, string entityId) where T : class { var entityType typeof(T).Name; var json JsonConvert.SerializeObject(entity, _jsonSettings); var existing await _db.QueryableUniversalEntity() .Where(x x.EntityType entityType x.EntityId entityId) .FirstAsync(); if (existing ! null) { existing.JsonData json; existing.ModifiedTime DateTime.UtcNow; await _db.Updateable(existing).ExecuteCommandAsync(); } else { await _db.Insertable(new UniversalEntity { EntityType entityType, EntityId entityId, JsonData json, ModifiedTime DateTime.UtcNow }).ExecuteCommandAsync(); } } public async Task DeleteAsyncT(string entityId) where T : class { var entityType typeof(T).Name; await _db.DeleteableUniversalEntity() .Where(x x.EntityType entityType x.EntityId entityId) .ExecuteCommandAsync(); } public async Taskbool ExistsAsyncT(string entityId) where T : class { var entityType typeof(T).Name; return await _db.QueryableUniversalEntity() .Where(x x.EntityType entityType x.EntityId entityId) .AnyAsync(); } }4. 多数据库适配4.1 SQLite配置SQLite是本地开发的理想选择// SQLite配置示例 new ConnectionConfig() { DbType DbType.Sqlite, ConnectionString DataSource./appdata.db, InitKeyType InitKeyType.Attribute, IsAutoCloseConnection true }提示SQLite文件建议放在AppData等专用目录而非程序根目录4.2 MySQL配置生产环境常用MySQL// MySQL配置示例 new ConnectionConfig() { DbType DbType.MySql, ConnectionString Serverlocalhost;Databasemydb;Uidroot;Pwd123456;, InitKeyType InitKeyType.Attribute, IsAutoCloseConnection true }常见MySQL连接问题解决方案版本兼容性问题确保NuGet包MySql.Data与服务器版本匹配推荐使用MySqlConnector替代MySql.Data时区问题ConnectionString ;AllowZeroDateTimeTrue;ConvertZeroDateTimeTrue;SSL问题ConnectionString ;SslModePreferred;4.3 多数据库切换通过配置实现数据库切换public enum DatabaseType { Sqlite, MySql, SqlServer } public static SqlSugarScope GetDatabase(DatabaseType dbType, string connectionString) { return new SqlSugarScope(new ConnectionConfig() { DbType (DbType)dbType, ConnectionString connectionString, IsAutoCloseConnection true }); }5. 高级应用技巧5.1 性能优化处理大量数据时的优化策略// 批量插入优化 public async Task BulkInsertAsyncT(ListT entities, FuncT, string idSelector) { var entityType typeof(T).Name; var records entities.Select(x new UniversalEntity { EntityType entityType, EntityId idSelector(x), JsonData JsonConvert.SerializeObject(x, _jsonSettings), ModifiedTime DateTime.UtcNow }).ToList(); await _db.FastestUniversalEntity().BulkCopyAsync(records); }5.2 索引优化为常用查询字段添加索引// 在InitializeDatabase中添加 _db.CodeFirst.InitTablesUniversalEntity(); _db.DbMaintenance.CreateIndex(idx_entity_type_id, nameof(UniversalEntity), new[] { nameof(UniversalEntity.EntityType), nameof(UniversalEntity.EntityId) });5.3 数据迁移当需要从通用存储迁移到专用表时public async Task MigrateToSpecificTableT() where T : class, new() { var entities await GetAllAsyncT(); await _db.Insertable(entities).ExecuteCommandAsync(); }6. 实际应用案例6.1 配置管理系统public class AppConfigService { private readonly IUniversalRepository _repo; public AppConfigService(IUniversalRepository repo) { _repo repo; } public async TaskT GetConfigAsyncT(string configKey) where T : class, new() { return await _repo.GetAsyncT(configKey) ?? new T(); } public async Task SaveConfigAsyncT(string configKey, T config) { await _repo.SaveAsync(config, configKey); } }6.2 用户偏好存储public class UserPreference { public string Theme { get; set; } light; public int FontSize { get; set; } 14; public Liststring RecentProjects { get; set; } new(); } public class UserPreferenceService { private readonly IUniversalRepository _repo; public async TaskUserPreference GetPreferences(string userId) { return await _repo.GetAsyncUserPreference(userId) ?? new UserPreference(); } public async Task SavePreferences(string userId, UserPreference prefs) { await _repo.SaveAsync(prefs, userId); } }在实现这套通用存储方案的过程中最大的收获是理解了如何在结构化和灵活性之间寻找平衡点。对于数据结构稳定的核心业务仍然建议使用传统关系型设计而对于配置、临时数据和快速迭代的功能这种Json-based方案能显著提升开发效率。

更多文章