跳到主要内容

SQL 查询与底层实例(GetListBySql / ExecuteScalar / GetDatabase)

当 DynamicFilter 无法表达查询需求(复杂联表、窗口函数、聚合、跨对象统计等)时,IChameleonDbContext 提供原生 SQL 查询能力,并可获取当前租户的 FreeSql 实例做更底层的 ORM 操作。编写 C# 脚本和微服务代码时一律使用异步版本。

概述​

  • GetListBySql / ExecuteScalar 在 IChameleonDbContext 接口上,所有场景可用
  • GetQuerySql / ExecuteDataTable 只在实现类 ChameleonDbContext 上,接口上没有;需要使用时必须注入具体类型 ChameleonDbContext
  • GetDatabase() 返回当前租户的 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