SQLSugar 封装原理详解:从架构到核心模块的底层实现

SQLSugar 封装原理详解:从架构到核心模块的底层实现

目录

​编辑

一、SQLSugar 整体架构设计:轻量 ORM 的分层与组件化

1.1 架构分层设计

1.1.1 核心组件依赖关系

1.2 核心类与接口设计

1.2.1 入口类:SqlSugarClient 与 ISqlSugarClient

1.2.2 适配接口:IDbAdapter(多数据库核心)

二、实体映射机制:ORM 的 “对象 - 关系” 桥梁封装

2.1 映射信息的结构封装

2.2 映射解析的封装流程

2.2.1 步骤 1:反射获取实体类信息

2.2.2 步骤 2:缓存映射结果

2.3 类型映射的适配封装

三、SQL 生成器:从 “对象操作” 到 “原生 SQL” 的封装

3.1 表达式树解析的封装逻辑

3.1.1 核心类:SqlExpressionVisitor

步骤 1:访问 Lambda 表达式根节点

步骤 2:处理 AndAlso 二元表达式(&&)

步骤 3:处理 GreaterThan 表达式(>)

步骤 4:处理 Contains 方法调用(u.Name.Contains ("张"))

3.2 分页 SQL 的适配封装

3.2.1 SQL Server 分页实现

3.2.2 MySQL 分页实现

3.3 批量操作的 SQL 优化封装

3.3.1 批量新增的 SQL 封装

四、连接管理:从 “创建” 到 “释放” 的生命周期封装

4.1 连接池的集成封装

4.2 连接生命周期的封装

4.2.1 自动释放(IsAutoCloseConnection=true,推荐)

4.2.2 手动释放(IsAutoCloseConnection=false)

4.3 多数据库连接的封装

五、事务机制:“原子性” 操作的封装实现

5.1 手动事务的封装

5.2 自动事务的封装(基于 AOP 与 IOC)

5.2.1 步骤 1:注册事务 AOP 服务

5.2.2 步骤 2:事务拦截器实现

5.2.3 步骤 3:使用[Transaction]特性

5.3 嵌套事务的封装(基于保存点)

六、AOP 模块:“无侵入” 扩展的封装

6.1 AOP 的委托链封装

6.2 AOP 的触发流程封装

6.3 AOP 的扩展场景

6.3.1 SQL 日志记录

6.3.2 性能监控

七、缓存机制:“减少数据库查询” 的封装实现

7.1 缓存 key 的生成封装

7.2 内存缓存的封装

7.3 分布式缓存的封装(以 Redis 为例)

7.4 缓存同步的封装(数据更新触发缓存清除)

八、代码生成器:“数据库结构→代码” 的自动化封装

8.1 数据库元数据解析的封装

8.2 实体类代码生成的封装

8.3 仓储类与服务类生成的封装

九、总结:SQLSugar 封装的设计思想与优势


一、SQLSugar 整体架构设计:轻量 ORM 的分层与组件化

SQLSugar 作为.NET 生态下的轻量级 ORM,其封装核心遵循 “分层解耦、组件化复用” 设计原则,整体架构分为基础设施层、核心功能层、应用适配层三层,各层通过接口定义边界,既保证核心逻辑的独立性,又支持灵活扩展。

1.1 架构分层设计

层级

核心职责

关键组件 / 接口

基础设施层

提供底层依赖能力(连接管理、反射缓存、序列化),为上层提供基础支撑

IDbConnection、ICacheService、反射工具类

核心功能层

实现 ORM 核心逻辑(实体映射、SQL 生成、事务、AOP),是封装的核心载体

SqlSugarClient、ISqlSugarClient、SQL 生成器、AOP 模块

应用适配层

适配不同应用场景(IOC 集成、多数据库、代码生成),降低上层使用成本

IocConfig、IDbAdapter、CodeGenerator

1.1.1 核心组件依赖关系

SQLSugar 的核心组件通过 “依赖注入 + 接口解耦” 组织,关键依赖链如下:

SqlSugarClient(入口)

├─ 依赖 ConnectionConfig(配置)

├─ 依赖 IDbAdapter(数据库适配)

├─ 依赖 ICacheService(缓存)

├─ 依赖 AOP委托链(日志、错误处理)

└─ 依赖 实体映射缓存(反射结果)

这种设计确保各组件可独立替换(如替换缓存为 Redis、新增数据库适配),符合 “开闭原则”。

1.2 核心类与接口设计

1.2.1 入口类:SqlSugarClient 与 ISqlSugarClient

SQLSugar 通过ISqlSugarClient接口定义核心操作契约(CRUD、事务、查询构建),SqlSugarClient作为实现类,是用户交互的唯一入口。其封装关键点:

  • 构造函数封装:接收ConnectionConfig或List<ConnectionConfig>,内部初始化数据库适配器、连接池、映射缓存;
  • 接口隔离:将复杂操作拆解为子接口(如IQueryable<T>负责查询、IInsertable<T>负责新增),避免类膨胀;
  • 单例与多实例控制:非 IOC 场景下支持手动创建实例,IOC 场景下通过Scoped生命周期避免线程安全问题。
1.2.2 适配接口:IDbAdapter(多数据库核心)

为支持多数据库,SQLSugar 封装了IDbAdapter接口,定义数据库专属语法的适配契约,不同数据库通过实现该接口完成差异化适配。接口核心方法:

public interface IDbAdapter

{

// 生成分页SQL

string GetPageSql(string sql, long skip, long take, string orderBy);

// 获取当前时间函数(如GETDATE()、NOW())

string GetDateFunc();

// 处理参数前缀(如@、:)

string GetParamPrefix();

// 字段类型映射(数据库类型→C#类型)

Type GetCSharpType(string dbType);

}

例如:

  • SqlServerAdapter实现GetPageSql生成ROW_NUMBER()分页;
  • MySqlAdapter实现GetPageSql生成LIMIT分页;
  • OracleAdapter实现GetParamPrefix返回:(Oracle 参数前缀)。

