1. 项目概述
"SqlSugar"作为.NET生态中广受欢迎的ORM框架,其简洁的API设计和高效的数据库操作能力深受开发者青睐。这个系列文章将带大家深入探索SqlSugar的核心功能与实战技巧,从基础配置到高级特性,全面解析这个轻量级ORM框架的完整技术栈。
我在实际项目中使用SqlSugar已有三年多时间,处理过从单表CRUD到复杂分布式事务的各种场景。本文将结合这些实战经验,分享SqlSugar最实用的功能模块、性能优化技巧以及那些官方文档中没有明确说明的"隐藏特性"。
2. 核心功能解析
2.1 基础CRUD操作
SqlSugar最令人称道的就是其极度简洁的CRUD语法。相比Entity Framework复杂的DbContext配置,SqlSugar只需要几行代码就能完成数据库操作:
csharp复制// 初始化SqlSugarClient
var db = new SqlSugarClient(new ConnectionConfig()
{
ConnectionString = "Server=.;Database=Demo;Uid=sa;Pwd=123456;",
DbType = DbType.SqlServer,
IsAutoCloseConnection = true
});
// 插入数据
var user = new User { Name = "张三", Age = 25 };
db.Insertable(user).ExecuteCommand();
// 查询单条记录
var firstUser = db.Queryable<User>().First();
这种链式调用的API设计让代码可读性大幅提升。在实际项目中,我通常会将这些基础操作封装成泛型仓储层:
csharp复制public class BaseRepository<T> where T : class, new()
{
public ISqlSugarClient Db { get; }
public BaseRepository(ISqlSugarClient client)
{
Db = client;
}
public virtual int Insert(T entity)
{
return Db.Insertable(entity).ExecuteCommand();
}
// 其他CRUD方法...
}
注意:虽然SqlSugar支持动态ConnectionString,但在生产环境中建议使用依赖注入管理生命周期。我曾遇到过因连接未及时释放导致的连接池耗尽问题。
2.2 高级查询功能
SqlSugar的查询能力远不止简单的First()或ToList()。它提供了丰富的查询构建方式:
csharp复制// 条件查询
var list = db.Queryable<User>()
.Where(u => u.Age > 18)
.WhereIF(!string.IsNullOrEmpty(name), u => u.Name.Contains(name))
.OrderBy(u => u.CreateTime, OrderByType.Desc)
.ToList();
// 分页查询
var page = db.Queryable<User>()
.Where(u => u.Status == 1)
.ToPageList(pageIndex, pageSize, ref totalCount);
特别值得一提的是SqlSugar对动态条件的优雅处理。通过WhereIF方法可以避免写大量if-else条件判断:
csharp复制// 传统方式
var query = db.Queryable<User>();
if(age > 0)
{
query = query.Where(u => u.Age > age);
}
if(!string.IsNullOrEmpty(name))
{
query = query.Where(u => u.Name.Contains(name));
}
// SqlSugar方式
var query = db.Queryable<User>()
.WhereIF(age > 0, u => u.Age > age)
.WhereIF(!string.IsNullOrEmpty(name), u => u.Name.Contains(name));
在实际项目中,这种写法可以大幅减少代码量。我统计过一个用户管理模块,使用WhereIF后条件查询代码减少了约40%。
3. 高级特性实战
3.1 事务处理
SqlSugar提供了多种事务处理方式,满足不同场景需求:
csharp复制// 单库事务(推荐)
db.Ado.BeginTran();
try
{
db.Insertable(user1).ExecuteCommand();
db.Updateable(user2).ExecuteCommand();
db.Ado.CommitTran();
}
catch
{
db.Ado.RollbackTran();
throw;
}
// 使用事务特性(更简洁)
[Transactional]
public void UpdateUser(User user)
{
db.Updateable(user).ExecuteCommand();
// 其他操作...
}
对于分布式事务,SqlSugar支持跨库事务(需要配置多个ConnectionString):
csharp复制using (var tran = db.UseTran())
{
db1.Insertable(user).ExecuteCommand(); // 操作数据库1
db2.Updateable(order).ExecuteCommand(); // 操作数据库2
tran.CommitTran();
}
经验分享:在ASP.NET Core中,我通常结合UnitOfWork模式管理事务。一个HTTP请求对应一个工作单元,在请求结束时统一提交或回滚。
3.2 性能优化技巧
经过多个项目实践,我总结了以下SqlSugar性能优化要点:
- 批量操作:对于大批量数据操作,务必使用批量方法
csharp复制// 错误做法(循环单条插入)
foreach(var item in list)
{
db.Insertable(item).ExecuteCommand();
}
// 正确做法(批量插入)
db.Insertable(list).ExecuteCommand();
- 查询优化:避免不必要的数据查询
csharp复制// 只查询需要的字段
db.Queryable<User>().Select(u => new { u.Id, u.Name }).ToList();
// 使用Any()代替Count()>0判断存在性
var exists = db.Queryable<User>().Any(u => u.Name == "张三");
- 二级缓存:合理使用缓存提升性能
csharp复制// 启用查询缓存
var list = db.Queryable<User>()
.WithCache()
.ToList();
// 自定义缓存时间(单位:秒)
var list = db.Queryable<User>()
.WithCache(60 * 5) // 缓存5分钟
.ToList();
在我的一个电商项目中,合理使用二级缓存后,商品列表查询的响应时间从平均200ms降到了50ms以下。
4. 实战问题排查
4.1 常见错误与解决方案
问题1:连接泄漏
症状:应用运行一段时间后出现"连接池耗尽"错误。
解决方案:
- 确保使用
IsAutoCloseConnection=true - 在ASP.NET Core中使用依赖注入管理生命周期
- 检查所有操作是否放在using块中
问题2:导航属性循环引用
症状:序列化包含导航属性的实体时出现堆栈溢出。
解决方案:
- 使用
[SugarColumn(IsIgnore = true)]忽略导航属性 - 或者配置Json序列化时忽略循环引用
csharp复制services.AddControllers()
.AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
});
问题3:批量插入性能差
症状:插入1000条数据耗时超过10秒。
解决方案:
- 使用
BulkCopy替代普通插入 - 调整
BulkCopy的批量大小
csharp复制db.Fastest<User>().BulkCopy(list); // 默认批量大小1000
db.Fastest<User>().BulkCopy(list, 5000); // 调整批量大小
4.2 监控与诊断
SqlSugar提供了完善的日志和诊断功能:
csharp复制// 启用SQL日志
db.Aop.OnLogExecuting = (sql, pars) =>
{
Console.WriteLine(sql);
};
// 性能分析
db.Aop.OnLogExecuted = (sql, pars) =>
{
Console.WriteLine($"执行时间:{db.Ado.SqlExecutionTime.TotalMilliseconds}ms");
};
// 错误监控
db.Aop.OnError = (exp) =>
{
Console.WriteLine($"错误:{exp.Message}");
// 发送错误通知...
};
在我的实践中,通常会将这些日志集成到应用的统一日志系统中,并设置关键操作的性能阈值告警。
5. 扩展应用场景
5.1 多租户实现
SqlSugar原生支持多租户方案,可以通过过滤器实现:
csharp复制// 配置租户过滤器
db.QueryFilter.Add(new TableFilterItem<User>(it => it.TenantId == currentTenantId));
// 所有查询将自动加上租户条件
var users = db.Queryable<User>().ToList();
// 实际执行:SELECT * FROM User WHERE TenantId = 'xxx'
对于分库分表的场景,可以使用SplitTable特性:
csharp复制[SugarTable("Order_{year}{month}")]
public class Order
{
[SplitField] // 分表字段
public DateTime CreateTime { get; set; }
}
// 自动路由到正确的分表
var orders = db.Queryable<Order>()
.Where(o => o.CreateTime >= DateTime.Now.AddMonths(-3))
.ToList();
5.2 读写分离
SqlSugar的读写分离配置非常简单:
csharp复制var db = new SqlSugarClient(new List<ConnectionConfig>()
{
// 主库(写)
new ConnectionConfig()
{
ConfigId = "master",
ConnectionString = masterConnStr,
DbType = DbType.SqlServer,
IsAutoCloseConnection = true
},
// 从库(读)
new ConnectionConfig()
{
ConfigId = "slave1",
ConnectionString = slave1ConnStr,
DbType = DbType.SqlServer,
IsAutoCloseConnection = true
}
},
db =>
{
// 读操作使用从库
db.GetConnection("slave1");
});
在实际部署时,建议结合健康检查实现从库的自动故障转移。
6. 最佳实践总结
经过多个项目的实战检验,我总结了以下SqlSugar最佳实践:
- 项目结构组织
- 将DbContext配置放在基础设施层
- 使用仓储模式封装常用数据访问逻辑
- 领域模型与数据库模型分离(必要时使用AutoMapper转换)
- 性能关键点
- 批量操作优先使用BulkCopy
- 查询时明确指定字段列表
- 合理使用二级缓存
- 避免在循环中执行数据库操作
- 维护性建议
- 为所有实体添加注释
[SugarTable("表名", "表描述")] - 使用AOP统一处理审计字段(如CreateTime, UpdateTime)
- 开发环境开启SQL日志,生产环境关闭
- 团队协作规范
- 统一命名约定(如表名复数,字段名小写等)
- 制定代码审查清单(如是否使用WhereIF等)
- 共享常用查询的代码片段
在我的团队中,我们基于这些实践建立了一套SqlSugar使用规范,新成员上手速度提升了约50%,项目中的数据库相关Bug减少了30%以上。
最后分享一个实用技巧:SqlSugar支持将Lambda表达式转换为SQL字符串,这在动态构建复杂查询时非常有用:
csharp复制// 获取条件SQL
Expression<Func<User, bool>> expr = u => u.Age > 18 && u.Name.Contains("张");
var sql = db.Utilities.ExpressionToSql(expr);
// 输出:([Age] > 18 AND [Name] LIKE '%张%')
这个功能在实现高级查询功能时特别有用,比如前端传递复杂过滤条件时,可以动态构建查询表达式。