LiteOrm 支持三种主要查询方式:Lambda、Expr、ExprString。
其中 Lambda 最终也会先转换成 Expr,再统一生成 SQL。
本文档聚焦“如何选型”和“常见查询入口”;如果你要系统学习 Expr 的构造方式、静态方法、扩展方法和组合语义,请优先阅读Expr 使用指南。
| 方式 | 语法 | 更适合 | 类型安全 |
|---|---|---|---|
| Lambda | u => u.Age > 18 |
固定条件、业务语义清晰 | ✅ 强 |
Expr |
Expr.Prop("Age") > 18 |
动态拼装、查询构造器、多条件后台筛选 | ✅ 编译期 |
ExprString |
$"WHERE {expr}" |
DAO 中的条件片段或完整 SQL | ❌ 运行时 |
Expr:例如管理后台筛选、前端构造器、跨层传递过滤条件。ExprString:它既能补 Search 的条件片段,也能传完整 SQL;Service 层不提供这个入口。var users = await userService.SearchAsync(u => u.Age >= 18);
var users = await userService.SearchAsync(u => u.UserName.Contains("admin"));
var users = await userService.SearchAsync(u => new[] { 1, 2, 3 }.Contains(u.Id));
var page = await userService.SearchAsync(
q => q.Where(u => u.Age >= 18)
.OrderByDescending(u => u.CreateTime)
.Skip(0)
.Take(20)
);
var keyword = "admin";
var users = await userService.SearchAsync(u => u.UserName.Contains(keyword));
Lambda 外定义的变量会被参数化。
如果是 DateTime.Now 这类值,希望参数化时应先赋给变量。
Exists 与 ExistsRelatedExistsLambda 写法:
using static LiteOrm.Common.Expr;
var users = await userService.SearchAsync(
u => Exists<Department>(d => d.Id == u.DeptId && d.Name == "研发中心")
);
Expr 写法:
using static LiteOrm.Common.Expr;
var expr = Exists<Department>(
Prop("Id") == Prop("T0", "DeptId") & Prop("Name") == "研发中心"
);
var users = await userService.SearchAsync(expr);
适合你想自己控制关联条件的场景。
ExistsRelatedLambda 写法:
using static LiteOrm.Common.Expr;
var users = await userService.SearchAsync(
u => ExistsRelated<DepartmentView>(d => d.Name == "研发中心")
);
Expr 写法:
using static LiteOrm.Common.Expr;
var expr = ExistsRelated<DepartmentView>(Prop("Name") == "研发中心");
var users = await userService.SearchAsync(expr);
适合模型里已经声明好关联路径,只想“按关联表条件过滤主表”的场景。
匹配逻辑、继承链规则和 ConstFilter 行为请看关联查询。
Expr 查询入口using static LiteOrm.Common.Expr;
LogicExpr condition = null;
if (minAge.HasValue)
condition &= Prop("Age") >= minAge.Value;
if (!string.IsNullOrWhiteSpace(keyword))
condition &= Prop("UserName").Contains(keyword);
var users = await userService.SearchAsync(condition);
Expr 最大的价值在于“先构造,再组合,再复用”。
而 Lambda 查询也会先转成 Expr 再继续生成 SQL,所以两者并不是两套互相隔离的能力体系。
有关 Expr 的详细说明,请转到:Expr 使用指南
ExprString 插值字符串ExprString 允许你在插值字符串中直接嵌入 Expr 对象和参数,适合 DAO 层里需要手动构造 Search 条件片段或完整 SQL 的场景。Service 层没有对应的公开查询重载。
using static LiteOrm.Common.Expr;
var condition = Prop("Age") >= 18;
var users = await userViewDAO.Search(
$"WHERE {condition} ORDER BY CreateTime DESC"
).ToListAsync();
using static LiteOrm.Common.Expr;
int minAge = 18;
var result = await userViewDAO.Search(
$"WHERE {Prop("Age")} >= {minAge}"
).ToListAsync();
插值里的普通值仍会按参数处理;插值里的 Expr 对象会先转成对应 SQL 片段再拼进去。
所以推荐优先插入 Expr.Prop(...)、Expr.Value(...)、LogicExpr 这类结构化对象,而不是在字符串里手写大量列名和值。
建议:
Search 条件片段,也可以在需要时配合 isFull: true 传完整 SQL。Expr 表达的过滤条件,建议先构造 Expr 后再插入 ExprString,而不是写死在字符串里。ExprString 按照 Expr 插入的顺序进行解析,参数生成的顺序以及上下文逻辑与完整 Expr 解析存在差异,例如 ExprString 中 SelectExpr 早于 FromExpr 解析,若 SelectExpr 中未指定表别名的列可能不能正确匹配默认表(主查询已通过预先创建默认主表的上下文解决,但子查询会存在问题),使用时需注意。[ 和 ] 当作通用引用符占位,执行前 LiteOrm 会按当前数据库方言把它们替换成真实的标识符引用符。var result = await dataViewDAO.Search(
$"SELECT [Id], [UserName] FROM [Users] WHERE [Age] >= {minAge}",
isFull: true
).GetResultAsync();
ExprString 不支持自动展开 CommonTableExpr,需要直接写完整 WITH ... SELECT ... SQL 或通过 ` SelectExpr` 构造 With 块。var result = await dataViewDAO.Search(
$"""
WITH ActiveUsers AS (
SELECT Id, UserName, Age
FROM Users
WHERE Age >= {minAge}
)
SELECT Id, UserName, Age
FROM ActiveUsers
""",
isFull: true
).GetResultAsync();
using static LiteOrm.Common.Expr;
var users1 = await userService.SearchAsync(u => u.Age >= 18);
var users2 = await userService.SearchAsync(Prop("Age") >= 18);
Expr 为主,适合业务语义清晰、需要事务/AOP/服务封装的场景。ExprString 查询重载;如果需求已经变成“手写 SQL”,就应该切到 DAO。using static LiteOrm.Common.Expr;
var users1 = await userViewDAO.Search(u => u.Age >= 18).ToListAsync();
var users2 = await userViewDAO.Search(Prop("Age") >= 18).ToListAsync();
var users3 = await userViewDAO.Search($"WHERE {Prop("Age")} > {minAge}").ToListAsync();
Expr,还支持 ExprString,因此更适合自定义 SQL 片段、完整 SQL、投影查询和 DataTable 查询。SearchAs(...)、Query(...)、Execute(...)、GetValue(...) 这类更底层能力时,也应该直接使用 DAO。