SqlSugarClient 初始化时,根据ConnectionConfig.DbType自动选择对应的IDbAdapter实例,确保 SQL 语法适配。

二、实体映射机制:ORM 的 “对象 - 关系” 桥梁封装

实体映射是 ORM 的核心,SQLSugar 通过 “反射解析 + 缓存优化” 封装实体类与数据库表的映射关系,避免重复反射开销,同时支持特性与约定两种映射方式。

2.1 映射信息的结构封装

SQLSugar 将实体映射信息封装为EntityInfo和ColumnInfo类,分别对应 “表” 和 “字段” 的映射元数据:

// 表映射元数据

public class EntityInfo

{

public Type EntityType { get; set; } // 实体类类型

public string TableName { get; set; } // 数据库表名

public List<ColumnInfo> Columns { get; set; } // 字段列表

public ColumnInfo PrimaryKey { get; set; } // 主键字段

}

// 字段映射元数据

public class ColumnInfo

{

public string ColumnName { get; set; } // 数据库字段名

public string PropertyName { get; set; } // 实体类属性名

public Type PropertyType { get; set; } // 属性类型

public bool IsPrimaryKey { get; set; } // 是否主键

public bool IsIdentity { get; set; } // 是否自增

public bool IsNullable { get; set; } // 是否允许为NULL

public int Length { get; set; } // 字段长度(字符串)

}

这些元数据是后续 SQL 生成、参数绑定的基础。

2.2 映射解析的封装流程

SQLSugar 在首次使用实体类(如Queryable<User>())时,触发映射解析,流程如下:

2.2.1 步骤 1:反射获取实体类信息

通过Type.GetTypeInfo()反射实体类,获取类特性([SugarTable])和属性信息:

// 简化代码:解析表名

var sugarTableAttr = entityType.GetCustomAttribute<SugarTableAttribute>();

var tableName = sugarTableAttr?.TableName ?? entityType.Name; // 约定:类名→表名

// 解析属性

foreach (var prop in entityType.GetProperties(BindingFlags.Public | BindingFlags.Instance))

{

var columnInfo = new ColumnInfo

{

PropertyName = prop.Name,

PropertyType = prop.PropertyType

};

// 解析字段特性([SugarColumn])

var sugarColumnAttr = prop.GetCustomAttribute<SugarColumnAttribute>();

if (sugarColumnAttr != null)

{

columnInfo.ColumnName = sugarColumnAttr.ColumnName ?? prop.Name;

columnInfo.IsPrimaryKey = sugarColumnAttr.IsPrimaryKey;

columnInfo.IsIdentity = sugarColumnAttr.IsIdentity;

columnInfo.IsNullable = sugarColumnAttr.IsNullable;

columnInfo.Length = sugarColumnAttr.Length;

}

// 约定:若未指定主键,默认“Id”或“XXXId”为PK

if (!columnInfo.IsPrimaryKey &&

(prop.Name.Equals("Id", StringComparison.OrdinalIgnoreCase) ||

prop.Name.EndsWith("Id", StringComparison.OrdinalIgnoreCase)))

{

columnInfo.IsPrimaryKey = true;

// 约定:整数类型主键默认自增

if (prop.PropertyType.IsValueType && !prop.PropertyType.IsNullableType())

{

columnInfo.IsIdentity = true;

}

}

entityInfo.Columns.Add(columnInfo);

}

2.2.2 步骤 2:缓存映射结果

为避免重复反射(反射性能开销较大),SQLSugar 将解析后的EntityInfo缓存到静态字典ConcurrentDictionary<Type, EntityInfo>中,key 为实体类Type,后续使用该实体类时直接从缓存获取:

private static readonly ConcurrentDictionary<Type, EntityInfo> _entityCache = new();

public EntityInfo GetEntityInfo(Type entityType)

{

if (_entityCache.TryGetValue(entityType, out var entityInfo))

{

return entityInfo;

}

// 解析映射信息

entityInfo = ParseEntityInfo(entityType);

// 缓存到字典

_entityCache.TryAdd(entityType, entityInfo);

return entityInfo;

}

这一封装使映射解析的性能开销仅发生一次,显著提升后续操作效率。

2.3 类型映射的适配封装

不同数据库的字段类型与 C# 类型存在差异(如 SQL Server 的nvarchar对应 C# 的string,MySQL 的int对应 C# 的int),SQLSugar 通过IDbAdapter封装类型映射逻辑:

// MySqlAdapter类型映射实现

public Type GetCSharpType(string dbType)

{

return dbType.ToLower() switch

{

"varchar" or "char" or "text" => typeof(string),

"int" or "tinyint" => typeof(int),

"bigint" => typeof(long),

"decimal" => typeof(decimal),

"datetime" => typeof(DateTime),

_ => typeof(object)

};

}

在代码生成器或反向映射(数据库表→实体类)时,通过该方法自动匹配 C# 类型,避免手动转换错误。

三、SQL 生成器:从 “对象操作” 到 “原生 SQL” 的封装

SQL 生成是 SQLSugar 的核心能力之一,其通过 “表达式树解析 + 数据库适配”,将 Lambda 表达式(如u => u.Age > 20)转化为适配目标数据库的参数化 SQL,同时避免 SQL 注入。

3.1 表达式树解析的封装逻辑

SQLSugar 的Queryable<T>、Updateable<T>等操作类,底层依赖ExpressionVisitor(表达式访问器)解析 Lambda 表达式,将其转化为 SQL 条件、字段列表等片段。

3.1.1 核心类:SqlExpressionVisitor

SqlExpressionVisitor继承自ExpressionVisitor,重写表达式树节点的访问方法(如VisitBinary处理二元表达式、VisitMember处理成员访问),逐步构建 SQL 片段和参数列表。

以解析u => u.Age > 20 && u.Name.Contains("张")为例,解析流程如下:

步骤 1:访问 Lambda 表达式根节点

public override Expression VisitLambda<T>(Expression<T> node)

