SqlSugar ORM
本页系统性介绍 Ape‑Volo‑Admin 中的 SqlSugar ORM 配置,帮助你快速理解多库、读写分离、实体映射、缓存、查询过滤器与 SQL 日志等能力如何落地。
你将获得
- 如何注册 SqlSugar 并加载多数据源配置
- CQRS 场景下的主从(读写分离)与从库权重
- 实体映射规则(下划线命名、可空性、MySQL longtext)
- 缓存策略与“写入即失效”
- 全局查询过滤器:软删除/多租户/数据权限
- AOP:写入前实体补全、SQL 日志记录、慢 SQL 告警与 MiniProfiler 集成
一、服务注册与配置来源
入口方法:AddSqlSugarService(this IServiceCollection services)
- 读取配置:
SystemOptions:MasterDataBase、LogDataBase、UseRedisCache、IsCqrs、IsQuickDebugDataConnectionOptions.ConnectionItem:连接列表(ConnId、DbType、ConnectionString、Enabled、HitRate)
- 校验:
- 必须存在至少一个启用的连接;
- 必须包含指定的主库
MasterDataBase与日志库LogDataBase; - SQLite 连接会自动拼接为站点相对路径:
DataSource=<ContentRootPath>/<file>。
- 构造
SqlSugarScope:为每个启用的连接创建ConnectionConfig,并注册为单例ISqlSugarClient。
示例(在 Startup/Program 中注册):
csharp
services.AddSqlSugarService();二、CQRS:读写分离(主从)
当 SystemOptions.IsCqrs == true:
- 会为“每个主库连接”自动收集同类型(DbType 相同)、不同 ConnId 的启用连接作为从库;
- 若未找到从库,直接抛错提示需要至少一个从库;
- 从库以
SlaveConnectionConfig形式挂载到主库,支持HitRate(访问权重)。
注意:
- 日志库(
LogDataBase)不会参与后续的 AOP 过滤与拦截(避免影响日志写入)。
三、实体映射与 Code-First 细节
在 ConfigureExternalServices 中:
- 命名规则:将实体属性名按“驼峰转下划线”生成列名:
UtilMethods.ToUnderLine; - MySQL 兼容:若数据类型识别为
varchar(max),强制改为longtext; - 可空性:通过
NullabilityInfoContext检测可写状态为可空时,设置列为IsNullable = true; - 其它:
SqlServerCodeFirstNvarchar = true,使 SQL Server 默认使用nvarchar。
四、缓存策略
MoreSettings.IsAutoRemoveDataCache = true:写操作后自动清理相关数据缓存。
DataInfoCacheService 选择:
UseRedisCache = true时使用SqlSugarRedisCache(分布式 Redis 缓存);- 否则使用
SqlSugarDistributedCache(本地/分布式缓存抽象)。
五、全局查询过滤器
对“非日志库”的连接统一配置:
- 软删除过滤器
csharp
db.QueryFilter.AddTableFilter<ISoftDeletedEntity>(it => it.IsDeleted == false);仅查询未删除的数据,要求实体实现 ISoftDeletedEntity。
- 多租户过滤器(当前用户有租户时)
csharp
db.QueryFilter.AddTableFilter<ITenantEntity>(it => it.TenantId == App.HttpUser.TenantId);要求实体实现 ITenantEntity。
- 用户数据权限过滤器(放在最后)
csharp
// 从 IDataScopeService 取允许访问账号列表
// 按列表数量生成 CreateBy 的等值或 in 查询;为空则默认限制为当前账号
db.QueryFilter.AddTableFilter<ICreateByEntity>(...);异常兜底:一旦数据权限计算异常,记录致命日志并回退为“仅当前账号”。
六、AOP:实体写入前补全(DataExecuting)
挂接位置:sugarScopeProvider.Aop.DataExecuting = DataExecuting;
逻辑要点:
- 主键赋值:
RootKey<long>且Id=0时自动分配雪花 Id; - 公共字段:
BaseEntity/BaseEntityNoDataScope:新增时补齐CreateTime;更新时刷新UpdateTime;- 若存在登录用户:新增时补齐
CreateBy,更新时写入UpdateBy; - 若实体实现
ITenantEntity且当前有租户,新增时回填TenantId;
这可以统一保证审计字段与多租户字段完整、可靠。
七、SQL 日志与性能分析
挂接位置:
- 执行结束:
Aop.OnLogExecuted = (sql, pars) => OnLogExecuted(...)(已启用) - 执行中:
Aop.OnLogExecuting(示例代码中保留了注释,可按需开启)
行为说明(OnLogExecuted):
- 开关:
SerilogOptions.RecordSqlEnabled == true时生效; - 输出:包含“用户、操作类型、数据库 ID、完整 SQL、耗时(ms)”;
- 慢 SQL:当
ado.SqlExecutionTime.TotalMilliseconds > 10,以 Warning 输出并提示优化; - 集成:
- Serilog:使用
LoggerPropertyConfiguration.Create.AddAopSqlProperty写入结构化属性; - MiniProfiler:
IsQuickDebug && MiniProfiler.Enabled时展示 SQL 详情。
- Serilog:使用
示例输出(简化):
执行DB--> 操作用户:[admin] 操作类型:[Insertable] 数据库ID:[master] INSERT ... VALUES ... [耗时]:12ms建议:根据环境调整“慢 SQL 阈值”(默认 10ms 偏激进,生产可设为 50~200ms)。
八、为何排除日志库
allConnectionConfig.Where(x => x.ConfigId.ToString() != systemOptions.LogDataBase):
- 日志库通常只承担写入,避免在其上套用过滤器与 AOP,减少干扰,提升日志写入稳定性。
九、常见配置对照
SystemOptions:MasterDataBase:主库标识;LogDataBase:日志库标识;IsCqrs:开启主从;UseRedisCache:使用 Redis 缓存;IsQuickDebug:开发调试增强。
SerilogOptions:RecordSqlEnabled:是否记录 SQL 文本(配合 Serilog 输出开关 ToConsole/ToFile/ToDb 等)。
MiddlewareOptions:MiniProfiler.Enabled:是否启用 MiniProfiler。
DataConnectionOptions.ConnectionItem[*]:ConnId、DbType、ConnectionString、Enabled、HitRate(从库权重)。
十、FAQ
- 为什么没有看到 SQL 日志?
- 确认
SerilogOptions.RecordSqlEnabled = true; - 检查 Serilog 的输出开关(ToConsole/ToFile/ToDb/ToElasticsearch)是否开启;
OnLogExecuted已启用,若需“执行中”日志可开启OnLogExecuting。
- 软删除/多租户/数据权限没生效?
- 实体需分别实现
ISoftDeletedEntity、ITenantEntity、ICreateByEntity; - 当前登录上下文(
App.HttpUser)是否存在、字段是否完整(Account/TenantId)。
- 读写分离报错或未生效?
IsCqrs=true时必须配置至少一个从库(相同 DbType、不同 ConnId、Enabled=true);- 从库
HitRate可调节访问分配比例。
- 审计字段没有自动写入?
- 确认实体继承了
BaseEntity/BaseEntityNoDataScope或包含对应字段; - 写入时机是
Aop.DataExecuting,请确认没有绕过 ORM 直接执行 SQL。
- 为什么日志库不加过滤器?
- 避免对日志写入带来额外条件与性能影响;如确有需要,可在代码中单独处理日志库逻辑。
参考阅读:
- 《DB 仓储》

