学习ORMX的查询构建功能,包括Where条件、OrderBy排序、GroupBy分组、Join连接和分页查询。 关键字:查询构建, Where条件, OrderBy排序, GroupBy分组, Join连接, 分页查询
查询构建
链式调用原理
ORMX 采用链式继承设计模式,每个查询方法返回不同的接口类型,实现流畅的链式调用。理解这一机制有助于更好地使用 ORMX。
链式调用的工作原理
userTable
.Where(u => u.Age > 25) // 返回 IWhere<User>
.OrderBy(u => u.Name) // 返回 IOrderBy<User>
.Limit(10) // 返回 ILimit<User>
.GetList(); // 返回 List<User>
工作原理:
- 每个方法(如
Where、OrderBy)返回一个特定的接口类型 - 返回的接口类型包含下一个可用的方法
- 所有方法共享同一个
QueryBuilder实例,累积查询条件 - 最终调用
GetList()、FirstOrDefault()等方法执行查询
设计优势
- 类型安全 - 编译时检查,避免拼写错误
- 智能提示 - IDE 会根据当前类型显示可用的方法
- 功能分离 - 每个方法专注于单一功能
- 易于扩展 - 新增功能只需添加新的接口和实现
常见链式调用顺序
虽然 ORMX 允许灵活的调用顺序,但以下顺序更符合 SQL 逻辑:
Table<T>()
→ Where() // 筛选条件
→ OrderBy() // 排序
→ Limit() // 分页
→ GetList() // 执行查询
或者:
Table<T>()
→ Where() // 筛选条件
→ GroupBy() // 分组
→ Having() // 分组筛选
→ OrderBy() // 排序
→ Limit() // 分页
→ GetList() // 执行查询
Where 条件查询
基本条件查询
使用 Where 方法添加查询条件:
var userTable = tableManager.Table<User>();
// 查询年龄大于 25 的用户
var users = userTable.Where(u => u.Age > 25).GetList();
Console.WriteLine(<div class="latex">$"年龄大于 25 的用户:{users.Count} 个");
// 查询姓名为"张三"的用户
var user = userTable.Where(u => u.Name == "张三").FirstOrDefault();
支持的比较运算符:
==- 等于!=- 不等于>- 大于>=- 大于等于<- 小于<=- 小于等于
多条件查询
使用多个 Where 方法或使用逻辑运算符:
// 方法一:链式调用
var users = userTable
.Where(u => u.Age > 25)
.Where(u => u.Name == "张三")
.GetList();
// 方法二:逻辑运算符
var users = userTable.Where(u => u.Age > 25 && u.Name == "张三").GetList();
// 方法三:使用 || 运算符
var users = userTable.Where(u => u.Age > 25 || u.Name == "张三").GetList();
支持的逻辑运算符:
&&- 逻辑与(AND)||- 逻辑或(OR)!- 逻辑非(NOT)
Between 范围查询
使用 Between 方法查询指定范围内的数据:
// 查询年龄在 18 到 30 之间的用户
var users = userTable.Between(u => u.Age, 18, 30).GetList();
Console.WriteLine($</div>"年龄在 18-30 之间的用户:{users.Count} 个");
// 查询创建时间在指定日期范围内的用户
var users = userTable
.Between(u => u.CreatedAt, new DateTime(2023, 1, 1), new DateTime(2023, 12, 31))
.GetList();
NotBetween 排除范围查询
使用 NotBetween 方法查询指定范围外的数据:
// 查询年龄不在 18 到 30 之间的用户
var users = userTable.NotBetween(u => u.Age, 18, 30).GetList();
Console.WriteLine(<div class="latex">$"年龄不在 18-30 之间的用户:{users.Count} 个");
In 列表查询
使用 In 方法查询在指定列表中的数据:
// 查询 ID 为 1、2、3 的用户
var users = userTable.In(u => u.Id, 1, 2, 3).GetList();
Console.WriteLine($</div>"ID 为 1、2、3 的用户:{users.Count} 个");
// 查询姓名在指定列表中的用户
var names = new string[] { "张三", "李四", "王五" };
var users = userTable.In(u => u.Name, names).GetList();
NotIn 排除列表查询
使用 NotIn 方法查询不在指定列表中的数据:
// 查询 ID 不为 1、2、3 的用户
var users = userTable.NotIn(u => u.Id, 1, 2, 3).GetList();
Console.WriteLine(<div class="latex">$"ID 不为 1、2、3 的用户:{users.Count} 个");
Like 模糊查询
使用 Like 方法进行模糊查询:
// 查询姓名以"张"开头的用户
var users = userTable.Like(u => u.Name, "张%").GetList();
Console.WriteLine($</div>"姓名以张开头的用户:{users.Count} 个");
// 查询姓名包含"三"的用户
var users = userTable.Like(u => u.Name, "%三%").GetList();
Console.WriteLine(<div class="latex">$"姓名包含三的用户:{users.Count} 个");
// 查询姓名以"三"结尾的用户
var users = userTable.Like(u => u.Name, "%三").GetList();
Console.WriteLine($</div>"姓名以三结尾的用户:{users.Count} 个");
// 查询邮箱以"@example.com"结尾的用户
var users = userTable.Like(u => u.Email, "%@example.com").GetList();
通配符说明:
%- 匹配任意长度的任意字符_- 匹配单个任意字符
NotLike 排除模糊查询
使用 NotLike 方法排除模糊查询的结果:
// 查询姓名不以"张"开头的用户
var users = userTable.NotLike(u => u.Name, "张%").GetList();
Console.WriteLine(<div class="latex">$"姓名不以张开头的用户:{users.Count} 个");
组合高级条件
可以将高级条件与基本条件组合使用:
// 查询年龄在 18-30 之间且姓名以"张"开头的用户
var users = userTable
.Between(u => u.Age, 18, 30)
.Like(u => u.Name, "张%")
.GetList();
// 查询年龄在 18-30 之间且 ID 不为 1、2、3 的用户
var users = userTable
.Between(u => u.Age, 18, 30)
.NotIn(u => u.Id, 1, 2, 3)
.GetList();
// 查询姓名包含"三"且邮箱以"@example.com"结尾的用户
var users = userTable
.Like(u => u.Name, "%三%")
.Like(u => u.Email, "%@example.com")
.GetList();
字符串操作(Where 表达式)
除了使用 Like 方法外,还可以在 Where 表达式中使用字符串方法:
// LIKE 查询(包含)- 等价于 WHERE Name LIKE '%张%'
var users = userTable.Where(u => u.Name.Contains("张")).GetList();
// LIKE 查询(开头)- 等价于 WHERE Name LIKE '张%'
var users = userTable.Where(u => u.Name.StartsWith("张")).GetList();
// LIKE 查询(结尾)- 等价于 WHERE Name LIKE '%三'
var users = userTable.Where(u => u.Name.EndsWith("三")).GetList();
// 等于查询
var users = userTable.Where(u => u.Name == "张三").GetList();
// 不等于查询
var users = userTable.Where(u => u.Name != "张三").GetList();
WhereComplex 复杂条件组合
WhereComplex 方法允许在一个表达式中组合多个 Where 条件,支持复杂的逻辑运算(AND、OR、括号)。这对于构建复杂的查询条件非常有用。
####.1 基本用法
// 组合 Where 和 Where 条件
var users = userTable
.WhereComplex(w => w.Where(u => u.Id == 1) && w.Where(u => u.Name == "张三"))
.GetList();
// 生成 SQL: SELECT * FROM User WHERE ("Id" = @p1) AND ("Name" = @p0)
####.2 组合 Where 和高级条件
// 组合 Where 和 Between
var users = userTable
.WhereComplex(w => w.Where(u => u.Name == "张三") && w.Between(u => u.Age, 18, 30))
.GetList();
// 生成 SQL: SELECT * FROM User WHERE ("Name" = @p2) AND ("Age" BETWEEN @p0 AND @p1)
// 组合 Where 和 In
var users = userTable
.WhereComplex(w => w.Where(u => u.Name == "张三") && w.In(u => u.Id, 1, 2, 3))
.GetList();
// 生成 SQL: SELECT * FROM User WHERE ("Name" = @p3) AND ("Id" IN (@p0, @p1, @p2))
// 组合 Where 和 Like
var users = userTable
.WhereComplex(w => w.Where(u => u.Age > 18) && w.Like(u => u.Name, "张%"))
.GetList();
// 生成 SQL: SELECT * FROM User WHERE ("Age" > @p1) AND ("Name" LIKE @p0)
####.3 使用 OR 逻辑
// 使用 OR 逻辑
var users = userTable
.WhereComplex(w => w.Where(u => u.Id == 1) || w.Where(u => u.Id == 2))
.GetList();
// 生成 SQL: SELECT * FROM User WHERE "Id" = @p0 OR "Id" = @p1
####.4 复杂嵌套条件
// 复杂嵌套条件(使用括号)
var users = userTable
.WhereComplex(w =>
(w.Where(u => u.Name == "张三") && w.Between(u => u.Age, 18, 30)) ||
(w.Where(u => u.Name == "李四") && w.Between(u => u.Age, 25, 40))
)
.GetList();
// 生成 SQL: SELECT * FROM User WHERE
// (("Name" = @p4) AND ("Age" BETWEEN @p5 AND @p1)) OR
// (("Name" = @p0) AND ("Age" BETWEEN @p2 AND @p3))
####.5 多条件组合
// 多个条件的复杂组合
var users = userTable
.WhereComplex(w =>
(w.Where(u => u.Name == "张三") && w.Between(u => u.Age, 18, 30) && w.In(u => u.Id, 1, 2, 3)) ||
(w.Where(u => u.Name == "李四") && w.NotBetween(u => u.Age, 40, 60) && w.Like(u => u.Email, "%company.com")) ||
(w.Where(u => u.Age > 18) && w.NotIn(u => u.Id, 10, 11, 12) && w.NotLike(u => u.Name, "Test%"))
)
.GetList();
WhereComplex 的优势:
- 支持复杂的逻辑运算(AND、OR、括号)
- 可以在一个表达式中组合多个 Where 条件
- 生成的 SQL 更清晰,可读性更好
- 适合构建复杂的查询条件
多表连接
ORMX 支持 JOIN 操作,可以连接多个表进行查询。关于 JOIN 操作的详细用法,请参考连表查询。
// 内连接
var result = userTable
.InnerJoin<Order>((u, o) => u.Id == o.UserId)
.GetList();
// 左连接
var result = userTable
.LeftJoin<Order>((u, o) => u.Id == o.UserId)
.GetList();
// 右连接
var result = userTable
.RightJoin<Order>((u, o) => u.Id == o.UserId)
.GetList();
// 全连接
var result = userTable
.FullJoin<Order>((u, o) => u.Id == o.UserId)
.GetList();
关于 JOIN 操作的详细说明,请查阅连表查询。
OrderBy 排序
单字段排序
// 升序排序
var users = userTable.OrderBy(u => u.Age).GetList();
// 降序排序
var users = userTable.OrderByDesc(u => u.Age).GetList();
多字段排序
// 先按年龄升序,再按姓名降序
var users = userTable
.OrderBy(u => u.Age)
.OrderByDesc(u => u.Name)
.GetList();
多表排序
在 Join 查询中排序:
var result = userTable
.Join<Order, User, Order, object>(
(u, o) => u.Id == o.UserId,
(u, o) => new { u.Name, o.Amount }
)
.OrderBy(x => x.Name)
.GetList();
GroupBy 分组
单字段分组
// 按年龄分组
var groups = userTable
.GroupBy(u => u.Age)
.Max(u => u.Age, "MaxAge")
.Min(u => u.Age, "MinAge")
.Count(u => u.Id, "Count")
.GetList<AgeGroupStats>();
多字段分组
// 按年龄和姓名分组
var groups = userTable
.GroupBy(u => new { u.Age, u.Name })
.Max(u => u.Age, "MaxAge")
.Count(u => u.Id, "Count")
.GetList<AgeNameGroupStats>();
关于聚合函数的详细用法,请查阅聚合函数。
Having 分组后筛选
// 分组后筛选数量大于 1 的组
var groups = userTable
.GroupBy(u => u.Age)
.Having(u => u.Age > 25)
.Max(u => u.Age, "MaxAge")
.Count(u => u.Id, "Count")
.GetList<AgeGroupStats>();
Join 连接查询
ORMX 支持多种 JOIN 操作,包括 INNER JOIN、LEFT JOIN、RIGHT JOIN 和 FULL JOIN。关于 JOIN 操作的详细说明,请查阅连表查询。
内连接(INNER JOIN)
var result = userTable
.InnerJoin<Order>((u, o) => u.Id == o.UserId)
.GetList();
左连接(LEFT JOIN)
var result = userTable
.LeftJoin<Order>((u, o) => u.Id == o.UserId)
.GetList();
右连接(RIGHT JOIN)
var result = userTable
.RightJoin<Order>((u, o) => u.Id == o.UserId)
.GetList();
全连接(FULL JOIN)
var result = userTable
.FullJoin<Order>((u, o) => u.Id == o.UserId)
.GetList();
无限级联 Join
var result = userTable
.InnerJoin<Order>((u, o) => u.Id == o.UserId)
.InnerJoin<Product>((o, p) => o.Id == p.OrderId)
.GetList();
关于 JOIN 操作的详细说明,请查阅连表查询。
Select 选择列
选择单个列
// 选择姓名列
var names = userTable.Select(u => u.Name).GetList();
选择多个列
// 选择姓名和邮箱列
var users = userTable.Select(u => new { u.Name, u.Email }).GetList();
Limit/Offset 分页
Limit 限制结果数量
// 只返回前 10 条记录
var users = userTable.Limit(10).GetList();
Offset 跳过记录
// 跳过前 10 条记录,返回剩余记录
var users = userTable.Offset(10).GetList();
分页查询
// 方法一:使用 Offset + Limit
var pageSize = 10;
var pageNumber = 2;
var users = userTable
.Offset((pageNumber - 1) * pageSize)
.Limit(pageSize)
.GetList();
// 方法二:使用 Page 方法(更简便)
var users = userTable.Page(pageNumber, pageSize).GetList();
Page 方法优势:
- 语法更简洁,一行代码完成分页
- 自动计算 OFFSET 值
- 推荐使用 Page 方法进行分页查询
链式查询组合
ORMX 支持链式调用,可以组合多个查询方法:
var users = userTable
.Where(u => u.Age > 25)
.Where(u => u.Name.Contains("张"))
.OrderByDesc(u => u.Age)
.Limit(10)
.GetList();
查询执行
GetList
将查询结果转换为列表:
var users = userTable.Where(u => u.Age > 25).GetList();
FirstOrDefault
返回第一个元素,如果没有则返回 null:
var user = userTable.Where(u => u.Name == "张三").FirstOrDefault();
GetOne
返回单个元素,如果没有则返回 null:
var user = userTable.Where(u => u.Id == 1).GetOne();
完整示例
using System;
using JCode.ORMX.Attributes;
using JCode.ORMX.DbProvider;
using JCode.ORMX.Core;
using Microsoft.Data.Sqlite;
[Table(Name = "Users")]
public class User
{
[Column(IsPrimaryKey = true, IsAutoIncrement = true)]
public int Id { get; set; }
public string Name { get; set; }
public string Email { get; set; }
public int Age { get; set; }
}
class Program
{
static void Main()
{
using var provider = new SqliteDatabaseProvider("Data Source=:memory:");
var tableManager = provider.GetTableManager();
// 创建表
tableManager.Create<User>();
// 插入数据
var userTable = tableManager.Table<User>();
userTable.Insert(new User { Name = "张三", Email = "zhangsan@example.com", Age = 25 });
userTable.Insert(new User { Name = "李四", Email = "lisi@example.com", Age = 30 });
// 查询数据
var users = userTable.Where(u => u.Age > 25).GetList();
foreach (var user in users)
{
Console.WriteLine($</div>"{user.Name}, {user.Age}岁");
}
}
}
EasyQuery 动态查询
什么是 EasyQuery
EasyQuery 是 ORMX 框架中提供的轻量级、灵活的查询条件构建器。它允许你通过简单的字符串表达式或强类型的 Lambda 表达式来构建复杂的数据库查询条件,非常适合用于处理前端传递的动态查询参数(如 RESTful API 的查询字符串)。
核心特性:
- 字符串表达式解析 - 支持将类似
[gt,18]、[like,*John*]的字符串解析为 SQL 查询条件 - 强类型 Lambda 支持 - 支持使用
x => x.Age > 18的方式设置条件,提供编译时类型检查 - 复杂逻辑分组 - 支持通过
SetGroup方法对条件进行AND/OR的复杂组合 - 排序支持 - 内置对
ASC和DESC排序的解析
基础用法
方式一:直接实例化并设置条件
var query = new EasyQuery<User>();
query.Set(new Dictionary<string, string>
{
{ "Name", "John" },
{ "Age", "18" }
});
var users = userTable.EasyWhere(query).GetList();
方式二:通过字典初始化(推荐用于 API 开发)
// 常用于接收前端 JSON 或 QueryString
var conditions = new Dictionary<string, string>
{
{ "Age", "[GT,18]" },
{ "Status", "1" }
};
var query = new EasyQuery<User>(conditions);
var users = userTable.EasyWhere(query).GetList();
方式三:强类型 Lambda 设置(最推荐)
var query = new EasyQuery<User>();
// 使用 Equals 方法传递表达式字符串,提供编译时类型检查
query.Set(x => x.Name.Equals("[LIKE,*John*]"));
query.Set(x => x.Age.Equals("[GT,18]"));
var users = userTable.EasyWhere(query).GetList();
表达式语法
EasyQuery 支持丰富的字符串表达式语法,基本格式为:[操作符,值]。
比较操作符
| 操作符 | 简写 | 说明 | 示例 | 对应 SQL |
|---|---|---|---|---|
EQ |
E, = |
等于 | [EQ,18] 或 18 |
= 18 |
NEQ |
NE, != |
不等于 | [NEQ,18] 或 !18 |
!= 18 |
GT |
> |
大于 | [GT,18] 或 >18 |
> 18 |
GTE |
GE, >= |
大于等于 | [GTE,18] 或 >=18 |
>= 18 |
LT |
< |
小于 | [LT,18] 或 <18 |
< 18 |
LTE |
LE, <= |
小于等于 | [LTE,18] 或 <=18 |
<= 18 |
字符串操作符
| 操作符 | 说明 | 示例 | 对应 SQL |
|---|---|---|---|
LIKE |
模糊匹配(使用 * 代替 %) |
[LIKE,*John*] |
LIKE '%John%' |
NOTLIKE |
不匹配 | [NOTLIKE,*John*] |
NOT LIKE '%John%' |
CONTAINS |
包含 | [CONTAINS,John] |
LIKE '%John%' |
STARTSWITH |
以...开始 | [STARTSWITH,John] |
LIKE 'John%' |
ENDSWITH |
以...结束 | [ENDSWITH,John] |
LIKE '%John' |
集合与范围操作符
| 操作符 | 说明 | 示例 | 对应 SQL |
|---|---|---|---|
IN |
在集合中(使用 \| 分隔) |
[IN,1\|2\|3] |
IN (1, 2, 3) |
NOTIN |
不在集合中 | [NOTIN,1\|2\|3] |
NOT IN (1, 2, 3) |
BETWEEN |
在范围内 | [BETWEEN,18\|30] |
BETWEEN 18 AND 30 |
NOTBETWEEN |
不在范围内 | [NOTBETWEEN,18\|30] |
NOT BETWEEN 18 AND 30 |
分组逻辑
使用 SetGroup 方法定义分组之间的逻辑关系:
var query = new EasyQuery<User>();
// 设置分组条件
query.Set(new Dictionary<string, string>
{
// G1 组:名字包含 John 且 年龄大于 18
{ "Name", "G1[LIKE,*John*]" },
{ "Age", "G1[GT,18]" },
// G2 组:邮箱包含 @example.com
{ "Email", "G2[LIKE,*@example.com*]" }
});
// 设置分组逻辑:(G1 的条件) OR (G2 的条件)
// 即:(Name LIKE '%John%' AND Age > 18) OR (Email LIKE '%@example.com%')
query.SetGroup("G1|G2");
var users = userTable.EasyWhere(query).GetList();
组合语法说明:
|表示OR(或)&表示AND(与)- 支持使用括号
()改变优先级,例如:(G1|G2)&G3
实际应用示例
示例 1:RESTful API 中的动态查询
[HttpGet("users")]
public IActionResult GetUsers([FromQuery] Dictionary<string, string> filters)
{
var query = new EasyQuery<User>(filters);
var users = _tableManager.Table<User>()
.EasyWhere(query)
.GetList();
return Ok(users);
}
// 前端请求:GET /api/users?Age=[GT,18]&Name=[LIKE,*John*]
示例 2:复杂条件查询
var query = new EasyQuery<User>();
query.Set(x => x.Age.Equals("[GTE,18]"));
query.Set(x => x.Status.Equals("1"));
query.Set(x => x.Email.Equals("[LIKE,*@example.com*]"));
// 按年龄降序排序
query.Set(x => x.Age, "[DESC]");
var users = userTable.EasyWhere(query).GetList();
示例 3:分组查询(高级)
var query = new EasyQuery<User>();
query.Set(new Dictionary<string, string>
{
{ "Age", "G1[GT,18]" }, // G1: 年龄>18
{ "Status", "G1[EQ,1]" }, // G1: 状态=1
{ "Name", "G2[LIKE,*张*]" }, // G2: 名字包含"张"
});
// 查询逻辑:(年龄>18 AND 状态=1) OR (名字包含"张")
query.SetGroup("G1|G2");
var users = userTable.EasyWhere(query).GetList();
EasyQuery 的优势
- 动态查询 - 非常适合处理前端传递的查询参数
- 类型安全 - 通过 Lambda 表达式提供编译时类型检查
- 灵活组合 - 支持复杂的逻辑分组和条件组合
- 易于测试 - 可以独立测试查询条件
- API 友好 - 天然适合 RESTful API 的查询字符串解析
注意事项
- 字符串格式 - 使用
*代替%进行模糊匹配 - 分组命名 - 分组名称(如 G1、G2)可以自定义,但要保持一致
- 空值处理 - 空字符串会被忽略,不会生成查询条件
- 异常处理 - 如果传入了不存在的属性名或无法转换的数据类型,会抛出异常,建议在 API 层进行统一捕获和处理
总结
本章介绍了 ORMX 框架的查询构建功能,包括 Where 条件查询、OrderBy 排序、GroupBy 分组、Join 连接和分页查询。ORMX 提供了丰富的查询方法,支持链式调用,可以灵活组合各种查询条件。WhereComplex 方法允许构建复杂的逻辑条件,支持 AND、OR 和括号运算。合理使用查询构建功能,可以高效地检索和过滤数据。
扩展思考
在复杂查询场景下,如何优化查询性能?是否需要使用索引、查询计划分析等技术?对于大数据量的查询,如何避免内存溢出和性能问题?在分布式系统中,如何实现跨节点的查询和聚合?这些问题值得在深入使用 ORMX 后进一步思考。