{

// 访问Lambda体(此处为BinaryExpression:AndAlso)

return Visit(node.Body);

}

步骤 2:处理 AndAlso 二元表达式(&&)

protected override Expression VisitBinary(BinaryExpression node)

{

if (node.NodeType == ExpressionType.AndAlso)

{

// 递归访问左节点(u.Age > 20)和右节点(u.Name.Contains("张"))

var leftSql = Visit(node.Left);

var rightSql = Visit(node.Right);

// 拼接SQL条件:左条件 AND 右条件

_sqlBuilder.Append($"({leftSql} AND {rightSql})");

}

// 其他二元表达式(如GreaterThan、Equal)处理逻辑...

return node;

}

步骤 3:处理 GreaterThan 表达式(>)

protected override Expression VisitBinary(BinaryExpression node)

{

if (node.NodeType == ExpressionType.GreaterThan)

{

// 访问左操作数(u.Age,成员访问)

var left = Visit(node.Left) as string;

// 访问右操作数(20,常量)

var right = Visit(node.Right) as object;

// 生成参数(避免SQL注入)

var paramName = $"@{_paramCounter++}";

_parameters.Add(new SugarParameter(paramName, right));

// 拼接条件:[Age] > @0

_sqlBuilder.Append($"{left} > {paramName}");

}

// ...

return node;

}

步骤 4:处理 Contains 方法调用(u.Name.Contains ("张"))

protected override Expression VisitMethodCall(MethodCallExpression node)

{

if (node.Method.Name == nameof(string.Contains))

{

// 访问实例(u.Name)

var member = Visit(node.Object) as string;

// 访问参数("张")

var value = Visit(node.Arguments[0]) as object;

// 生成参数:@1 = '%张%'

var paramName = $"@{_paramCounter++}";

_parameters.Add(new SugarParameter(paramName, $"%{value}%"));

// 拼接Like条件:[Name] LIKE @1

_sqlBuilder.Append($"{member} LIKE {paramName}");

}

// ...

return node;

}

最终生成的 SQL 条件的是:([Age] > @0 AND [Name] LIKE @1),参数列表为@0=20、@1='%张%',既完成了表达式解析,又通过参数化避免了 SQL 注入。

3.2 分页 SQL 的适配封装

不同数据库的分页语法差异较大(如 SQL Server 用ROW_NUMBER(),MySQL 用LIMIT),SQLSugar 通过IDbAdapter.GetPageSql封装分页逻辑,实现 “一套代码,多库适配”。

3.2.1 SQL Server 分页实现

public string GetPageSql(string sql, long skip, long take, string orderBy)

{

// 原始SQL:SELECT * FROM Users WHERE Age > 20

// 生成ROW_NUMBER()子查询

var rowSql = $"SELECT *, ROW_NUMBER() OVER (ORDER BY {orderBy}) AS RowNum FROM ({sql}) AS Temp";

// 外层筛选分页范围

var pageSql = $"SELECT * FROM ({rowSql}) AS Temp WHERE RowNum > {skip} AND RowNum <= {skip + take}";

return pageSql;

}

3.2.2 MySQL 分页实现

public string GetPageSql(string sql, long skip, long take, string orderBy)

{

// 直接拼接LIMIT:SELECT * FROM Users WHERE Age > 20 ORDER BY RegisterTime DESC LIMIT 10,10

return $"{sql} ORDER BY {orderBy} LIMIT {skip},{take}";

}

在ToPageList方法中,SQLSugar 先生成基础 SQL,再调用当前IDbAdapter的GetPageSql生成适配分页 SQL,无需用户关注数据库差异。

3.3 批量操作的 SQL 优化封装

批量新增、修改等操作若循环执行单条 SQL,会产生大量数据库交互开销。SQLSugar 通过 “批量 SQL 拼接 + 参数复用” 封装优化,减少交互次数。

3.3.1 批量新增的 SQL 封装

以 SQL Server 为例,批量新增时生成VALUES多行 SQL:

// 批量新增用户列表

public string GenerateBatchInsertSql(List<User> users, EntityInfo entityInfo)

{

var columns = string.Join(",", entityInfo.Columns.Where(c => !c.IsIdentity).Select(c => $"[{c.ColumnName}]"));

var paramBuilder = new StringBuilder();

var valueBuilder = new StringBuilder();

int paramIndex = 0;

foreach (var user in users)

{

var values = new List<string>();

foreach (var column in entityInfo.Columns.Where(c => !c.IsIdentity))

{

var prop = entityInfo.EntityType.GetProperty(column.PropertyName);

var value = prop.GetValue(user);

var paramName = $"@{paramIndex++}";

paramBuilder.Append($"{paramName}={value},");

values.Add(paramName);

}

valueBuilder.Append($"({string.Join(",", values)}),");

}

// 生成SQL:INSERT INTO Users ([UserName],[Age],[Gender]) VALUES (@0,@1,@2),(@3,@4,@5)

var sql = $"INSERT INTO [{entityInfo.TableName}] ({columns}) VALUES {valueBuilder.ToString().TrimEnd(',')}";

return sql;

}

这种封装将 N 条新增操作转化为 1 条 SQL,数据库交互次数从 N 次减少到 1 次,显著提升性能。

四、连接管理:从 “创建” 到 “释放” 的生命周期封装

SQLSugar 基于ADO.NET连接池,封装了连接的 “获取 - 使用 - 释放” 生命周期,避免连接泄露,同时支持多数据库连接管理。

4.1 连接池的集成封装

SQLSugar 默认使用ADO.NET内置连接池(无需额外配置),连接池的核心参数(如最大连接数、连接超时)通过连接字符串配置:

// SQL Server连接字符串(配置连接池)

Server=localhost;Database=TestDB;Uid=sa;Pwd=123456;Max Pool Size=100;Connect Timeout=30;

  • Max Pool Size:连接池最大连接数(默认 100);
  • Connect Timeout:连接超时时间(默认 15 秒)。

SQLSugar 不重复造轮子,而是基于原生连接池优化连接获取逻辑,确保连接复用率。

