SQLSugar 快速入门:从基础到实战查询与使用指南
目录
编辑
一、SQLSugar 简介
二、SQLSugar 环境搭建
2.1 安装 SQLSugar
2.1.1 通过 Visual Studio NuGet 图形化界面安装
2.1.2 通过 NuGet 命令行安装
2.2 引用 SQLSugar 命名空间
三、SQLSugar 核心初始化配置
3.1 基础初始化(非 IOC 模式)
3.1.1 单数据库配置
3.1.2 多数据库配置
3.2 IOC 模式初始化(适用于ASP.NET Core)
3.2.1 配置服务(Program.cs/.NET 6+)
3.2.2 在控制器中注入使用
3.3 连接字符串配置(AppSettings.json)
四、实体类映射(ORM 核心)
4.1 特性映射(推荐)
4.1.1 常用特性说明
4.1.2 实体类示例
4.2 约定映射(默认)
4.3 导航属性配置
五、SQLSugar 常见查询举例与对应 SQL 实现
5.1 单表查询
5.1.1 查询所有数据(无条件)
5.1.2 根据主键查询单条数据
5.1.3 条件查询(单条件 / 多条件)
5.1.4 模糊查询(Like)
5.1.5 范围查询(In/Between)
5.1.6 排序查询(OrderBy)
5.1.7 限制结果集(Take/Skip)
5.1.8 分页查询
5.1.9 统计查询(Count/Sum/Avg/Max/Min)
5.1.10 分组统计查询(GroupBy)
5.2 多表联查
5.2.1 两表联查(左联:Left Join)
5.2.2 两表联查(内联:Inner Join)
5.2.3 三表联查(左联 + 内联)
5.3 子查询
5.3.1 子查询作为条件(Exists/In)
5.3.2 子查询作为结果集(From 子查询)
5.4 自定义 SQL 查询
5.4.1 执行查询 SQL(返回实体类 / 匿名对象)
5.4.2 带参数的自定义 SQL(防止 SQL 注入)
六、SQLSugar 详细使用指导
6.1 CRUD 操作完整示例(除查询外)
6.1.1 新增操作(Insert)
6.1.2 修改操作(Update)
6.1.3 删除操作(Delete)
6.2 事务处理
6.2.1 手动事务(推荐,灵活控制)
6.2.2 自动事务(简化代码,通过特性或方法)
6.3 缓存功能
6.3.1 基础缓存使用(内存缓存)
6.3.2 自定义 Redis 缓存
6.4 代码生成器
6.4.1 基础代码生成(生成实体类)
6.4.2 高级代码生成(生成仓储类 + 服务类)
七、SQLSugar 使用注意事项
7.1 连接与性能相关
7.2 实体类映射相关
7.3 查询相关
一、SQLSugar 简介
SQLSugar 是一款轻量级、高性能的 ORM(对象关系映射)框架,基于.NET 平台开发,支持多种数据库(如 SQL Server、MySQL、Oracle、SQLite、PostgreSQL 等)。它兼具了 EF(Entity Framework)的易用性和 Dapper 的高性能,提供了丰富的 API 接口,能轻松实现数据库的增删改查(CRUD)操作,同时支持复杂查询、事务处理、代码优先(Code First)、数据库优先(DB First)等功能,是.NET 开发者在项目开发中操作数据库的优质选择。
相比其他 ORM 框架,SQLSugar 具有以下优势:
- 性能优异:采用了高效的 SQL 生成机制和参数化查询,避免 SQL 注入问题,同时减少数据库交互次数,提升查询效率。
- 易用性强:API 设计简洁直观,学习成本低,开发者无需编写复杂的 SQL 语句,通过面向对象的方式即可操作数据库。
- 功能全面:支持单表查询、多表联查、子查询、分组统计、分页查询等各类查询场景,同时提供事务、缓存、日志、代码生成等辅助功能。
- 多数据库支持:只需修改连接字符串和数据库类型配置,即可在不同数据库之间无缝切换,降低项目的数据库迁移成本。
- 扩展性好:支持自定义 SQL、存储过程调用、函数调用等,满足复杂业务场景的需求。
二、SQLSugar 环境搭建
2.1 安装 SQLSugar
SQLSugar 可通过 NuGet 包管理器快速安装,支持.NET Framework 4.5+、.NET Core 2.0+、.NET 5/6/7/8 等版本。以下是两种常见的安装方式:
2.1.1 通过 Visual Studio NuGet 图形化界面安装
- 打开 Visual Studio,创建或打开一个.NET 项目(如控制台应用、ASP.NET Core Web 应用等)。
- 在 “解决方案资源管理器” 中,右键点击项目名称,选择 “管理 NuGet 程序包”。
- 在弹出的 NuGet 包管理器界面中,切换到 “浏览” 选项卡,在搜索框中输入 “SQLSugar”。
- 找到 “SQLSugarCore”(.NET Core/.NET 5 + 版本)或 “SQLSugar”(.NET Framework 版本),点击 “安装” 按钮,等待安装完成。
2.1.2 通过 NuGet 命令行安装
打开 Visual Studio 的 “程序包管理器控制台”(工具 -> NuGet 包管理器 -> 程序包管理器控制台),根据项目框架版本执行以下命令:
- .NET Core/.NET 5+:
Install-Package SQLSugarCore
- .NET Framework:
Install-Package SQLSugar
2.2 引用 SQLSugar 命名空间
安装完成后,在需要使用 SQLSugar 的代码文件中,引用以下命名空间:
using SqlSugar;
using SqlSugar.IOC; // 若使用IOC容器集成,需引用此命名空间
三、SQLSugar 核心初始化配置
在使用 SQLSugar 操作数据库前,需要先进行初始化配置,主要包括创建SqlSugarClient实例、设置连接字符串、配置数据库类型等。SqlSugarClient是 SQLSugar 的核心对象,所有数据库操作都通过该对象完成。
3.1 基础初始化(非 IOC 模式)
适用于小型项目或不需要依赖注入的场景,直接创建SqlSugarClient实例并配置相关参数。
3.1.1 单数据库配置
以 SQL Server 为例,基础初始化代码如下:
// 1. 创建连接配置对象
var connectionConfig = new ConnectionConfig
{
ConnectionString = "Server=localhost;Database=TestDB;Uid=sa;Pwd=123456;", // 数据库连接字符串
DbType = DbType.SqlServer, // 数据库类型(SqlServer、MySql、Oracle、Sqlite、PostgreSQL等)
IsAutoCloseConnection = true, // 是否自动关闭数据库连接(建议设置为true,避免连接泄露)
InitKeyType = InitKeyType.Attribute // 主键初始化方式(Attribute:通过实体类特性指定主键;Auto:自动识别主键)
};
// 2. 创建SqlSugarClient实例
SqlSugarClient db = new SqlSugarClient(connectionConfig);
// 3. (可选)配置日志输出(便于调试,查看生成的SQL语句)
db.Aop.OnLogExecuting = (sql, pars) =>
{
Console.WriteLine($"SQL语句:{sql}");
Console.WriteLine($"参数:{string.Join(",", pars.Select(p => $"{p.ParameterName}={p.Value}"))}");
};
3.1.2 多数据库配置
若项目中需要操作多个不同类型的数据库(如同时操作 SQL Server 和 MySQL),可通过ConnectionConfig的ConfigId属性区分不同数据库配置:
// 配置SQL Server数据库(ConfigId=1)
var sqlServerConfig = new ConnectionConfig
{
ConfigId = 1,
ConnectionString = "Server=localhost;Database=TestDB_SQLServer;Uid=sa;Pwd=123456;",
DbType = DbType.SqlServer,
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute
};
// 配置MySQL数据库(ConfigId=2)
var mySqlConfig = new ConnectionConfig
{
ConfigId = 2,
ConnectionString = "Server=localhost;Database=TestDB_MySQL;Uid=root;Pwd=123456;Port=3306;",
DbType = DbType.MySql,
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute
};
// 创建SqlSugarClient实例,传入多个连接配置
SqlSugarClient db = new SqlSugarClient(new List<ConnectionConfig> { sqlServerConfig, mySqlConfig });
// 使用不同数据库:通过ConfigId获取对应数据库的操作对象
var sqlServerDb = db.GetConnection(1); // SQL Server数据库操作对象
var mySqlDb = db.GetConnection(2); // MySQL数据库操作对象
3.2 IOC 模式初始化(适用于ASP.NET Core)
在ASP.NET Core 项目中,推荐使用 IOC(依赖注入)模式集成 SQLSugar,便于项目的解耦和维护。
3.2.1 配置服务(Program.cs/.NET 6+)
var builder = WebApplication.CreateBuilder(args);
// 1. 添加SQLSugar服务
builder.Services.AddSqlSugar(new IocConfig
{
ConnectionConfigs = new List<ConnectionConfig>
{
new ConnectionConfig
{
ConfigId = "MainDB", // 配置ID(自定义,用于区分不同数据库)
ConnectionString = builder.Configuration.GetConnectionString("MainDB"), // 从配置文件读取连接字符串
DbType = DbType.SqlServer,
IsAutoCloseConnection = true,
InitKeyType = InitKeyType.Attribute
}
},
Scope = ServiceLifetime.Scoped // 服务生命周期(Scoped:每个请求创建一个实例;Singleton:单例;Transient:每次获取创建一个实例)
});
// 2. 添加其他服务(如MVC控制器)
builder.Services.AddControllersWithViews();
var app = builder.Build();
// 后续中间件配置...
3.2.2 在控制器中注入使用
public class UserController : Controller
{
private readonly ISqlSugarClient _db; // 注入ISqlSugarClient接口
// 构造函数注入
public UserController(ISqlSugarClient db)
{
_db = db;
}
// 示例:查询用户列表
public IActionResult UserList()
{
var userList = _db.Queryable<User>().ToList();
return View(userList);
}
}
3.3 连接字符串配置(AppSettings.json)
为了便于项目配置管理,通常将连接字符串配置在AppSettings.json文件中(以ASP.NET Core 为例):
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*",
"ConnectionStrings": {
"MainDB": "Server=localhost;Database=TestDB;Uid=sa;Pwd=123456;", // SQL Server连接字符串
"MySqlDB": "Server=localhost;Database=TestDB_MySQL;Uid=root;Pwd=123456;Port=3306;" // MySQL连接字符串
}
}
读取连接字符串时,可通过IConfiguration接口获取:
// 在Program.cs中
var connectionString = builder.Configuration.GetConnectionString("MainDB");
// 在控制器或服务中(需注入IConfiguration)
private readonly IConfiguration _configuration;
public MyService(IConfiguration configuration)
{
_configuration = configuration;
}
public void Test()
{
var connectionString = _configuration.GetConnectionString("MySqlDB");
}
四、实体类映射(ORM 核心)
SQLSugar 通过实体类与数据库表进行映射,实体类的属性对应数据库表的字段。映射方式主要有两种:特性映射(通过实体类特性指定表名、字段名、主键、自增等)和约定映射(默认约定,如实体类名对应表名、属性名对应字段名)。
4.1 特性映射(推荐)
通过SqlSugar命名空间下的特性(如SugarTable、SugarColumn)明确指定实体类与数据库表的映射关系,灵活性高,适用于表名 / 字段名与实体类 / 属性名不一致的场景。
4.1.1 常用特性说明
特性 | 作用 |
[SugarTable("表名")] | 指定实体类对应的数据库表名(若不指定,默认实体类名即为表名)。 |
[SugarColumn(IsPrimaryKey = true)] | 指定该属性为表的主键。 |
[SugarColumn(IsIdentity = true)] | 指定该属性为自增字段(仅支持整数类型主键)。 |
[SugarColumn(ColumnName = "字段名")] | 指定属性对应的数据库字段名(若不指定,默认属性名即为字段名)。 |
[SugarColumn(IsNullable = true/false)] | 指定字段是否允许为 NULL(默认根据属性类型判断,引用类型允许为 NULL)。 |
[SugarColumn(ColumnDescription = "字段描述")] | 添加字段描述(生成表结构时会同步到数据库表字段的描述中)。 |
[SugarColumn(IsIgnore = true)] | 忽略该属性,不参与数据库映射(即该属性不会对应表中的任何字段)。 |
[SugarColumn(Length = 50)] | 指定字段长度(适用于字符串类型,如 VARCHAR (50))。 |
4.1.2 实体类示例
以 “用户表(Users)” 和 “订单表(Orders)” 为例,实体类定义如下:
using System;
using SqlSugar;
// 用户表实体类(对应数据库表:Users)
[SugarTable("Users")]
public class User
{
// 主键(自增)
[SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnName = "UserId")]
public int Id { get; set; }
// 用户名(字段名:UserName,长度50,非NULL)
[SugarColumn(ColumnName = "UserName", Length = 50, IsNullable = false)]
public string Name { get; set; }
// 年龄(字段名:Age,允许为NULL)
[SugarColumn(ColumnName = "Age", IsNullable = true)]
public int? Age { get; set; }
// 性别(字段名:Gender,长度10,允许为NULL)
[SugarColumn(ColumnName = "Gender", Length = 10, IsNullable = true)]
public string Gender { get; set; }
// 注册时间(字段名:RegisterTime,默认值为当前时间)
[SugarColumn(ColumnName = "RegisterTime", DefaultValue = "GETDATE()")]
public DateTime RegisterTime { get; set; }
// 忽略字段(不映射到数据库表)
[SugarColumn(IsIgnore = true)]
public string TempData { get; set; }
}
// 订单表实体类(对应数据库表:Orders)
[SugarTable("Orders")]
public class Order
{
// 订单ID(主键,自增)
[SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnName = "OrderId")]
public int Id { get; set; }
// 用户ID(外键,关联Users表的UserId)
[SugarColumn(ColumnName = "UserId", IsNullable = false)]
public int UserId { get; set; }
// 订单金额(字段名:OrderAmount)
[SugarColumn(ColumnName = "OrderAmount", IsNullable = false)]
public decimal Amount { get; set; }
// 订单状态(字段名:OrderStatus,1:待支付,2:已支付,3:已取消)
[SugarColumn(ColumnName = "OrderStatus", IsNullable = false)]
public int Status { get; set; }
// 订单创建时间(字段名:CreateTime)
[SugarColumn(ColumnName = "CreateTime")]
public DateTime CreateTime { get; set; }
// 导航属性(关联用户表,非数据库字段,需忽略)
[SugarColumn(IsIgnore = true)]
public User User { get; set; }
}
4.2 约定映射(默认)
若数据库表名与实体类名一致、表字段名与实体类属性名一致(大小写不敏感,如 “UserId” 与 “userId” 视为一致),则无需添加任何特性,SQLSugar 会自动完成映射。例如:
// 实体类名(User)对应表名(User),属性名(UserId、UserName等)对应字段名(UserId、UserName等)
public class User
{
// 主键(默认识别名为“Id”或“UserId”的属性为主键,若为整数类型,默认自增)
public int UserId { get; set; }
public string UserName { get; set; }
public int? Age { get; set; }
public DateTime RegisterTime { get; set; }
}
4.3 导航属性配置
导航属性用于表示实体类之间的关联关系(如一对一、一对多、多对多),SQLSugar 支持通过导航属性实现关联查询。例如,订单表(Order)与用户表(User)为一对多关系(一个用户可拥有多个订单),可通过以下方式配置导航属性:
// 用户表实体类(一对多:一个用户有多个订单)
[SugarTable("Users")]
public class User
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnName = "UserId")]
public int Id { get; set; }
[SugarColumn(ColumnName = "UserName")]
public string Name { get; set; }
// 导航属性:用户的所有订单(一对多)
[SugarColumn(IsIgnore = true)]
public List<Order> Orders { get; set; }
}
// 订单表实体类(多对一:多个订单属于一个用户)
[SugarTable("Orders")]
public class Order
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnName = "OrderId")]
public int Id { get; set; }
[SugarColumn(ColumnName = "UserId")]
public int UserId { get; set; }
[SugarColumn(ColumnName = "OrderAmount")]
public decimal Amount { get; set; }
// 导航属性:订单所属的用户(多对一)
[SugarColumn(IsIgnore = true)]
public User User { get; set; }
}
五、SQLSugar 常见查询举例与对应 SQL 实现
查询是数据库操作中最常用的场景,SQLSugar 提供了Queryable方法构建查询,支持单表查询、多表联查、子查询、分页查询、分组统计等各类查询需求。以下将结合实例,详细讲解常见查询的实现方式,并给出对应的 SQL 语句。
5.1 单表查询
单表查询是最基础的查询场景,主要通过db.Queryable<T>()方法获取查询对象,再结合Where、Select、OrderBy、Take、Skip等方法筛选、排序、限制结果集,最后通过ToList()、FirstOrDefault()等方法执行查询并返回结果。
5.1.1 查询所有数据(无条件)
需求:查询Users表中的所有用户数据。
C# 代码:
// 方式1:查询所有字段(返回实体类列表)
var allUsers = db.Queryable<User>().ToList();
// 方式2:查询指定字段(返回匿名对象列表)
var userNames = db.Queryable<User>()
.Select(u => new { u.Id, u.Name, u.RegisterTime }) // 指定查询字段
.ToList();
对应 SQL 实现:
- 方式 1 对应 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
- 方式 2 对应 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [RegisterTime] AS [RegisterTime]
FROM [Users]
5.1.2 根据主键查询单条数据
需求:根据UserId(主键)查询指定用户。
C# 代码:
// 方式1:使用FindById方法(推荐,仅支持主键查询)
int userId = 1;
var userById = db.Queryable<User>().FindById(userId);
// 方式2:使用Where+FirstOrDefault方法
var userByWhere = db.Queryable<User>()
.Where(u => u.Id == userId) // 主键条件
.FirstOrDefault(); // 返回第一条数据,无数据则返回NULL
对应 SQL 实现:
- 方式 1 / 方式 2 对应 SQL(参数化查询,避免 SQL 注入):
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
WHERE [UserId] = @Id1 -- @Id1=1
5.1.3 条件查询(单条件 / 多条件)
需求 1:查询年龄大于 20 的用户(单条件)。
C# 代码:
var usersOver20 = db.Queryable<User>()
.Where(u => u.Age > 20) // 单条件:年龄>20
.ToList();
对应 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
WHERE [Age] > @Age1 -- @Age1=20
需求 2:查询年龄大于 20 且性别为 “男” 的用户(多条件:AND)。
C# 代码:
var maleUsersOver20 = db.Queryable<User>()
.Where(u => u.Age > 20 && u.Gender == "男") // 多条件AND
.ToList();
// 或使用Where链式调用(效果相同)
var maleUsersOver20_2 = db.Queryable<User>()
.Where(u => u.Age > 20)
.Where(u => u.Gender == "男")
.ToList();
对应 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
WHERE [Age] > @Age1 AND [Gender] = @Gender1 -- @Age1=20, @Gender1='男'
需求 3:查询年龄大于 20 或注册时间在 2024 年之后的用户(多条件:OR)。
C# 代码:
var targetUsers = db.Queryable<User>()
.Where(u => u.Age > 20 || u.RegisterTime >= new DateTime(2024, 1, 1)) // 多条件OR
.ToList();
对应 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
WHERE [Age] > @Age1 OR [RegisterTime] >= @RegisterTime1 -- @Age1=20, @RegisterTime1='2024-01-01 00:00:00'
5.1.4 模糊查询(Like)
需求:查询用户姓名中包含 “张” 字的用户。
C# 代码:
// 方式1:使用Contains(自动拼接%,对应LIKE '%张%')
var usersWithZhang = db.Queryable<User>()
.Where(u => u.Name.Contains("张"))
.ToList();
// 方式2:使用StartsWith(对应LIKE '张%')
var usersStartWithZhang = db.Queryable<User>()
.Where(u => u.Name.StartsWith("张"))
.ToList();
// 方式3:使用 EndsWith(对应LIKE '%张')
var usersEndWithZhang = db.Queryable<User>()
.Where(u => u.Name.EndsWith("张"))
.ToList();
// 方式4:手动拼接%(适用于复杂模糊查询场景)
var usersLikeZhang = db.Queryable<User>()
.Where(u => SqlFunc.Like(u.Name, "%张%")) // 使用SqlFunc.Like方法
.ToList();
对应 SQL 实现:
- 方式 1 对应 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
WHERE [UserName] LIKE @Name1 -- @Name1='%张%'
- 方式 2 对应 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
WHERE [UserName] LIKE @Name1 -- @Name1='张%'
- 方式 3 对应 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
WHERE [UserName] LIKE @Name1 -- @Name1='%张'
- 方式 4 对应 SQL(与方式 1 相同):
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
WHERE [UserName] LIKE @Name1 -- @Name1='%张%'
5.1.5 范围查询(In/Between)
需求 1:查询用户 ID 在 [1,3,5] 范围内的用户(In)。
C# 代码:
List<int> userIds = new List<int> { 1, 3, 5 };
var usersInIds = db.Queryable<User>()
.Where(u => userIds.Contains(u.Id)) // 使用Contains实现In查询
.ToList();
// 或使用SqlFunc.In方法(效果相同)
var usersInIds_2 = db.Queryable<User>()
.Where(u => SqlFunc.In(u.Id, userIds))
.ToList();
对应 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
WHERE [UserId] IN (@Id1, @Id2, @Id3) -- @Id1=1, @Id2=3, @Id3=5
需求 2:查询注册时间在 2023 年 1 月 1 日至 2024 年 1 月 1 日之间的用户(Between)。
C# 代码:
DateTime startDate = new DateTime(2023, 1, 1);
DateTime endDate = new DateTime(2024, 1, 1);
// 方式1:使用>=和<=实现Between
var usersBetweenDate = db.Queryable<User>()
.Where(u => u.RegisterTime >= startDate && u.RegisterTime <= endDate)
.ToList();
// 方式2:使用SqlFunc.Between方法
var usersBetweenDate_2 = db.Queryable<User>()
.Where(u => SqlFunc.Between(u.RegisterTime, startDate, endDate))
.ToList();
对应 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
WHERE [RegisterTime] BETWEEN @RegisterTime1 AND @RegisterTime2 -- @RegisterTime1='2023-01-01 00:00:00', @RegisterTime2='2024-01-01 00:00:00'
5.1.6 排序查询(OrderBy)
需求:查询所有用户,先按注册时间降序排序(最新注册的在前),再按年龄升序排序(年龄小的在前)。
C# 代码:
var sortedUsers = db.Queryable<User>()
.OrderBy(u => u.RegisterTime, OrderByType.Desc) // 降序排序(Desc)
.OrderBy(u => u.Age, OrderByType.Asc) // 升序排序(Asc,默认可省略)
.ToList();
对应 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
ORDER BY [RegisterTime] DESC, [Age] ASC
5.1.7 限制结果集(Take/Skip)
需求 1:查询前 10 条用户数据(Take)。
C# 代码:
var top10Users = db.Queryable<User>()
.Take(10) // 取前10条
.ToList();
对应 SQL(SQL Server):
SELECT TOP 10 [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
对应 SQL(MySQL):
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
LIMIT 10
需求 2:跳过前 5 条数据,查询后续的 10 条数据(Skip+Take,常用于分页的基础)。
C# 代码:
var skip5Take10Users = db.Queryable<User>()
.Skip(5) // 跳过前5条
.Take(10) // 取后续10条
.ToList();
对应 SQL(SQL Server):
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM (
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime],
ROW_NUMBER() OVER (ORDER BY (SELECT 1)) AS RowNum -- 临时排序字段
FROM [Users]
) AS Temp
WHERE Temp.RowNum > 5 AND Temp.RowNum <= 15 -- 5+10=15
对应 SQL(MySQL):
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
LIMIT 5, 10 -- 跳过5条,取10条(偏移量5,条数10)
5.1.8 分页查询
分页查询是项目中常用的查询场景,SQLSugar 提供了ToPageList方法,直接支持分页,无需手动计算Skip和Take,同时可获取总记录数。
需求:查询用户列表,每页显示 10 条数据,查询第 2 页的数据,并获取总记录数。
C# 代码:
int pageIndex = 2; // 当前页码(从1开始)
int pageSize = 10; // 每页条数
int totalCount = 0; // 总记录数(输出参数)
// 方式1:使用ToPageList方法(推荐,自动计算分页,并返回总记录数)
var userPageList = db.Queryable<User>()
.OrderBy(u => u.RegisterTime, OrderByType.Desc) // 分页必须排序,否则结果不稳定
.ToPageList(pageIndex, pageSize, ref totalCount);
// 方式2:手动计算Skip和Take(适用于复杂场景)
var userPageList_2 = db.Queryable<User>()
.OrderBy(u => u.RegisterTime, OrderByType.Desc)
.Skip((pageIndex - 1) * pageSize) // 计算跳过的条数:(页码-1)*每页条数
.Take(pageSize)
.ToList();
// 手动获取总记录数
totalCount = db.Queryable<User>().Count();
Console.WriteLine($"总记录数:{totalCount}");
Console.WriteLine($"第{pageIndex}页数据条数:{userPageList.Count}");
对应 SQL(SQL Server,方式 1):
- 查询分页数据的 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM (
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime],
ROW_NUMBER() OVER (ORDER BY [RegisterTime] DESC) AS RowNum
FROM [Users]
) AS Temp
WHERE Temp.RowNum > 10 AND Temp.RowNum <= 20 -- (2-1)*10=10,2*10=20
- 获取总记录数的 SQL:
SELECT COUNT(1) FROM [Users]
对应 SQL(MySQL,方式 1):
- 查询分页数据的 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users]
ORDER BY [RegisterTime] DESC
LIMIT 10, 10 -- (2-1)*10=10(偏移量),10(条数)
- 获取总记录数的 SQL:
SELECT COUNT(1) FROM [Users]
5.1.9 统计查询(Count/Sum/Avg/Max/Min)
需求 1:统计Users表中的总用户数(Count)。
C# 代码:
// 方式1:统计所有记录数
int totalUserCount = db.Queryable<User>().Count();
// 方式2:统计满足条件的记录数(年龄>20的用户数)
int userCountOver20 = db.Queryable<User>()
.Where(u => u.Age > 20)
.Count();
对应 SQL:
- 方式 1:SELECT COUNT(1) FROM [Users]
- 方式 2:SELECT COUNT(1) FROM [Users] WHERE [Age] > @Age1(@Age1=20)
需求 2:统计所有订单的总金额(Sum)、平均金额(Avg)、最大金额(Max)、最小金额(Min)。
C# 代码:
// 总金额(Sum)
decimal totalAmount = db.Queryable<Order>().Sum(o => o.Amount);
// 平均金额(Avg,返回double类型)
double avgAmount = db.Queryable<Order>().Avg(o => o.Amount);
// 最大金额(Max)
decimal maxAmount = db.Queryable<Order>().Max(o => o.Amount);
// 最小金额(Min)
decimal minAmount = db.Queryable<Order>().Min(o => o.Amount);
// 若结果可能为NULL(如表中无数据),建议使用可空类型接收
decimal? totalAmountNullable = db.Queryable<Order>().Sum<decimal?>(o => o.Amount);
对应 SQL:
- 总金额:SELECT SUM([OrderAmount]) FROM [Orders]
- 平均金额:SELECT AVG(CAST([OrderAmount] AS FLOAT)) FROM [Orders](SQL Server 中需转换类型)
- 最大金额:SELECT MAX([OrderAmount]) FROM [Orders]
- 最小金额:SELECT MIN([OrderAmount]) FROM [Orders]
5.1.10 分组统计查询(GroupBy)
需求:按用户 ID 分组,统计每个用户的订单数量和订单总金额。
C# 代码:
// 分组统计:按UserId分组,统计订单数和总金额
var userOrderStats = db.Queryable<Order>()
.GroupBy(o => o.UserId) // 按UserId分组
.Select(o => new
{
UserId = o.UserId,
OrderCount = SqlFunc.AggregateCount(o.Id), // 统计订单数
TotalAmount = SqlFunc.AggregateSum(o.Amount) // 统计总金额
})
.ToList();
// 筛选分组结果:仅显示订单数>=2的用户
var userOrderStatsFiltered = db.Queryable<Order>()
.GroupBy(o => o.UserId)
.Having(o => SqlFunc.AggregateCount(o.Id) >= 2) // 分组后筛选(Having)
.Select(o => new
{
UserId = o.UserId,
OrderCount = SqlFunc.AggregateCount(o.Id),
TotalAmount = SqlFunc.AggregateSum(o.Amount)
})
.ToList();
对应 SQL:
- 未筛选分组结果:
SELECT [UserId] AS [UserId],
COUNT([OrderId]) AS [OrderCount],
SUM([OrderAmount]) AS [TotalAmount]
FROM [Orders]
GROUP BY [UserId]
- 筛选分组结果(Having):
SELECT [UserId] AS [UserId],
COUNT([OrderId]) AS [OrderCount],
SUM([OrderAmount]) AS [TotalAmount]
FROM [Orders]
GROUP BY [UserId]
HAVING COUNT([OrderId]) >= @Count1 -- @Count1=2
5.2 多表联查
多表联查用于查询多个关联表的数据,SQLSugar 支持Join方法实现内联(Inner Join)、左联(Left Join)、右联(Right Join)、全联(Full Join)等联查方式,联查条件通过On方法指定。
5.2.1 两表联查(左联:Left Join)
需求:查询所有用户及其关联的订单信息(若用户无订单,订单信息为 NULL)。
C# 代码:
// 方式1:使用Join方法(Left Join)
var userOrderLeftJoin = db.Queryable<User>()
.LeftJoin<Order>((u, o) => u.Id == o.UserId) // 左联Orders表,联查条件:u.Id = o.UserId
.Select((u, o) => new
{
UserId = u.Id,
UserName = u.Name,
OrderId = o.Id,
OrderAmount = o.Amount,
OrderStatus = o.Status
})
.ToList();
// 方式2:通过导航属性联查(简化代码,需先配置导航属性)
var userOrderNav = db.Queryable<User>()
.Include(u => u.Orders) // 加载用户的Orders导航属性(左联)
.ToList();
对应 SQL(方式 1):
SELECT
u.[UserId] AS [UserId],
u.[UserName] AS [UserName],
o.[OrderId] AS [OrderId],
o.[OrderAmount] AS [OrderAmount],
o.[OrderStatus] AS [OrderStatus]
FROM [Users] u
LEFT JOIN [Orders] o ON u.[UserId] = o.[UserId]
5.2.2 两表联查(内联:Inner Join)
需求:查询有订单的用户及其订单信息(仅返回同时存在于 Users 和 Orders 表中的数据)。
C# 代码:
var userOrderInnerJoin = db.Queryable<User>()
.InnerJoin<Order>((u, o) => u.Id == o.UserId) // 内联Orders表
.Select((u, o) => new
{
UserId = u.Id,
UserName = u.Name,
OrderId = o.Id,
OrderAmount = o.Amount
})
.ToList();
对应 SQL:
SELECT
u.[UserId] AS [UserId],
u.[UserName] AS [UserName],
o.[OrderId] AS [OrderId],
o.[OrderAmount] AS [OrderAmount]
FROM [Users] u
INNER JOIN [Orders] o ON u.[UserId] = o.[UserId]
5.2.3 三表联查(左联 + 内联)
需求:假设有Users(用户表)、Orders(订单表)、OrderDetails(订单详情表),查询用户、订单及对应的订单详情信息(左联订单表,内联订单详情表)。
实体类补充:
[SugarTable("OrderDetails")]
public class OrderDetail
{
[SugarColumn(IsPrimaryKey = true, IsIdentity = true, ColumnName = "DetailId")]
public int Id { get; set; }
[SugarColumn(ColumnName = "OrderId")]
public int OrderId { get; set; }
[SugarColumn(ColumnName = "ProductName", Length = 100)]
public string ProductName { get; set; }
[SugarColumn(ColumnName = "ProductPrice")]
public decimal ProductPrice { get; set; }
[SugarColumn(ColumnName = "Quantity")]
public int Quantity { get; set; }
// 导航属性
[SugarColumn(IsIgnore = true)]
public Order Order { get; set; }
}
C# 代码:
var userOrderDetailJoin = db.Queryable<User>()
.LeftJoin<Order>((u, o) => u.Id == o.UserId) // 左联Orders表
.InnerJoin<OrderDetail>((u, o, od) => o.Id == od.OrderId) // 内联OrderDetails表
.Select((u, o, od) => new
{
UserId = u.Id,
UserName = u.Name,
OrderId = o.Id,
OrderAmount = o.Amount,
ProductName = od.ProductName,
ProductPrice = od.ProductPrice,
Quantity = od.Quantity
})
.ToList();
对应 SQL:
SELECT
u.[UserId] AS [UserId],
u.[UserName] AS [UserName],
o.[OrderId] AS [OrderId],
o.[OrderAmount] AS [OrderAmount],
od.[ProductName] AS [ProductName],
od.[ProductPrice] AS [ProductPrice],
od.[Quantity] AS [Quantity]
FROM [Users] u
LEFT JOIN [Orders] o ON u.[UserId] = o.[UserId]
INNER JOIN [OrderDetails] od ON o.[OrderId] = od.[OrderId]
5.3 子查询
子查询(嵌套查询)是指在一个查询语句内部包含另一个查询语句,SQLSugar 支持通过Queryable嵌套实现子查询,适用于复杂的查询场景。
5.3.1 子查询作为条件(Exists/In)
需求 1:查询存在订单的用户(使用 Exists 子查询)。
C# 代码:
var usersWithOrders = db.Queryable<User>()
.Where(u => db.Queryable<Order>()
.Any(o => o.UserId == u.Id)) // Any对应Exists:判断是否存在满足条件的订单
.ToList();
对应 SQL:
SELECT [UserId] AS [Id], [UserName] AS [Name], [Age] AS [Age], [Gender] AS [Gender], [RegisterTime] AS [RegisterTime]
FROM [Users] u
WHERE EXISTS (
SELECT 1 FROM [Orders] o WHERE o.[UserId] = u.[UserId]
)
需求 2:查询订单金额大于平均订单金额的订单(使用子查询获取平均金额)。
C# 代码:
// 子查询:获取平均订单金额
var ordersOverAvgAmount = db.Queryable<Order>()
.Where(o => o.Amount > db.Queryable<Order>()
.Avg<decimal?>(o2 => o2.Amount)) // 子查询获取平均金额
.ToList();
对应 SQL:
SELECT [OrderId] AS [Id], [UserId] AS [UserId], [OrderAmount] AS [Amount], [OrderStatus] AS [Status], [CreateTime] AS [CreateTime]
FROM [Orders] o
WHERE o.[OrderAmount] > (
SELECT AVG([OrderAmount]) FROM [Orders]
)
5.3.2 子查询作为结果集(From 子查询)
需求:查询每个用户的最新订单(先按用户分组获取最新订单 ID,再关联订单表查询详情)。
C# 代码:
// 子查询:按用户分组,获取每个用户的最新订单ID(最大OrderId)
var subQuery = db.Queryable<Order>()
.GroupBy(o => o.UserId)
.Select(o => new { UserId = o.UserId, MaxOrderId = SqlFunc.AggregateMax(o.Id) });
// 主查询:关联子查询和订单表,获取最新订单详情
var latestOrders = db.Queryable(subQuery, "sub") // 子查询作为临时表,别名sub
.InnerJoin<Order>((sub, o) => sub.MaxOrderId == o.Id) // 关联订单表
.Select((sub, o) => new
{
UserId = sub.UserId,
OrderId = o.Id,
OrderAmount = o.Amount,
CreateTime = o.CreateTime
})
.ToList();
对应 SQL:
SELECT
sub.[UserId] AS [UserId],
o.[OrderId] AS [OrderId],
o.[OrderAmount] AS [OrderAmount],
o.[CreateTime] AS [CreateTime]
FROM (
SELECT [UserId] AS [UserId], MAX([OrderId]) AS [MaxOrderId]
FROM [Orders]
GROUP BY [UserId]
) sub
INNER JOIN [Orders] o ON sub.[MaxOrderId] = o.[OrderId]
5.4 自定义 SQL 查询
对于特别复杂的查询场景(如多表嵌套、特殊函数调用等),若通过Queryable方法难以实现,可直接使用 SQLSugar 的自定义 SQL 查询功能,通过Ado.SqlQuery或Ado.SqlQuerySingle方法执行原生 SQL 语句。
5.4.1 执行查询 SQL(返回实体类 / 匿名对象)
需求:通过自定义 SQL 查询用户及其订单数量。
C# 代码:
// 方式1:返回匿名对象(适用于无需定义实体类的场景)
string sql = @"
SELECT
u.UserId AS Id,
u.UserName AS Name,
COUNT(o.OrderId) AS OrderCount
FROM Users u
LEFT JOIN Orders o ON u.UserId = o.UserId
GROUP BY u.UserId, u.UserName
";
var userOrderCount = db.Ado.SqlQuery<dynamic>(sql); // dynamic类型接收匿名对象
// 方式2:返回实体类(需定义对应实体类)
// 定义接收结果的实体类
public class UserOrderCountDto
{
public int Id { get; set; }
public string Name { get; set; }
public int OrderCount { get; set; }
}
var userOrderCountDto = db.Ado.SqlQuery<UserOrderCountDto>(sql);
对应 SQL:与自定义 SQL 语句一致。
5.4.2 带参数的自定义 SQL(防止 SQL 注入)
需求:通过自定义 SQL 查询指定用户 ID 的订单信息(带参数)。
C# 代码:
int targetUserId = 1;
string sqlWithParam = @"
SELECT
OrderId AS Id,
UserId,
OrderAmount AS Amount,
OrderStatus AS Status
FROM Orders
WHERE UserId = @UserId
";
// 方式1:使用匿名对象传递参数
var ordersByUserId1 = db.Ado.SqlQuery<Order>(sqlWithParam, new { UserId = targetUserId });
// 方式2:使用SugarParameter传递参数(适用于复杂参数场景)
var param = new SugarParameter("@UserId", targetUserId);
var ordersByUserId2 = db.Ado.SqlQuery<Order>(sqlWithParam, param);
对应 SQL:
SELECT
OrderId AS Id,
UserId,
OrderAmount AS Amount,
OrderStatus AS Status
FROM Orders
WHERE UserId = @UserId -- @UserId=1
六、SQLSugar 详细使用指导
6.1 CRUD 操作完整示例(除查询外)
除了查询操作,SQLSugar 还提供了简洁的 API 实现新增(Create)、修改(Update)、删除(Delete)操作,以下结合实例讲解。
6.1.1 新增操作(Insert)
需求 1:新增单个用户。
C# 代码:
var newUser = new User
{
Name = "李四",
Age = 25,
Gender = "男",
RegisterTime = DateTime.Now
};
// 方式1:新增并返回自增主键(适用于自增主键场景)
int newUserId = db.Insertable(newUser).ExecuteReturnIdentity(); // 返回新增用户的Id(自增主键)
// 方式2:新增并返回受影响行数(适用于非自增主键场景)
int affectedRows = db.Insertable(newUser).ExecuteCommand(); // 返回受影响的行数(1表示成功)
对应 SQL:
INSERT INTO [Users] ([UserName], [Age], [Gender], [RegisterTime])
VALUES (@UserName1, @Age1, @Gender1, @RegisterTime1);
SELECT SCOPE_IDENTITY() AS [Id] -- SQL Server获取自增主键;MySQL为SELECT LAST_INSERT_ID()
需求 2:批量新增用户(高效,减少数据库交互次数)。
C# 代码:
var userList = new List<User>
{
new User { Name = "王五", Age = 22, Gender = "女", RegisterTime = DateTime.Now },
new User { Name = "赵六", Age = 30, Gender = "男", RegisterTime = DateTime.Now },
new User { Name = "孙七", Age = 28, Gender = "女", RegisterTime = DateTime.Now }
};
// 批量新增(支持批量插入,自动优化SQL)
int totalAffectedRows = db.Insertable(userList).ExecuteCommand(); // 返回总受影响行数(3表示成功)
对应 SQL(SQL Server,批量插入优化):
INSERT INTO [Users] ([UserName], [Age], [Gender], [RegisterTime])
VALUES
(@UserName1, @Age1, @Gender1, @RegisterTime1),
(@UserName2, @Age2, @Gender2, @RegisterTime2),
(@UserName3, @Age3, @Gender3, @RegisterTime3)
6.1.2 修改操作(Update)
需求 1:修改单个用户信息(根据主键)。
C# 代码:
var updateUser = new User
{
Id = 1, // 主键(必须指定,用于定位要修改的记录)
Name = "李四_修改", // 要修改的字段
Age = 26 // 要修改的字段
};
// 方式1:修改所有非NULL字段(默认)
int affectedRows1 = db.Updateable(updateUser).ExecuteCommand();
// 方式2:指定修改的字段(忽略其他字段)
int affectedRows2 = db.Updateable(updateUser)
.SetColumns(u => new User { Name = u.Name, Age = u.Age }) // 仅修改Name和Age字段
.ExecuteCommand();
// 方式3:根据条件修改(不依赖实体类主键)
int affectedRows3 = db.Updateable<User>()
.SetColumns(u => u.Age == 26) // 修改字段:Age=26
.Where(u => u.Id == 1) // 条件:Id=1
.ExecuteCommand();
对应 SQL:
- 方式 1 / 方式 2:
UPDATE [Users]
SET [UserName] = @UserName1, [Age] = @Age1
WHERE [UserId] = @Id1 -- @Id1=1, @UserName1='李四_修改', @Age1=26
- 方式 3:
UPDATE [Users]
SET [Age] = @Age1
WHERE [UserId] = @Id1 -- @Id1=1, @Age1=26
需求 2:批量修改订单状态(将用户 ID 为 1 的所有订单状态改为 “已支付”)。
C# 代码:
int targetUserId = 1;
int newStatus = 2; // 2:已支付
int affectedRows = db.Updateable<Order>()
.SetColumns(o => o.Status == newStatus)
.Where(o => o.UserId == targetUserId)
.ExecuteCommand();
对应 SQL:
UPDATE [Orders]
SET [OrderStatus] = @Status1
WHERE [UserId] = @UserId1 -- @Status1=2, @UserId1=1
6.1.3 删除操作(Delete)
需求 1:根据主键删除单个用户。
C# 代码:
int targetUserId = 1;
// 方式1:根据实体类主键删除
var deleteUser = new User { Id = targetUserId };
int affectedRows1 = db.Deleteable(deleteUser).ExecuteCommand();
// 方式2:直接根据主键值删除
int affectedRows2 = db.Deleteable<User>().In(targetUserId).ExecuteCommand();
// 方式3:根据条件删除
int affectedRows3 = db.Deleteable<User>()
.Where(u => u.Id == targetUserId)
.ExecuteCommand();
对应 SQL:
DELETE FROM [Users] WHERE [UserId] = @Id1 -- @Id1=1
需求 2:批量删除用户(根据用户 ID 列表)。
C# 代码:
List<int> deleteUserIds = new List<int> { 2, 3, 4 };
// 批量删除(In查询)
int affectedRows = db.Deleteable<User>().In(deleteUserIds).ExecuteCommand();
对应 SQL:
DELETE FROM [Users] WHERE [UserId] IN (@Id1, @Id2, @Id3) -- @Id1=2, @Id2=3, @Id3=4
6.2 事务处理
事务用于保证一组数据库操作的原子性(要么全部成功,要么全部失败),SQLSugar 支持手动事务和自动事务两种方式。
6.2.1 手动事务(推荐,灵活控制)
需求:新增用户的同时,为该用户创建一条初始订单,若其中一个操作失败,回滚所有操作。
C# 代码:
try
{
// 1. 开启事务
db.Ado.BeginTran();
// 2. 执行新增用户操作
var newUser = new User { Name = "钱八", Age = 24, Gender = "男", RegisterTime = DateTime.Now };
int newUserId = db.Insertable(newUser).ExecuteReturnIdentity();
// 3. 执行新增订单操作(关联新用户ID)
var newOrder = new Order
{
UserId = newUserId,
Amount = 100.50m,
Status = 1, // 1:待支付
CreateTime = DateTime.Now
};
db.Insertable(newOrder).ExecuteCommand();
// 4. 提交事务(所有操作成功,提交)
db.Ado.CommitTran();
Console.WriteLine("事务执行成功!");
}
catch (Exception ex)
{
// 5. 回滚事务(若有操作失败,回滚)
db.Ado.RollbackTran();
Console.WriteLine($"事务执行失败,已回滚:{ex.Message}");
}
6.2.2 自动事务(简化代码,通过特性或方法)
需求:在ASP.NET Core 控制器中,通过特性自动开启事务。
C# 代码:
// 1. 注册事务服务(Program.cs)
builder.Services.AddSqlSugarTransaction();
// 2. 在控制器方法中使用[Transaction]特性
[ApiController]
[Route("api/[controller]")]
public class UserController : ControllerBase
{
private readonly ISqlSugarClient _db;
public UserController(ISqlSugarClient db)
{
_db = db;
}
[HttpPost("AddUserAndOrder")]
[Transaction] // 自动开启事务,方法执行成功自动提交,失败自动回滚
public IActionResult AddUserAndOrder(User user, Order order)
{
// 新增用户
int newUserId = _db.Insertable(user).ExecuteReturnIdentity();
// 新增订单
order.UserId = newUserId;
_db.Insertable(order).ExecuteCommand();
return Ok("操作成功!");
}
}
6.3 缓存功能
SQLSugar 提供了内置缓存功能,支持将查询结果缓存到内存中,减少数据库查询次数,提升系统性能。缓存默认使用内存缓存,也支持自定义缓存(如 Redis)。
6.3.1 基础缓存使用(内存缓存)
需求:查询用户列表并缓存 10 分钟,10 分钟内再次查询时直接从缓存获取。
C# 代码:
// 方式1:使用WithCache方法设置缓存(缓存Key自动生成)
var userList1 = db.Queryable<User>()
.WithCache(600) // 缓存时间:600秒(10分钟)
.ToList();
// 方式2:自定义缓存Key(便于手动清除缓存)
string cacheKey = "UserList_CacheKey";
var userList2 = db.Queryable<User>()
.WithCache(600, cacheKey) // 自定义缓存Key
.ToList();
// 手动清除缓存(当数据更新时,需清除对应缓存)
db.CacheService.Remove(cacheKey); // 清除指定Key的缓存
// db.CacheService.Clear(); // 清除所有缓存
6.3.2 自定义 Redis 缓存
需求:将查询结果缓存到 Redis 中,适用于分布式系统。
C# 代码:
// 1. 实现ICacheService接口(Redis缓存实现)
public class RedisCacheService : ICacheService
{
private readonly IDistributedCache _redisCache;
public RedisCacheService(IDistributedCache redisCache)
{
_redisCache = redisCache;
}
// 添加缓存
public void Add<V>(string key, V value, int cacheDurationInSeconds)
{
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(cacheDurationInSeconds)
};
string jsonValue = JsonSerializer.Serialize(value);
_redisCache.SetString(key, jsonValue, options);
}
// 获取缓存
public V Get<V>(string key)
{
string jsonValue = _redisCache.GetString(key);
return string.IsNullOrEmpty(jsonValue) ? default : JsonSerializer.Deserialize<V>(jsonValue);
}
// 移除缓存
public void Remove(string key)
{
_redisCache.Remove(key);
}
// 清除所有缓存(Redis不建议批量清除,可根据实际需求实现)
public void Clear()
{
// Redis批量清除需通过KEYS命令,生产环境慎用,此处省略实现
}
}
// 2. 注册Redis缓存服务(Program.cs)
builder.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = "localhost:6379"; // Redis连接字符串
});
builder.Services.AddScoped<ICacheService, RedisCacheService>();
// 3. 使用Redis缓存查询
var userList = db.Queryable<User>()
.WithCache(600, "UserList_RedisKey") // 使用Redis缓存
.ToList();
6.4 代码生成器
SQLSugar 提供了代码生成器功能,可根据数据库表结构自动生成实体类、仓储类、服务类等代码,减少重复编码工作,提升开发效率。
6.4.1 基础代码生成(生成实体类)
C# 代码:
// 1. 创建代码生成器配置
var codeConfig = new CodeGeneratorConfig
{
ConnectionString = "Server=localhost;Database=TestDB;Uid=sa;Pwd=123456;",
DbType = DbType.SqlServer,
OutputDir = @"D:\CodeGeneratorOutput", // 代码输出目录
Namespace = "TestProject.Entities", // 实体类命名空间
GenerateNullableReferenceType = true, // 生成可空引用类型(.NET 6+支持)
EntityNameFormat = "{0}", // 实体类名格式({0}表示表名)
ColumnNameFormat = "{0}" // 属性名格式({0}表示字段名)
};
// 2. 创建代码生成器
var codeGenerator = new CodeGenerator(codeConfig);
// 3. 生成实体类(生成所有表的实体类)
codeGenerator.GenerateEntities();
// 4. (可选)生成指定表的实体类
codeGenerator.GenerateEntities(new List<string> { "Users", "Orders" }); // 仅生成Users和Orders表的实体类
Console.WriteLine("代码生成完成!");
6.4.2 高级代码生成(生成仓储类 + 服务类)
C# 代码:
// 配置代码生成器
var codeConfig = new CodeGeneratorConfig
{
ConnectionString = "Server=localhost;Database=TestDB;Uid=sa;Pwd=123456;",
DbType = DbType.SqlServer,
OutputDir = @"D:\CodeGeneratorOutput",
Namespace = "TestProject",
// 生成仓储类(Repository)
GenerateRepository = true,
RepositoryNamespace = "TestProject.Repositories",
// 生成服务类(Service)
GenerateService = true,
ServiceNamespace = "TestProject.Services"
};
var codeGenerator = new CodeGenerator(codeConfig);
// 生成实体类、仓储类、服务类
codeGenerator.GenerateAll(); // 生成所有支持的代码类型
Console.WriteLine("所有代码生成完成!");
七、SQLSugar 使用注意事项
7.1 连接与性能相关
- 自动关闭连接:初始化SqlSugarClient时,务必将IsAutoCloseConnection设置为true,避免数据库连接泄露。若设置为false,需手动调用db.Close()关闭连接。
- SqlSugarClient实例生命周期:
-
- 非 IOC 模式:建议每个请求创建一个SqlSugarClient实例,使用完毕后自动关闭(依赖IsAutoCloseConnection=true),避免单例实例导致的线程安全问题。
-
- IOC 模式:在ASP.NET Core 中,推荐使用Scoped生命周期(每个请求一个实例),避免Singleton(单例)导致的线程安全问题。
- 批量操作优化:批量新增、修改、删除时,使用 SQLSugar 提供的批量 API(如Insertable<List<T>>、Updateable<T>.In),避免循环调用单条操作,减少数据库交互次数。
- 避免过度查询:查询时仅获取需要的字段(使用Select指定字段),避免Select *查询不必要的字段,减少数据传输量和数据库负担。
7.2 实体类映射相关
- 主键与自增配置:
-
- 主键必须通过[SugarColumn(IsPrimaryKey = true)]明确指定,否则 SQLSugar 无法识别主键,导致修改、删除操作失败。
-
- 自增字段需同时设置IsIdentity = true,且字段类型必须为整数类型(int、long 等),非整数类型无法支持自增。
- 字段类型匹配:实体类属性类型需与数据库表字段类型匹配,例如:
-
- 数据库中的VARCHAR/NVARCHAR类型对应 C# 中的string类型。
-
- 数据库中的DECIMAL类型对应 C# 中的decimal类型(避免使用double,防止精度丢失)。
-
- 数据库中的DATETIME/DATETIME2类型对应 C# 中的DateTime类型。
- 导航属性忽略:导航属性(如User.Orders、Order.User)仅用于关联查询,不对应数据库表字段,需通过[SugarColumn(IsIgnore = true)]标记为忽略,否则会导致映射错误。
- 表名 / 字段名大小写:SQLSugar 默认不区分表名和字段名的大小写(如 “Users” 与 “users” 视为一致),但部分数据库(如 MySQL)默认区分大小写,需确保实体类特性指定的表名 / 字段名与数据库一致。
7.3 查询相关
- 分页必须排序:使用ToPageList进行分页查询时,必须通过OrderBy指定排序字段,否则数据库返回的结果顺序不稳定,导致分页数据重复或缺失。
- 子查询性能:复杂子查询可能导致性能问题,建议优先使用联查(Join)替代子查询,或通过索引优化子查询条件字段。
- 模糊查询优化:使用Like '%关键词%'(对应Contains)会导致索引失效,若需优化性能,可考虑:
-
- 使用Like '关键词%'(对应StartsWith),可利用字段的前缀索引。