SQL 查询与底层实例(GetListBySql / ExecuteScalar / GetDatabase)
当 DynamicFilter 无法表达查询需求(复杂联表、窗口函数、聚合、跨对象统计等)时,IChameleonDbContext 提供原生 SQL 查询能力,并可获取当前租户的 FreeSql 实例做更底层的 ORM 操作。编写 C# 脚本和微服务代码时一律使用异步版本。
概述
GetListBySql/ExecuteScalar在IChameleonDbContext接口上,所有场景可用GetQuerySql/ExecuteDataTable只在实现类ChameleonDbContext上,接口上没有;需要使用时必须注入具体类型ChameleonDbContextGetDatabase()返回当前租户的IFreeSql实例;事务中返回事务绑定的连接- 原生 SQL 直接作用于当前租户库,表名为实际物理表名(对象编码小写、
.转_,如"User.Information"→user_information),主键列为_id - 写 SQL 时不要拼接用户输入,一律使用参数化查询
API 方法
SQL 查询列表
// 异步版本(推荐)
Task<List<ObjectData>> GetListBySqlAsync(string sql, Dictionary<string, object> param = null);
// 同步版本
List<ObjectData> GetListBySql(string sql, Dictionary<string, object> param = null);
返回 ObjectData 列表,键为查询结果列名(小写)。
SQL 查询单值
// 异步版本(推荐)
Task<object> ExecuteScalarAsync(string sql, Dictionary<string, object> param = null);
// 同步版本
object ExecuteScalar(string sql, Dictionary<string, object> param = null);
返回结果集第一行第一列(如 COUNT(*)、MAX(...)),需要自行转换为具体类型。
查询 DataTable / 生成 SQL(仅实现类)
// 以下方法在 ChameleonDbContext 实现类上,IChameleonDbContext 接口没有
Task<DataTable> ExecuteDataTableAsync(string sql, Dictionary<string, object> param = null);
string GetQuerySql(string metaObjectCode, DynamicFilter filter, string[] columns = null,
Dictionary<string, int> sort = null, int skip = -1, int limit = -1);
GetQuerySql 按与 GetList 相同的规则生成 SQL 文本(不执行),可用于调试或日志记录。
获取底层 FreeSql 实例
IFreeSql GetDatabase();
返回当前租户的 IFreeSql,可用 FreeSql 的完整链式能力(联表、聚合等)。注意:
- 调 用前必须保证上下文中的租户 ID 有效,否则抛
ArgumentException/InvalidOperationException - 通过它做的写操作不经过
DbContext的审计字段填充与变更审计日志,需要自行维护 - 事务中返回的是
UnitOfWork.Orm,写操作参与事务
使用示例
参数化 SQL 查询
var results = await sdk.DbContext.GetListBySqlAsync(
@"SELECT * FROM ""leaveapplication""
WHERE ""status"" = @status
ORDER BY ""createdtime"" DESC
LIMIT 10",
new Dictionary<string, object> { { "status", "Pending" } }
);
聚合统计
var total = await sdk.DbContext.ExecuteScalarAsync(
@"SELECT COALESCE(SUM(""amount""), 0) FROM ""order""
WHERE ""isdeleted"" = false AND ""status"" = @status",
new Dictionary<string, object> { { "status", "Completed" } }
);
var sum = Convert.ToDecimal(total);
联表查询(原生 SQL)
var rows = await sdk.DbContext.GetListBySqlAsync(
@"SELECT o.""orderno"", o.""amount"", u.""name"" AS ""applicantname""
FROM ""order"" o
JOIN ""user_information"" u ON o.""applicantid"" = u.""_id""
WHERE o.""isdeleted"" = false
LIMIT 100");
生成 SQL 文本(调试用)
// 需要 ChameleonDbContext 具体类型
var sql = chameleonDbContext.GetQuerySql("Order",
FilterBuilder.Eq("isdeleted", false),
sort: new Dictionary<string, int> { { "createdtime", -1 } },
limit: 20);
注意事项
- 原生 SQL 不再经过
DynamicFilter的注入校验与类型转换,必须参数化(@param占位 +param字典) - 不同数据库方言(PostgreSQL 等)的引号、函数有差异,写 SQL 时以当前租户库类型为准
GetListBySql返回的列名以 SQL 的 SELECT 别名为准,_id列会自动转换为Guid- 优先级建议:
DynamicFilter查询 → 原生 SQL →GetDatabase()链式操作
下一步
相关实现
- SQL 层实现:
chameleon-core/components/Chameleon.Database/DbContext/ChameleonDbContextSqlOperation.cs - 租户库解析:
chameleon-core/components/Chameleon.Database/DbContext/ChameleonDbContext.cs的GetDatabase/GetDatabaseDirectely