4.2 连接生命周期的封装

SQLSugar 通过IsAutoCloseConnection配置,封装连接的自动释放逻辑,生命周期如下:

4.2.1 自动释放(IsAutoCloseConnection=true,推荐)

// 简化代码:执行查询时的连接管理

public List<T> Query<T>(string sql, List<SugarParameter> pars)

{

IDbConnection conn = null;

try

{

// 1. 从连接池获取连接

conn = GetConnection();

// 2. 执行SQL

var result = conn.Query<T>(sql, pars).ToList();

return result;

}

catch (Exception ex)

{

// 触发AOP错误事件

_aop.OnError?.Invoke(sql, pars, ex);

throw;

}

finally

{

// 3. 自动释放连接(归还到连接池,非关闭物理连接)

if (IsAutoCloseConnection && conn != null && conn.State == ConnectionState.Open)

{

conn.Close();

}

}

}

关键:conn.Close()并非关闭物理连接,而是将连接归还到连接池,供后续操作复用,减少连接创建的开销。

4.2.2 手动释放(IsAutoCloseConnection=false)

适用于长事务场景,需用户手动调用Close()或Dispose()释放连接:

// 手动管理连接

using (var conn = db.Ado.GetConnection())

{

conn.Open();

// 执行多步操作(事务)

db.Ado.BeginTran();

// ...

db.Ado.CommitTran();

// using结束后自动Dispose,释放连接

}

通过using语句确保连接最终释放,避免泄露。

4.3 多数据库连接的封装

当项目需操作多个数据库时,SQLSugar 通过ConfigId标识不同连接配置,封装多连接管理:

// 内部维护多连接配置字典

private readonly ConcurrentDictionary<string, ConnectionConfig> _connectionConfigs = new();

// 添加多连接配置

public void AddConnectionConfig(ConnectionConfig config)

{

_connectionConfigs.TryAdd(config.ConfigId, config);

}

// 根据ConfigId获取连接

public IDbConnection GetConnection(string configId = "MainDB")

{

if (!_connectionConfigs.TryGetValue(configId, out var config))

{

throw new Exception($"未找到ConfigId为{configId}的连接配置");

}

// 根据数据库类型创建连接实例

IDbConnection conn = config.DbType switch

{

DbType.SqlServer => new SqlConnection(config.ConnectionString),

DbType.MySql => new MySqlConnection(config.ConnectionString),

DbType.Oracle => new OracleConnection(config.ConnectionString),

_ => throw new Exception("不支持的数据库类型")

};

return conn;

}

用户通过db.GetConnection("MySqlDB")即可切换到指定数据库,无需重新创建SqlSugarClient实例。

五、事务机制:“原子性” 操作的封装实现

SQLSugar 封装了手动事务与自动事务两种模式,确保一组数据库操作的原子性(要么全成功,要么全回滚),同时支持嵌套事务。

5.1 手动事务的封装

手动事务基于ADO.NET的IDbTransaction,封装事务的 “开启 - 提交 - 回滚” 流程:

// 内部维护当前事务

private IDbTransaction _currentTransaction;

// 开启事务

public void BeginTran()

{

var conn = GetConnection();

if (conn.State != ConnectionState.Open)

{

conn.Open();

}

// 创建事务实例

_currentTransaction = conn.BeginTransaction();

// 绑定事务到后续操作(确保后续SQL使用同一事务)

_isInTransaction = true;

}

// 提交事务

public void CommitTran()

{

if (_currentTransaction != null)

{

_currentTransaction.Commit();

// 释放事务和连接

_currentTransaction.Dispose();

_currentTransaction = null;

_isInTransaction = false;

if (IsAutoCloseConnection)

{

GetConnection().Close();

}

}

}

// 回滚事务

public void RollbackTran()

{

if (_currentTransaction != null)

{

_currentTransaction.Rollback();

_currentTransaction.Dispose();

_currentTransaction = null;

_isInTransaction = false;

if (IsAutoCloseConnection)

{

GetConnection().Close();

}

}

}

关键:开启事务后,后续所有 SQL 操作会自动绑定到_currentTransaction,确保在同一事务上下文执行。

5.2 自动事务的封装(基于 AOP 与 IOC)

ASP.NET Core 等 IOC 场景下,SQLSugar 通过[Transaction]特性 + AOP 拦截,实现事务自动管理,封装流程如下:

5.2.1 步骤 1:注册事务 AOP 服务

// Program.cs中注册事务拦截器

builder.Services.AddScoped<TransactionInterceptor>();

// 配置AOP拦截控制器方法

builder.Services.AddControllers(options =>

{

options.Filters.Add<TransactionInterceptor>();

});

5.2.2 步骤 2:事务拦截器实现

public class TransactionInterceptor : IActionFilter

{

private readonly ISqlSugarClient _db;

public TransactionInterceptor(ISqlSugarClient db)

{

_db = db;

}

// 方法执行前开启事务

public void OnActionExecuting(ActionExecutingContext context)

{

// 判断方法是否标记[Transaction]特性

var hasTransactionAttr = context.ActionDescriptor.EndpointMetadata

.Any(em => em is TransactionAttribute);

if (hasTransactionAttr)

{

_db.Ado.BeginTran();

// 将事务状态存入上下文,供执行后使用

context.HttpContext.Items["IsInTransaction"] = true;

}

}

// 方法执行后提交/回滚

public void OnActionExecuted(ActionExecutedContext context)

{

if (context.HttpContext.Items.TryGetValue("IsInTransaction", out var isInTransaction) &&

(bool)isInTransaction)

{

if (context.Exception == null)

{

// 无异常,提交事务

_db.Ado.CommitTran();

}

else

{

// 有异常,回滚事务

_db.Ado.RollbackTran();

}

}

}

}

5.2.3 步骤 3:使用[Transaction]特性

[HttpPost("AddUserAndOrder")]

[Transaction] // 自动开启事务

public IActionResult AddUserAndOrder(User user, Order order)

{

_db.Insertable(user).ExecuteReturnIdentity();

_db.Insertable(order).ExecuteCommand();

return Ok();

}

这种封装使开发者无需手动编写事务代码,只需添加特性即可确保方法内所有操作的原子性。

5.3 嵌套事务的封装(基于保存点)

SQLSugar 通过数据库 “保存点(Savepoint)” 封装嵌套事务,支持内层事务独立回滚,而不影响外层事务:

// 开启嵌套事务(创建保存点)

public void BeginNestedTran(string savepointName)

{

if (_currentTransaction == null)

{

throw new Exception("需先开启外层事务");

}

// 创建保存点

_currentTransaction.Save(savepointName);

}

// 回滚嵌套事务(回滚到保存点)

public void RollbackNestedTran(string savepointName)

{

if (_currentTransaction != null)

{

_currentTransaction.Rollback(savepointName);

}

}

示例:

db.Ado.BeginTran(); // 外层事务

try

{

db.Insertable(user1).ExecuteCommand(); // 操作1

db.Ado.BeginNestedTran("Savepoint1"); // 开启嵌套事务

try

{

db.Insertable(user2).ExecuteCommand(); // 操作2

throw new Exception("嵌套事务出错");

db.Ado.CommitTran(); // 不会执行

}

catch

{

db.Ado.RollbackNestedTran("Savepoint1"); // 仅回滚操作2

}

db.Ado.CommitTran(); // 提交操作1

}

catch

{

db.Ado.RollbackTran(); // 回滚操作1

}

最终仅user1插入成功,user2被回滚,实现嵌套事务的灵活控制。

六、AOP 模块:“无侵入” 扩展的封装

SQLSugar 通过 AOP(面向切面编程)封装 SQL 执行的拦截能力,支持日志记录、性能监控、错误处理等扩展,且不侵入核心业务代码。

6.1 AOP 的委托链封装

SQLSugar 的 AOP 基于 “委托链” 实现,定义了 SQL 执行各阶段的委托事件:

public class AopConfig

{

// SQL执行前触发(可修改SQL和参数)

public Action<string, List<SugarParameter>> OnLogExecuting { get; set; }

// SQL执行后触发(可获取执行时间、影响行数)

public Action<string, List<SugarParameter>, long> OnLogExecuted { get; set; }

// SQL执行出错时触发

public Action<string, List<SugarParameter>, Exception> OnError { get; set; }

}

这些委托通过 “多播委托” 支持多个方法注册,例如同时注册日志记录和性能监控:

db.Aop.OnLogExecuting += (sql, pars) => Console.WriteLine(sql); // 日志

db.Aop.OnLogExecuting += (sql, pars) => _performanceMonitor.Start(sql); // 性能监控

6.2 AOP 的触发流程封装

在 SQL 执行的关键节点,SQLSugar 触发对应的 AOP 委托,流程如下:

public int ExecuteNonQuery(string sql, List<SugarParameter> pars)

{

long startTime = DateTime.Now.Ticks;

try

{

// 1. 执行前触发OnLogExecuting

_aop.OnLogExecuting?.Invoke(sql, pars);

var conn = GetConnection();

var cmd = CreateCommand(conn, sql, pars);

int rows = cmd.ExecuteNonQuery();

// 2. 执行后触发OnLogExecuted(计算执行时间)

long executeTime = (DateTime.Now.Ticks - startTime) / 10000; // 毫秒

_aop.OnLogExecuted?.Invoke(sql, pars, executeTime);

return rows;

}

catch (Exception ex)

{

// 3. 出错时触发OnError

_aop.OnError?.Invoke(sql, pars, ex);

throw;

}

}

这种封装确保 AOP 逻辑与核心 SQL 执行逻辑解耦,开发者可按需扩展,无需修改 SQLSugar 源码。

6.3 AOP 的扩展场景

6.3.1 SQL 日志记录

db.Aop.OnLogExecuting = (sql, pars) =>

{

// 记录SQL到日志文件

LogHelper.Info($"SQL: {sql}");

LogHelper.Info($"Params: {string.Join(",", pars.Select(p => $"{p.ParameterName}={p.Value}"))}");

};

6.3.2 性能监控

db.Aop.OnLogExecuting += (sql, pars) =>

{

// 记录SQL开始执行时间

HttpContext.Current.Items[$"SqlStartTime_{sql.GetHashCode()}"] = DateTime.Now;

};

db.Aop.OnLogExecuted += (sql, pars, executeTime) =>

{

// 输出执行时间(毫秒)

Console.WriteLine($"SQL执行时间:{executeTime}ms");

// 若执行时间过长,告警

if (executeTime > 500)

{

LogHelper.Warn($"SQL执行超时:{sql},耗时:{executeTime}ms");

}

};

七、缓存机制:“减少数据库查询” 的封装实现

SQLSugar 封装了多级缓存能力(内存缓存、分布式缓存如 Redis),通过 “缓存 key 生成 + 过期策略 + 缓存同步”,减少重复数据库查询,提升性能。

7.1 缓存 key 的生成封装

为避免缓存冲突,SQLSugar 根据 “查询标识 + 参数” 生成唯一缓存 key,核心逻辑:

public string GenerateCacheKey(string sql, List<SugarParameter> pars)

{

// 1. 拼接SQL和参数(参数按顺序排序,确保相同参数生成相同字符串)

var paramStr = string.Join(",", pars.OrderBy(p => p.ParameterName)

.Select(p => $"{p.ParameterName}={p.Value ?? "null"}"));

// 2. 计算MD5哈希(避免key过长)

using var md5 = MD5.Create();

var bytes = Encoding.UTF8.GetBytes($"{sql}_{paramStr}");

var hash = md5.ComputeHash(bytes);

// 3. 转化为16进制字符串作为key

return BitConverter.ToString(hash).Replace("-", "").ToLower();

}

例如:

  • SQL:SELECT * FROM Users WHERE Age > @0;
  • 参数:@0=20;
  • 生成 key:a3f2d4e5b6c7a8b9c0d1e2f3a4b5c6d7。

7.2 内存缓存的封装

SQLSugar 默认使用内存缓存(基于ConcurrentDictionary),封装缓存的 “添加 - 获取 - 删除” 操作:

public class MemoryCacheService : ICacheService

{

// 缓存字典(key:缓存key,value:缓存项)

private readonly ConcurrentDictionary<string, CacheItem> _cache = new();

// 过期清理定时器(定期清理过期缓存)

private readonly Timer _expireTimer;

public MemoryCacheService()

{

// 每30秒清理一次过期缓存

_expireTimer = new Timer(RemoveExpiredCache, null, 0, 30000);

}

// 添加缓存(支持绝对过期)

public void Add<V>(string key, V value, int expireSeconds)

{

var cacheItem = new CacheItem

{

Value = value,

ExpireTime = DateTime.Now.AddSeconds(expireSeconds)

};

_cache.AddOrUpdate(key, cacheItem, (k, old) => cacheItem);

}

// 获取缓存

public V Get<V>(string key)

{

if (_cache.TryGetValue(key, out var cacheItem))

{

// 检查是否过期

if (cacheItem.ExpireTime > DateTime.Now)

{

return (V)cacheItem.Value;

}

// 过期则删除

_cache.TryRemove(key, out _);

}

return default;

}

// 删除缓存

public void Remove(string key)

{

_cache.TryRemove(key, out _);

}

// 清理过期缓存

private void RemoveExpiredCache(object state)

{

var expiredKeys = _cache.Where(kv => kv.Value.ExpireTime <= DateTime.Now)

.Select(kv => kv.Key)

.ToList();

foreach (var key in expiredKeys)

{

_cache.TryRemove(key, out _);

}

}

// 缓存项类(包含值和过期时间)

private class CacheItem

{

public object Value { get; set; }

public DateTime ExpireTime { get; set; }

}

}

7.3 分布式缓存的封装(以 Redis 为例)

通过ICacheService接口解耦,SQLSugar 支持自定义分布式缓存(如 Redis),封装逻辑:

public class RedisCacheService : ICacheService

{

private readonly IDistributedCache _redisCache;

private readonly JsonSerializerOptions _jsonOptions;

public RedisCacheService(IDistributedCache redisCache)

{

_redisCache = redisCache;

_jsonOptions = new JsonSerializerOptions { PropertyNameCaseInsensitive = true };

}

// 添加缓存

public void Add<V>(string key, V value, int expireSeconds)

{

// 序列化(JSON)

var json = JsonSerializer.Serialize(value, _jsonOptions);

// 设置Redis缓存(绝对过期)

_redisCache.SetString(key, json, new DistributedCacheEntryOptions

{

AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(expireSeconds)

});

}

// 获取缓存

public V Get<V>(string key)

{

var json = _redisCache.GetString(key);

if (string.IsNullOrEmpty(json))

{

return default;

}

// 反序列化

return JsonSerializer.Deserialize<V>(json, _jsonOptions);

}

// 删除缓存

public void Remove(string key)

{

_redisCache.Remove(key);

}

}

在 IOC 中注册 Redis 缓存:

// Program.cs

builder.Services.AddStackExchangeRedisCache(options =>

{

options.Configuration = "localhost:6379"; // Redis连接字符串

});

// 注入Redis缓存服务

builder.Services.AddScoped<ICacheService, RedisCacheService>();

// SQLSugar使用Redis缓存

builder.Services.AddSqlSugar(options =>

{

options.ConnectionConfigs = new List<ConnectionConfig> { /* 连接配置 */ };

options.CacheService = new RedisCacheService(builder.Services.BuildServiceProvider().GetRequiredService<IDistributedCache>());

});

7.4 缓存同步的封装(数据更新触发缓存清除)

当数据发生修改(Update/Delete/Insert)时,SQLSugar 自动清除相关缓存,避免缓存脏数据。核心逻辑:

// 执行Update操作后,清除相关缓存

public int Update<T>(T entity)

{

var entityInfo = GetEntityInfo(typeof(T));

var rows = ExecuteNonQuery(GenerateUpdateSql(entity), GenerateUpdateParams(entity));

// 清除该表的所有缓存(简化逻辑,实际可更精细)

var tableCacheKeys = _cacheService.GetAllKeys().Where(key => key.Contains(entityInfo.TableName)).ToList();

foreach (var key in tableCacheKeys)

{

_cacheService.Remove(key);

}

return rows;

}

更精细的缓存同步可通过 AOP 实现,例如根据修改的主键值,清除包含该主键的查询缓存。

八、代码生成器:“数据库结构→代码” 的自动化封装

SQLSugar 代码生成器通过 “数据库元数据解析 + 代码模板拼接”,自动生成实体类、仓储类、服务类,减少重复编码。

8.1 数据库元数据解析的封装

代码生成器首先连接数据库,通过 “信息_schema” 视图查询表、字段、主键等元数据:

// 查询表列表(SQL Server)

public List<TableInfo> GetTables(string connectionString)

{

using var conn = new SqlConnection(connectionString);

var sql = @"

SELECT

TABLE_NAME AS TableName,

TABLE_COMMENT AS TableDescription

FROM INFORMATION_SCHEMA.TABLES

WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = 'dbo'

";

return conn.Query<TableInfo>(sql).ToList();

}

// 查询字段列表(SQL Server)

public List<ColumnInfo> GetColumns(string connectionString, string tableName)

{

using var conn = new SqlConnection(connectionString);

var sql = @"

SELECT

COLUMN_NAME AS ColumnName,

DATA_TYPE AS DbType,

IS_NULLABLE AS IsNullable,

CHARACTER_MAXIMUM_LENGTH AS Length,

COLUMN_COMMENT AS ColumnDescription,

COLUMN_DEFAULT AS DefaultValue

FROM INFORMATION_SCHEMA.COLUMNS

WHERE TABLE_NAME = @TableName AND TABLE_SCHEMA = 'dbo'

";

return conn.Query<ColumnInfo>(sql, new { TableName = tableName }).ToList();

}

// 查询主键(SQL Server)

public string GetPrimaryKey(string connectionString, string tableName)

{

using var conn = new SqlConnection(connectionString);

var sql = @"

SELECT COLUMN_NAME

FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE

WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + CONSTRAINT_NAME), 'IsPrimaryKey') = 1

AND TABLE_NAME = @TableName AND TABLE_SCHEMA = 'dbo'

";

return conn.QueryFirstOrDefault<string>(sql, new { TableName = tableName });

}

8.2 实体类代码生成的封装

根据解析的元数据,通过字符串拼接生成实体类代码,处理特性、注释等:

public string GenerateEntityCode(TableInfo table, List<ColumnInfo> columns, string primaryKey)

{

var codeBuilder = new StringBuilder();

// 命名空间

codeBuilder.AppendLine("using System;");

codeBuilder.AppendLine("using SqlSugar;");

codeBuilder.AppendLine();

// 表特性(包含描述)

codeBuilder.AppendLine($"[SugarTable(\"{table.TableName}\", \"{table.TableDescription}\")]");

codeBuilder.AppendLine($"public class {table.TableName}");

codeBuilder.AppendLine("{");

// 生成字段

foreach (var column in columns)

{

// 字段注释

if (!string.IsNullOrEmpty(column.ColumnDescription))

{

codeBuilder.AppendLine($" /// <summary>");

codeBuilder.AppendLine($" /// {column.ColumnDescription}");

codeBuilder.AppendLine($" /// </summary>");

}

// 字段特性

var attrBuilder = new StringBuilder();

attrBuilder.Append("[SugarColumn(");

// 主键特性

if (column.ColumnName == primaryKey)

{

attrBuilder.Append("IsPrimaryKey = true, ");

// 自增(整数类型默认自增)

if (IsIntType(column.DbType))

{

attrBuilder.Append("IsIdentity = true, ");

}

}

// 字段名(若与属性名一致,可省略)

if (column.ColumnName != column.ColumnName) // 实际逻辑:判断是否需要显式指定ColumnName

{

attrBuilder.Append($"ColumnName = \"{column.ColumnName}\", ");

}

// 可空特性

attrBuilder.Append($"IsNullable = {column.IsNullable.Equals("YES", StringComparison.OrdinalIgnoreCase).ToString().ToLower()}, ");

// 长度(字符串类型)

if (IsStringType(column.DbType) && column.Length.HasValue)

{

attrBuilder.Append($"Length = {column.Length.Value}, ");

}

// 默认值

if (!string.IsNullOrEmpty(column.DefaultValue))

{

attrBuilder.Append($"DefaultValue = \"{column.DefaultValue}\", ");

}

// 移除末尾逗号

if (attrBuilder.ToString().EndsWith(", "))

{

attrBuilder.Length -= 2;

}

attrBuilder.Append(")]");

codeBuilder.AppendLine($" {attrBuilder}");

// 字段类型(C#类型)

var csharpType = GetCSharpType(column.DbType, column.IsNullable);

// 字段定义

codeBuilder.AppendLine($" public {csharpType} {column.ColumnName} {{ get; set; }}");

codeBuilder.AppendLine();

}

codeBuilder.AppendLine("}");

return codeBuilder.ToString();

}

// 判断是否为整数类型

private bool IsIntType(string dbType)

{

return dbType.ToLower() is "int" or "tinyint" or "bigint";

}

// 判断是否为字符串类型

private bool IsStringType(string dbType)

{

return dbType.ToLower() is "varchar" or "char" or "text" or "nvarchar" or "ntext";

}

// 获取C#类型(处理可空)

private string GetCSharpType(string dbType, string isNullable)

{

var baseType = dbType.ToLower() switch

{

"varchar" or "char" or "text" => "string",

"int" => "int",

"bigint" => "long",

"decimal" => "decimal",

"datetime" => "DateTime",

_ => "object"

};

// 可空类型(值类型+允许为NULL)

if (isNullable.Equals("YES", StringComparison.OrdinalIgnoreCase) && IsValueType(baseType))

{

return $"{baseType}?";

}

return baseType;

}

8.3 仓储类与服务类生成的封装

除实体类外,代码生成器还支持生成仓储类(数据访问层)和服务类(业务逻辑层),以仓储类为例:

public string GenerateRepositoryCode(string entityName)

{

var codeBuilder = new StringBuilder();

codeBuilder.AppendLine("using SqlSugar;");

codeBuilder.AppendLine($"using {_namespace}.Entities;");

codeBuilder.AppendLine();

codeBuilder.AppendLine($"public class {entityName}Repository");

codeBuilder.AppendLine("{");

codeBuilder.AppendLine(" private readonly ISqlSugarClient _db;");

codeBuilder.AppendLine();

codeBuilder.AppendLine(" public {entityName}Repository(ISqlSugarClient db)");

codeBuilder.AppendLine(" {");

codeBuilder.AppendLine(" _db = db;");

codeBuilder.AppendLine(" }");

codeBuilder.AppendLine();

// 生成查询所有方法

codeBuilder.AppendLine(" /// <summary>");

codeBuilder.AppendLine(" /// 查询所有数据");

codeBuilder.AppendLine(" /// </summary>");

codeBuilder.AppendLine($" public List<{entityName}> GetAll()");

codeBuilder.AppendLine(" {");

codeBuilder.AppendLine($" return _db.Queryable<{entityName}>().ToList();");

codeBuilder.AppendLine(" }");

codeBuilder.AppendLine();

// 生成根据主键查询方法

codeBuilder.AppendLine(" /// <summary>");

codeBuilder.AppendLine(" /// 根据主键查询");

codeBuilder.AppendLine(" /// </summary>");

codeBuilder.AppendLine($" public {entityName} GetById(int id)");

codeBuilder.AppendLine(" {");

codeBuilder.AppendLine($" return _db.Queryable<{entityName}>().FindById(id);");

codeBuilder.AppendLine(" }");

codeBuilder.AppendLine();

// 生成新增、修改、删除方法(类似逻辑)

// ...

codeBuilder.AppendLine("}");

return codeBuilder.ToString();

}

九、总结:SQLSugar 封装的设计思想与优势

SQLSugar 的封装并非简单的 “API 包装”,而是基于 “轻量高效、灵活扩展、易用性优先” 的设计思想,通过以下核心设计实现:

  1. 接口解耦:通过IDbAdapter、ICacheService等接口,实现多数据库、多缓存的灵活替换;
  2. 性能优化:反射缓存、表达式树缓存、连接池复用、批量 SQL,减少性能开销;
  3. 易用性封装:自动事务、代码生成、AOP 日志,降低开发者使用成本;
  4. 安全性保障:参数化 SQL 避免注入,事务确保数据一致性;
  5. 扩展性设计:AOP 拦截、自定义适配器,支持业务个性化扩展。

这些封装设计使 SQLSugar 在 “轻量级” 与 “功能全面” 之间取得平衡,既适合小型项目快速开发,也能支撑中大型项目的复杂需求,成为.NET 生态中 ORM 框架的优秀选择。

Read more

告别996:GitHub Copilot将我的开发效率提升300%的实战记录

告别996:GitHub Copilot将我的开发效率提升300%的实战记录

👋 大家好,欢迎来到我的技术博客! 📚 在这里,我会分享学习笔记、实战经验与技术思考,力求用简单的方式讲清楚复杂的问题。 🎯 本文将围绕AI这个话题展开,希望能为你带来一些启发或实用的参考。 🌱 无论你是刚入门的新手,还是正在进阶的开发者,希望你都能有所收获! 文章目录 * 告别996:GitHub Copilot将我的开发效率提升300%的实战记录 * 引言:从疲惫到高效 * 什么是GitHub Copilot?🤖 * 效率提升300%的核心场景 * 1. 快速生成样板代码 * 2. 自动编写单元测试 * 3. 智能调试与注释 * 集成Copilot到工作流 * 步骤1:设置合理的期望 * 步骤2:结合IDE使用 * 步骤3:代码审查与调整 * 高级用法:超越代码生成 * 数据库查询优化 * API接口设计 * 正则表达式助手 * 数据支撑:效率提升分析 * 避坑指南:常见问题与解决 * 1. 可能生成过时或不安全代码

By Ne0inhk

Pi0大模型多场景落地:新能源电池拆解机器人安全指令理解应用

Pi0大模型多场景落地:新能源电池拆解机器人安全指令理解应用 1. 为什么电池拆解需要更聪明的机器人? 新能源汽车退役潮正在加速到来。据行业统计,2025年我国退役动力电池预计超100万吨——这些电池里藏着钴、镍、锂等高价值金属,回收率每提升10%,相当于新增一座中型矿山。但现实很骨感:当前90%以上的电池包仍靠人工拆解。工人要徒手拧开上百颗防爆螺栓,用万用表逐个检测电芯残压,稍有不慎就可能引发热失控甚至起火。 传统工业机器人在这里“失语”了。它们能精准重复动作,却看不懂一张带锈迹的电池模组照片,听不懂“先断开高压母线,再取下BMS板”这样的分步指令,更无法在电芯鼓包、绝缘层破损等异常状态下自主调整操作力度。这不是精度问题,而是理解力缺失。 Pi0模型的出现,恰恰切中这个痛点。它不是又一个“会动的机械臂”,而是一个真正能“看、听、想、做”的机器人操作系统内核。在某新能源回收企业的真实产线测试中,搭载Pi0的拆解机器人首次实现了对三元锂电池包的全流程自主识别与安全操作:从判断电池包是否已放电完毕,到识别不同型号的固定结构,再到根据指令动态选择拆解路径——整个过程无需预编程,仅靠

By Ne0inhk

EgoPoseFormer v2:解决 AR/VR 场景中的第一视角人体动捕问题

目录 一、前言 二、EgoPoseFormer v2 核心内容总结 1. 研究背景与挑战 2. EPFv2 的核心创新 3. 实验结果 4. 应用价值 三、DeepSeek是不是发布过关于图像识别顺序的因果时间注意力机制?         3.1 它们各自是怎么实现的,技术上有没有底层的联系和区别? 1.DeepSeek的“视觉因果流” (空间逻辑重排) 2.Meta EPFv2的“因果时间注意力” (时间逻辑依赖) 3.底层联系与核心区别 4.总结 四、EPFv2和DeepSeek OCR2和SAM2跟踪的区别和联系         4.1 EPFv2和DeepSeek OCR2和SAM2跟踪的区别和联系是什么?         4.2 技术上的相似性 🧩 不同的应用方式:从“基础模块”到“特定智能”

By Ne0inhk

4步精通:AI人脸修复工具GFPGAN的跨平台部署与应用指南

4步精通:AI人脸修复工具GFPGAN的跨平台部署与应用指南 【免费下载链接】GFPGANTencentARC/GFPGAN: GFPGAN(GFPGAN: Real-World Blind Face Restoration with PULSE++)是由腾讯ARC实验室研发的一个基于深度学习的人脸图像修复工具,主要用于低质量人脸图像的超分辨率恢复。 项目地址: https://gitcode.com/gh_mirrors/gf/GFPGAN GFPGAN作为腾讯ARC实验室研发的深度学习人脸修复工具,能够将低质量人脸图像恢复至高清状态。本文将系统讲解该工具在Windows、Linux和Mac系统上的部署流程,帮助技术用户快速掌握这一强大的图像修复解决方案。 核心环境配置指南 基础依赖要求 GFPGAN运行需要以下核心组件: * Python 3.7及以上版本(推荐Anaconda环境) * PyTorch 1.7.0及更高版本 * 可选的NVIDIA GPU加速支持(推荐使用) 操作系统适配说明 * Windows系统:Windows 10/11 64位版本

By Ne0inhk